Symphony extension applications are standalone web applications that are embedded within the Symphony user interface as iframes that interact with the Symphony container using the Client Extension API.
The Client Extension API is a JavaScript library that consists of services containing methods that allow developers to build apps that extend and interact with Symphony's user interface. Using these services, developers can:
Add modules, or windows, containing your app content to the Symphony client
Add entry points for your app, such as navigation items in Symphony's left sidebar or links on #hashtag and $cashtag hovercards
Add interactive buttons to chat and user profile module headers
Enable users to share content from your app into Symphony chats
Register custom renderers to richly display messages containing structured objects
Some of Symphony’s services will require you to implement your own services with methods to handle events. For example:
Handling a user click on your app’s left sidebar menu item by opening your default app module
Handling a user click on your app’s #hashtag or $cashtag hovercard link by opening an app module with a contextual search
Applications created with the Client Extension API run in iframes inside the Symphony client.
Symphony apps consists of:
The main application controller, a hidden iframe that uses the Client Extension API services to bootstrap your app, extending the Symphony user interface
In most cases, one or more application views, separate iframes that are rendered within Symphony modules
Applications can be built using any web development technology of your choice.
Services are used for communication between your app and the Symphony client. There are two types of services: remote and local.
Remote services are services that are shared:
The services of the Client Extension API are remote services whose methods can be invoked by your application controller and views
You can also implement remote services that can be shared between your application controllers and views
Local services are services are specific to either your controller or one of your views.
To learn more about the services and capabilities provided by the Extension API continue here:
Extension API ServicesTo use the Client Extension API services, you must include the symphony-api.js JavaScript file in your application controller and views.
<script type="text/javascript" src="https://cdn.symphony.com/resources/api/v1.0/symphony-api.js" charset="utf-8"></script>
To style your app, you must include the symphony-style.css CSS file in your application views and add the class "symphony-external-app" to the <body>
tag of your app views.
<link rel="stylesheet" type="text/css" href="https://cdn.symphony.com/resources/api/v1.1/symphony-style.css">
Icons must be square, with a recommended size of 32x32.
The supported formats are SVG (recommended), PNG and JPG.
Transparency is supported, but please take into account that your icon should work both in the light theme and the dark theme.
The SYMPHONY.remote.hello()
method should be used to initialize the connection to the Client Extension API from your application controller and views.
Returns a promise that will be fulfilled when the introduction is complete. If there is a problem, the promise will be rejected. The promise returns an object containing the user's Symphony client theme name, font size, and any associated classes, including those for theme name or size, as well for condensed and contrast modes.
hello: function()
Returns
Type
Description
themeV2
Object
An object containing the user's Symphony client theme settings
locale
String
The language selected by the user in his settings. Possible values are "en-US" | "fr-FR" | "ja-JP".
{
"themeV2" : {
"name": "dark",
"size": "normal",
// This will include a list of all theme and font classes available.
"classes": [],
},
"locale" : "en-US"
}
You should style your application according to the user's theme by applying the theme and font size classes to the <body>
tag of any application modules.
SYMPHONY.remote.hello().then(function(data) {
// Set the theme of the app module
var themeColor = data.themeV2.name;
var themeSize = data.themeV2.size;
// You must add the symphony-external-app class to the body element
document.body.className = "symphony-external-app " + themeColor + " " + themeSize;
});
If a user changes his theme, a themeChangeV2 event is fired from the ui service, which will pass a themeV2
object with the new values. You should use a service to listen to this event and update the classes on the application module <body>
.
You must register your application controller with Symphony and connect your application views using the SYMPHONY.application
methods. During this time, the Services that will be used by your application must be specified.
Register an application controller with the Symphony client. Additionally, subscribe the application to remote services and register local services that can be used remotely. Returns a promise that will be fulfilled when registration is complete.
This method must be called before the application can register or subscribe to any services.
register : function(id, servicesWanted, servicesSent)
Parameter
Type
Description
id
String
The id of your application. For partner apps, this is an alphanumeric string chosen by the partner. For custom enterprise apps, this is generated when creating the app in the Admin Portal.
servicesWanted
Array of Strings
A list of names of remote services that your application wants to subscribe to
servicesSent
Array of Strings
A list of names of local services your application wants to make available remotely (any implemented methods on this service will be made available remotely)
Returns
Type
Description
userReferenceId
String
A unique anonymized identifier for the user in context that will perpetuate until the user uninstalls the application
// Register the "hello" application with the Symphony client
// Subscribe our application to Symphony's services
// Register the "hello" app's controller service (this service must be registered using SYMPHONY.services.register())
SYMPHONY.application.register(
"hello",
["modules", "applications-nav", "ui", "share", "commerce"],
["hello:controller"]
).then(function(response) {
var userId = response.userReferenceId;
}));
Connect an application view to an existing application that has been registered with Symphony. Additionally, subscribe the application to remote services and register local services that can be used remotely. Returns a promise that will be fulfilled when connection is complete.
connect : function(id, servicesWanted, servicesSent)
Parameter
Type
Description
id
String
The id of your application. For partner apps, this is an alphanumeric string chosen by the partner. For custom enterprise apps, this is generated when creating the app in the Admin Portal.
servicesWanted
Array of Strings
A list of names of remote services that your application wants to subscribe to
servicesSent
Array of Strings
A list of names of local services your application wants to make available remotely (any implemented methods on this service will be made available remotely)
Returns
Type
Description
userReferenceId
String
A unique anonymized identifier for the user in context that will perpetuate until the user uninstalls the application
// Connect an application view to the "hello" application
// Subscribe our application to Symphony's services
// Register the "hello" app's view service (this service must be registered using SYMPHONY.services.register())
SYMPHONY.application.connect(
"hello",
["modules", "applications-nav", "ui", "share", "commerce"],
["hello:app"]
).then(function(response) {
var userId = response.userReferenceId;
}));
The Client Extension API uses services for communication between your application and the Symphony client. The Client Extension API provides a set of services that your application can leverage to extend the Symphony Client and to create custom workflows and experiences.
The Client Extensions API provides the following remote services:
Use the modules
service to create application-specific modules:
The Applications navigation section is found at the bottom of the left-hand sidebar of the Symphony client workspace. Use the applications-nav
service to create a navigation item for your application:
Use the ui
service to extend various parts of the Symphony client user interface. For example, add buttons on 1 to 1 chats and chatroom modules or add links to the #hashtag and $cashtag hovercards:
Use the share
service to allow users to share content from your application into Symphony conversations:
Use the entity
service to allow your app to render a Structured Object within a within a message sent by the REST API:
Apps can offer premium functionality through licensed subscriptions. Use the commerce
service to identify the products (premium versions) to which a user is subscribed:
Use the dialogs
service to create modal windows (e.g. to open a modal window from a button registered at a room level):
Both the Client Extensions API services and your application services use the same interface. Continue here to learn how to implement the Service interface methods:
Service InterfaceBoth the Client Extensions API services and your application services use the same interface. The service interface consists of the following methods:
Create a method on a service and specify the implementation.
function implement(methodName, implementation)
Parameter
Type
Description
methodName
String
The name of the method to create on your service
implementation
Function
The implementation of the method
var helloAppService = SYMPHONY.services.register("hello:app");
helloAppService.implement("helloWorld", function() {
console.log("Hello World!");
});
Alternately, create several methods on a service at once by specifying an object consisting of multiple functions:
function implement(implementations)
Parameters
Type
Description
implementations
Object
An object containing one or more functions to create on this service, where the keys are the names of the functions and the values are the implementations
var helloAppService = SYMPHONY.services.register("hello:app");
helloAppService.implement({
helloWorld: function() {
console.log("Hello World!");
}
});
Call a method on a service. Any extra parameters passed to this method will be sent as arguments to the service method:
function invoke(methodName, ...)
Parameters
Type
Description
methodName
String
The name of the method to call on the service
var helloAppService = SYMPHONY.services.register("hello:app");
helloAppService.implement("helloWorld", function() {
console.log("Hello World!");
});
helloAppService.invoke("helloWorld");
Fire an event from a service. Any extra parameters passed to this method will be passed to the callbacks defined by services that listen to this event:
function fire(eventName, ...)
Parameters
Type
Description
eventName
String
The name of the event to fire
var helloAppService = SYMPHONY.services.register("hello:app");
helloAppService.fire('myEvent');
Subscribe a service to an event that is fired by another service (or itself). This method returns a handle that can later be used to remove the listener:
function listen(eventName, callback)
Parameters
Type
Description
eventName
String
The name of the event to listen for
callback
Function
The function that will be called when the event is fired
var uiService = SYMPHONY.services.subscribe("ui");
// themeChangeV2 is an event fired by the UI service when the user changes his theme or font. The themeV2 object which contains the theme name and font size is also passed along with the event.
uiService.listen("themeChangeV2", function() {
themeColor = data.themeV2.name;
themeSize = data.themeV2.size;
console.log("The user changed his theme color to " + themeColor " and font size to " + themeSize ".");
});
Unsubscribe a service from an event:
function remove(eventName, handle)
Parameters
Type
Description
eventName
String
The name of the event to unsubscribe from
handle
String
The handle returned by the call to listen
var uiService = SYMPHONY.services.subscribe("ui");
// themeChangeV2 is an event fired by the UI service when the user changes his theme or font. The themeV2 object which contains the theme name and font size is also passed along with the event.
var handle = uiService.listen("themeChangeV2", function() {
themeColor = data.themeV2.name;
themeSize = data.themeV2.size;
// Styling is achieved by specifying the appropriate classes on the app module's body element.
document.body.className = "symphony-external-app " + themeColor + " " + themeSize;
console.log("The user changed his theme color to " + themeColor " and font size to " + themeSize ".");
});
uiService.remove("themeChangeV2", handle);
In order to leverage the services provided by the Client Extension API, you must first subscribe to them. To use your applications own services, you must register them via the Client Extension API. Extension apps can register and subscribe to local and remote services in the following ways:
Creates a new local service and register it using existing classes:
If you are developing an Object-Oriented Application, SYMPHONY.services.make()
allows you to use a class as a prototype for implementing service methods, allowing you to choose which methods of the class will be available in the service by specifying them in the implements
list.
This cannot be and this cannot be achieved by the SYMPHONY.services.register()
function, which is only recommended when creating small services since it does not require any class, and it only requires the serviceName
. If you use SYMPHONY.services.register()
for large services, you will have to call service.implement()
passing an object to it. As a result, all the methods will be public and the code might look unorganized.
SYMPHONY.services.make(name, context, methods, makeEventHandlers)
Parameter
Description
name
The name of the service being created
context
The object instance, usually this
methods
The names of the methods of this object that will be available on the created service
makeEventHandlers
If true, the methods listen and fire will be added to this instance. Calling the listen or fire objects will do so on the service.
class Navigation {
implements = ['ready', 'select'];
serviceName = 'sample-navigation';
register() {
SYMPHONY.services.make(this.serviceName, this, this.implements, true);
}
ready() {
this.nav = SYMPHONY.services.subscribe('applications-nav');
this.nav.add('sample', 'My App', this.serviceName)
}
select() {
// do something here
}
}
var service = new Navigation();
service.register()
Creates a new local service and register it to be used by a specific application view or your application controller:
register: function(serviceName)
Parameter
Type
Description
serviceName
String
The name of the service to register
// hello:controller is a service implemented by my application
var helloControllerService = SYMPHONY.services.register("hello:controller");
Finds a service - either local or remote - that has been registered and returns it. Returns false if the service does not exist.
In order to use a service, it must have been requested by your application during application.register()
or application.connect()
.
subscribe : function(serviceName)
Parameter
Type
Description
serviceName
String
The name of the service to subscribe to
// modules is a service provided by the Client Extensions API
var modulesService = SYMPHONY.services.subscribe("modules");
Takes an existing local service and makes it available remotely. Use this to make services available between multiple application views or between your application view and controller:
register : function(serviceName)
Parameter
Type
Description
serviceName
String
The name of the existing local service to make available remotely
Imports a remote service and makes it available locally. Returns a promise which resolves to a reference to a service. This service would now be available in your registry using SYMPHONY.services.subscribe:
subscribe : function(serviceName)
Parameter
Type
Description
serviceName
String
The name of the remote service that you would like to access locally.
A module is a new window inside the Symphony client workspace, such as a chatroom or an instant message. Use the modules
service to create application-specific modules.
// To use the modules service, you must subscribe to it from your application
var modulesService = SYMPHONY.services.subscribe("modules");
The following methods are available on the modules
service:
Show a new application module:
function show(id, title, serviceName, iframe, options)
Parameter
Type
Description
id
String
A unique id for this module (must be unique across all modules of a given application)
Either title
or {title, icon}
String or Object
Either the title of the module as a string
or an object with the keys title
and icon
The value of title
is a string
The value of icon
is the url of a square SVG image (recommended), or the url of a square png/jpg image. Recommended size is 32x32.
serviceName
String
The name of a local service implemented by your application that will be invoked when a user action is performed relating to this module
iframe
String
The URL for the content of the module (must be an HTTPS URL)
options
Object
An object, which can contain:
canFloat
: if set to true, a menu item will be added to the More menu (found under the (…) on the module frame) that, when clicked, will pop the module out into its own browser window
parentModuleId
: if set to the ID of a module opened by an application, the specified module will not be closed when this module is shown
modulesService.show(
"hello",
{title: "Hello World App"},
"hello:controller",
"https://localhost:4000/app.html",
{
"canFloat": true
}
);
Hide an existing application module:
function hide(id)
Parameter
Type
Description
id
String
The id of the module that should be hidden.
modulesService.hide("hello");
Change the title of an existing application module:
function setTitle(id, title)
Parameters
Type
Description
id
String
The id of the module for which the title should be changed
Either title
or {title, icon}
String or Object
Either the title of the module as a string
or an object with the keys title
and icon
The value of title
is a string
The value of icon
is the url of a square SVG image (recommended), or the url of a square png/jpg image. Recommended size is 32x32.
modulesService.setTitle("hello", "New Module Title");
Focus an existing application module:
function focus(id)
Parameter
Type
Description
id
String
The id of the module to focus
modulesService.focus("hello");
Opens a link from your application in a new tab in the user's default browser. This method should be used to open links, rather than <a href="..." target="_blank">...</a>
.
function openLink(url)
Parameter
Type
Description
url
String
The URL to be opened
// This code will live in your application view.
// Assume there is a button element with id "link" on the application module
// If that button is clicked, open a Google link.
var linkButton = document.getElementById("link");
linkButton.addEventListener("click", function(){
modulesService.openLink("https://www.google.com");
});
Reloads the content of the module at the new URL.
The Client Extensions API is designed for single-page applications. Use this method with multi-page applications to load new content when users navigate to another page:
function redirect(id, url)
Parameter
Type
Description
id
String
The unique identifier for the module. A module with this id must already exist.
url
String
The URL of the new iframe to load in the module.
onSelect : function(symbol) {
this.modulesService.redirect(this.moduleId, MODULE.baseUrl + 'details?symbol=' + encodeURIComponent(symbol));
},
Use the entity
service to allow your app to render a Structured Object created by the REST API within a message:
var entityService = SYMPHONY.services.subscribe("entity");
The following methods are available on the entity
service:
Register a renderer for a type
of entity:
function registerRenderer(type, options, serviceName)
Parameter
Type
Description
type
String
The type of entity that will be rendered by your application. Entities are namespaced using reverse domain name notation, e.g. com.symphony.address
.
options
Array
Reserved for future use.
serviceName
String
The name of the application service that will be used to render this entity.
You must implement the render()
method on the specified application service. This method will be invoked when the associated entity is rendered in the Symphony client.
Renders an entity given its type and data values:
render: function(type, data) {}
Parameter
Type
Description
type
String
The type of entity to be rendered.
data
Object
The data for the specific entity being rendered. This data is specified when the message is created.
The render method returns an object with the following fields:
Parameter
Type
Description
template
String
An ExtensionML string that specifies the object's presentation.
In addition to ExtensionML tags, iframe tags are also supported.
data
Object
An object containing the data referenced by the template. Described in entity advanced templating.
entityInstanceId
String
A unique identifier used to reference a specific entity.
Effectively re-renders an entity with new template and data objects given its entityInstanceId:
update: function(entityInstanceId, template, data) {}
Parameter
Type
Description
entityInstanceId
String
The instance id of the entity to be updated
data
Object
The data for the entity being updated
// The application service that will be used to handle entity renderering
const helloControllerService = SYMPHONY.services.register("hello:controller");
const entityService = SYMPHONY.services.subscribe("entity");
entityService.registerRenderer(
"com.symphony.address",
{},
"hello:controller"
);
// Implement the trigger method on your application service
helloControllerService.implement({
render: (type, data) => {
const template = "<entity><span>Street Address: <text id='address'/></span><br/><span>City: Palo Alto</span><br/><span>State: California</span><br/><span>Zip Code: 94304</span></entity>"
entityInstanceId = "0";
if (type == "com.symphony.address") {
const newTemplate = "<entity><span>The message is updated</span></entity>";
// Update the entity after 5 seconds with a new template
setTimeout(() => {
entityService.update(entityInstanceId, newTemplate, {});
}, 5000);
return {
template,
data,
entityInstanceId
};
}
}
});
The 'pause' and 'resume' methods are optional. If you choose to use the methods, implement them on the renderer service. These methods will be invoked when the associated entity is rendered in the Symphony client.
Entities are checked periodically (every two seconds) if they are visible on the screen. If an entity is completely visible, a resume event is triggered to the renderer service of this entity. If an entity is partially visible or completely hidden, a pause event is triggered instead.
Example: • Pause event: responsible for stopping/pausing a video transmission (video iframe). • Resume event: responsible for playing the video.
In addition to ExtensionML tags, iframe
tags are also supported in the template
parameter:
helloControllerService.implement({
render: function(type, data) {
if (type == "com.symphony.address") {
return {
template: '<entity><iframe src="http://your-site.com/iframe-url.html" /></entity>',
data: {}
};
}
}
});
The template
parameter in the render()
function of the entity Service is an ExtensionML string used to render entities. To render a static object or an iFrame, you can use advanced templating.
For a full ExtensionML reference, continue here:
Message Format - ExtensionMLYou can create a reusable template
that leverages the values in the data
object for the specific object. When using the data
for these attributes, the template uses the id
attribute on the tag to specify which member of the data object holds these attributes.
For example, the template and data below could be used to display a link within a Symphony message:
<!-- A <text> primitive must be used to inject
text objects from the JSON data into the template -->
<messageml>
<a id="url"><text id="text"></a>
</messageml>
{
url: "https://www.symphony.com",
text: "Symphony website"
}
Use the following flow control tags to support entities that have conditional logic or recursive data:
Tag
Description
<if>
Use this tag to conditionally use the enclosing template. This tag must include an id
attribute. If there is data at the key
specified by the id
, then the enclosing template will be used, otherwise it will be skipped.
<if:last>
Use this tag within an iteration to conditionally use the enclosing template. If the current iteration is the last item in the iterated list, the enclosing template will be used.
<if:not-last>
Use this tag within an iteration to conditionally use the enclosing template. If the current iteration is not last item in the iterated list, the enclosing template will be used. This is useful to add commas between items in a list, without adding one to the last item.
<iterate>
Use this tag to loop through the items in an array. The template between the opening and closing <iterate>
tag will be used for each item in the array. The data for the template will reference the data in current the list item. This tag must include an id
attribute. This must be the key
to an array in the data
object.
Use the following tags to have interactivity on messages by implementing methods that are called to execute business logic:
Tag
Description
<action>
Use this tag to enable clicks on entities. For more information on how to use the action
tag, refer to Using Actions.
The following steps show examples on how to use actions.
Add the <action>
tag to an entity template.
<entity id="survey-voting-template" class="template">
<card id="card">
<h3>How did you like today's <text id="type"/> from <text id="venue"/>?</h3>
<p>
Please provide feedback by clicking one of the stars below to rate the meal.
</p>
<p>
<action id="onestar"/>
<action id="twostars"/>
<action id="threestars"/>
<action id="fourstars"/>
<action id="fivestars"/>
</p>
<p>
Voting ends at <text id="end"/>
</p>
</card>
</entity>
The data field of the entity must have objects that match the actions ids. For these objects, we can provide an icon, a label and a service name for the action.
onestar: {
icon: 'icon_url',
label: '',
service: serviceName,
data: {}
}
Implement an action method for the service of the entity renderer. This method will be called once the <action>
tag is clicked.
action: function(data){
console.log(data);
}
ExtensionML is a special form of markup that a front end app can use to perform custom rendering of an entity, instead of relying on Symphony to perform the default presentation.
ExtensionML is generated from a given entity and emitted by a built-in or third-party renderer. It is similar to PresentationML but is used for interactive presentation.
While similar to PresentationML, ExtensionML consists of a template and corresponding data. Each tag in the template can have an ID that references a value stored in the data, binding the data to whatever is being rendered. For example, multiple paragraphs in the template can reference a sentence stored in the data by ID, allowing for reuse of that sentence in multiple places within the template being rendered.
Note: ExtensionML does not support Symphony Elements. For more information, refer to Symphony Elements.
A number of standard HTML tags are supported within templates: b
, u
, i
, strong
, br
, ul
, ol
, li
, span
, div
, table
, th
, tr
, and td
.
These behave like their HTML counterparts but must be properly-formatted XML. So, for example, rather than <br>
you must use <br/>
.
The following tags present text in different ways. Most of these require data to specify their content, but some can also use the content between the opening and closing tags.
Tag
Description
Attributes
<formatted>
Adds text formatted with HTML markup. This must be properly formatted XML See here for information on converting HTML to XHTML.
• id
(Required): The key of a string within the template data.
<text>
Specifies regular text to be inserted into the entity. This text is inserted as is, without processing any HTML markup.
• id
(Optional): The key of a string within the template data. If no id
is specified, it uses the contents between the opening and closing tags.
<label>
Inserts a label. Labels are like text tags but are styled differently.
• id
(Optional): The key of a string within the template data. If no id
is specified, it uses the contents between the opening and closing tags.
<color-text>
Inserts a colored text. Supported colors: red, purple, green, darkGreen, blue, darkBlue, orange, grey, and yellow.
id
(Optional): The key of an object with two members:
• text specifies the text to be used
• color is one of the listed colors.
<pill>
Inserts text with a colored background and rounded corners. Supported colors: red, purple, green, darkGreen, blue, darkBlue, orange, grey, and yellow.
id
(Optional): The key of an object with two members:
• text specifies the text to be used
• color is one of the listed colors.
The following HTML tags are handled in a slightly modified way:
Tag
Description
Attributes
<a>
Inserts a link.
• id
(Optional): The tag must include either an id
attribute or an href
attribute. If id
is specified, it must be a key to a string specifying the URL of the link. If no id
attribute is specified, the href
attribute is used.
<hr>
Inserts a horizontal line.
<icon>
Displays a 16x16 pixel icon.
• id
(Required): The key to a text string specifying the URL to the image to display.
<small-icon>
Displays a 13x13 pixel icon.
• id
(Required): The key to a text string specifying the URL to the image to display.
<img>
Displays a 128x128 pixel image.
• id
(Optional): The tag must include either an id
attribute or an src
attribute. If id
is specified, it must be a key to a string specifying the URL of the link. If no id
attribute is specified, the src
attribute is used.
<iframe>
Inserts an iframe.
• src
(Required): The URL to the iframe.
• height
(Optional): If not specified, the default height of the iframe will be 50px. The maximum height is 1000px.
• width
(Optional): If not specified, the default width of the iframe will be 100%. The maximum width is 300px.
Tag
Description
Attributes
<mention>
Insert a mention.
• id
(required): The key to an object with the following members:
- id
: The unique ID of the user being mentioned.
- name
: The pretty name that is displayed for the mentioned user.
<hashtag>
Inserts a hashtag.
• id
(required): The key to a string specifying the hashtag. The string must be prefixed with '#'.
<cashtag>
Inserts a cashtag.
• id
(required): The key to a string specifying the cashtag. The string must be prefixed with '$'.
The following flow control tags are used for entities that have conditional logic or data that can be iterated upon:
Tag
Description
Attributes
<if>
Conditionally uses the enclosing template.
• id
(required): The key to the data. If there is data at the specified key, the enclosing template is used; otherwise it is skipped.
<if:not-last>
Conditionally uses the enclosing template within an iteration. If the current iteration is not the last item in the iterated list, the enclosing template is used. This is convenient when you want to add commas between list items without adding one after the last item.
<if:last>
Conditionally uses the enclosing template within an iteration. If the current iteration is the last item in the iterated list, the enclosing template will be used.
<iterate>
Loops through the items in an array. The template between the opening and closing iterate
tag is used for each array item. The data for the template references the data in the current list item.
• id
(Required): The key to an array in the data object.
The following example shows the XML template for an entity with flow control logic and corresponding data:
<entity>
<label>Guild: </label><text id="guildName"/>
<if id="webpageLink">
<label>Web page: </label>
<a id="webpageLink"><text id="webpageName"/></a>
</if>
<if id="people">
<iterate id="people">
<label>Name: </label><text id="name">
<label>Notes:</label><br/>
<formatted id="notes"/>
<if:not-last><hr/></if:not-last>
</iterate>
</if>
</entity>
var data = {
"content" = {
"guildName": "Loyal Order of Water Buffalo",
"webpageLink": "https://www.waterbuffalo.net/bedrock",
"webpageName": "The Loyal Order of Water Buffalo, Bedrock Chapter",
"people": [{
"name": "Fred Flintstone",
"notes": "Yabba Dabba Dooooo!"
},
{
"name": "Barney Rubble",
"notes": "Fred's sidekick"
},
{
"name": "Dino",
"notes": "He's kind of like a dog, but also a small sauropod. Yaps a lot.<br/> Really odd, he spoke in his first appearance."
}]
}
}
The following function can be used to turn HTML into properly formatted XML:
function xmlize(html) {
return new XMLSerializer().serializeToString($('<span>').html(html)[0])
}
The Applications navigation section is found at the bottom of the left-hand sidebar of the Symphony client workspace. Use the applications-nav
service to create a navigation item for your application:
// To use the applications-nav service, you must subscribe to it from your application
var navService = SYMPHONY.services.subscribe("applications-nav");
The following methods are available on the applications-nav
service:
add
remove
count
rename
focus
Add a new navigation item to the Applications section of the left-hand sidebar:
function add(id, title, serviceName)
Parameter
Type
Description
id
String
A unique id for this navigation item (must be unique across all navigation items of a given application)
Either title
or {title, icon}
String or Object
Either the title of the left navigation item as a string or an object with the keys title
and icon
where the value of title
is a string and the value of icon
is the url of a square SVG (recommended), or the url of a square png/jpg image. Recommended size is 32x32.
serviceName
String
The name of a local service implemented by your application that will be invoked when a user action is performed relating to the application navigation
// The application service that will be used to handle left navigation item clicks
var helloControllerService = SYMPHONY.services.register("hello:controller");
navService.add("hello-nav", "Hello World App", "hello:controller");
// Implement the select method on your application service
helloControllerService.implement({
select: function(id) {
if (id == "hello-nav") {
console.log("hello-nav was selected.");
}
}
});
Remove an existing application navigation item:
function remove(id)
Parameter
Type
Description
id
String
The id of the navigation item that should be removed
navService.remove('hello-nav');
Set the badge (notification) count on an application navigation item:
function count(id, count)
Parameter
Type
Description
id
String
The id of the navigation item that should have its count updated
count
Integer
The new badge count number. Specifying 0 will hide the badge count.
navService.count("hello-nav", count);
Change the title of an existing application navigation item.
Note that this only changes the title of a specific navigation item -- not to all navigation items created by the application:
function rename(id, title)
Parameter
Type
Description
id
String
The id of the navigation item that should be renamed
Either title
or {title, icon}
String or Object
Either the title of the left navigation item as a string or an object with the keys title
and icon
where the value of title
is a string and the value of icon
is the url of a square SVG (recommended), or the url of a square png/jpg image. Recommended size is 32x32.
navService.rename('hello-nav', 'New Left Nav Title');
Focus an existing application navigation item:
function focus(id)
Parameter
Type
Description
id
String
The id of the application navigation item to focus
navService.focus("hello-nav");
Use the share
service to allow users to share content from your application into Symphony conversation:
// To use the share service, you must subscribe to it from your application
var shareService = SYMPHONY.services.subscribe("share");
When the share function is invoked, a modal dialog is launched and populated with the shared object content (for example, the article options). The end user can then select conversations (IMs or chatrooms) into which to share the article.
Once the article is shared, it appears in the conversation view in a card format. The article will be linked either to a webpage (if the href
option is provided) or deep linked into the app (if the id
option is provided).
In order to view article contents in an application (for example, if the article id
is provided), the user must have the application installed.
If the recipient of a shared article does not have the application installed, the user will be prompted to install the application (provided that the user's enterprise has that application enabled).
If the recipient of a shared article does not have the application installed, and the application is not enabled for the user's enterprise, the user can view the content via the link (if href
is provided). If a link is not provided, the user will be notified that the article cannot be viewed because the application is disabled for the enterprise.
The following methods are available on the share
service:
Launches the "Share on Symphony" modal from your application, allowing the user to share content from your application into a Symphony conversation (IM or chatroom):
function share(type, content, options)
Parameter
Type
Required
Description
type
String
Yes
The type of content that is being shared.
content
Object
Yes
An object that describes the content being shared. For a list of objects see standard entities.
options
Object
No
An object that describes options that can be used to enhance the share service
The following JavaScript shows an example of an article being shared:
// This code will live in your application view.
// Assume there is a button element with id "share" on the application module
// If that button is clicked, launch the Share modal.
var shareButton = document.getElementById("share");
var articleContent = {
title: "Symphony Launches Mobile App",
subTitle: "Application is mobile device management (MDM) compatible.",
blurb: "Symphony Communication Services, a Palo Alto, Calif.-based messaging startup, announced its enterprise-ready mobile app for Apple iPhone is now available for download.",
date : new Date("07 June 2016").getTime() / 1000,
publisher: "Waters Technology",
author: "Dan DeFrancesco",
id: "symphony-article",
thumbnail: 'https://symphony.com/example/image.png',
href: 'https://symphony.com'
};
var shareOptions = {
prepopulateUsers: ['71811853190920', '71811853190903']
};
// Launch Symphony's share modal when the Share button is clicked
shareButton.addEventListener("click", function(){
shareService.share(
"article",
articleContent,
shareOptions
);
});
The following table shows the article content:
Field
Required
Format
Description
title
Yes
String
The headline of the article
subTitle
No
String
The subtitle of the article
blurb
No
String
A summary of the article to display
publisher
No
String
Name of the publisher
author
No
String
Name of the author
thumbnail
No
URL (could be a data url)
Image to be displayed - 106px-106px
id
Must provide either id
or href
, or both
String
An identifier used by the application to deeplink to the article
href
Must provide either id
or href
, or both
URL
URL to the article (opened in a new browser window)
The following table shows the share options:
Field
Required
Format
Description
prepopulateUsers
No
Array of strings
The users (UserIds) who will be listed initially as recipients in the share modal.
Available only for authenticated apps, and only for Client 2.0.
It is recommended to limit the number of pre-populated users so the Symphony end user can easily review the list of recipients before sharing.
The share
function can also be used to share custom, third-party entity types. In this case, the data
parameter must be populated with the following fields:
Field
Description
inputAutofill
Use this to fill the comment field in the share dialog to provide initial text. Cash tags and hash tags that are specified in the text will be converted to the correct entity.
plaintext
The markdown representation of the entity, supporting a limited set of markdown features. The value of this field will be displayed on mobile devices and other older clients.
presentationML
The default presentation of the entity using presentationML. This will be seen by everybody who does not have an app with a custom renderer for the given type.
entityJSON
The object being shared.
format
The format of the message being sent. This must be set to "com.symphony.messageml.v2".
The following JavaScript shows an example of a custom third party entity being shared:
share : function(gameNbr, time)
{
var fullTime = time;
var hours = Math.floor(time / 60 / 60 / 1000);
time -= hours * 60 * 60 * 1000;
var minutes = Math.floor(time / 60 / 1000);
time -= minutes * 60 * 1000;
var seconds = Math.floor(time / 1000);
var duration = hours.toString() + ':' + minutes.toString().pad(2, '0', 'left') + ':' + seconds.toString().pad(2, '0', 'left');
var title = 'Somebody you know won at Mah Jongg Solitaire';
var blurb = 'try to beat their time of ' + duration;
var date = new Date().getTime() / 1000;
var thumbnail = this.thumb;
var id = JSON.stringify({gameNbr: gameNbr, time: fullTime});
var presentationML =`
<entity>
<table><tr>
<td><img src="${thumbnail}" /></td>
<td>
<h1>${title}</h1>
${blurb}
</td>
</tr></table>
</entity>`;
var entityJSON = {
date: date,
thumbnail: thumbnail,
results: id,
time : time,
gameNbr : gameNbr,
};
var data = {
plaintext: `*${title}*\n${blurb}\n`,
presentationML : presentationML,
entityJSON: entityJSON,
entity: {},
format: 'com.symphony.messageml.v2',
inputAutofill : 'I ROCK!',
}
this.shareService.share('com.symfuny.invite.won', data);
}
In this example, the following modal dialog is launched and populated with the shared object content:
You must specify your own application service for handling clicks on shared articles using handleLink
if you use the id
field for deep linking articles into your application.
You must implement the link
method on your application service in order to handle clicks on shared articles in conversations.
// This code will live in your application controller.
// The application service that will be used to handle clicks on shared articles
var helloControllerService = SYMPHONY.services.register("hello:controller");
// Assume you have registered your application with the Symphony client and subscribed to the Share service.
shareService.handleLink("article", "hello:controller");
helloControllerService.implement({
// You only need to implement this function if you intend to deeplink articles into your app by specifying an id for the article. If you use href, then article links will open in a new browser window.
link: function(type, articleId) {
if(type == "article") {
// Implement this
// For example, you might launch a new application module with a url that includes the articleId in the query parameters
console.log("Article with id: " + articleId + " was clicked.");
}
}
});
Apps can offer premium functionality through licensed subscriptions. Use the commerce
service to identify the products (premium versions) to which a user is subscribed:
// To use the commerce service, you must subscribe to it from your application
var commerceService = SYMPHONY.services.subscribe("commerce");
Returns the list of products to which the user is subscribed for your app:
function getProducts(serviceName)
Parameters
Type
Description
serviceName (optional)
String
The name of a local application-implemented service. If passed, the productUpdate
event will be fired on that service if the user's product subscriptions change.
This method returns a promise that will be fulfilled with the array of products the user is subscribed to for your app. For each product, the following is returned:
{
name: "<name>",
type: "premium",
sku: "<sku>",
subscribed: true/false
}
"Premium" is the term used for paid subscription products, while "Standard" represents a freemium app. The name and SKU will be values specified by the application developer when implementing a premium version of their app.
commerceService.getProducts('hello:controller').then(function(products) {
console.log(products);
});
This event is fired when the user's product subscriptions are changed.
Use the listen method on the service specified in getProducts()
to subscribe to this event and specify a callback that will be executed when the event is fired. The callback should change the contents/features of your application to match the user's updated product subscriptions.
Use the dialogs
service to create modal windows (e.g. to open a modal window from a button registered at a room level).
The following methods are available on the dialogs
service:
show
rerender
hide
The following picture is an example of what you will be able to create with this service - this module will overlay onto the entire Symphony client window:
Presents a modal dialog to the user:
function show(id, serviceName, template, data, options)
Parameter
Type
Description
id
String
A unique id for the dialog.
serviceName
String
The name of a local application-implemented service implemented.
template
String
The extensionML for the dialog content.
data
String
The data for the extensionML.
options
Object
The data for the extensionML
const dialogsService = SYMPHONY.services.subscribe("dialogs");
dialogsService.show(
"my-dialog",
"hello:controller",
`<dialog>
<div class="container">
<div class="header">
<h1>Configuration</h1>
<br/>
<div class="headerError">
<text id="title"/>
</div>
<p class="value">Please check if the public and private
key files match and if they are correctly configured in
Jira's application link section.</p>
</div>
</div>
</dialog>`,
"undefined",
{
title: "Application Configuration"
}
);
Changes the contents of the dialog. This is usually invoked when the user has performed some action:
function rerender(id, template, data)
Parameter
Type
Description
id
String
The id of the dialog that should be updated.
template
String
The new extensionML content to display.
data
String
The data for the extensionML.
function close(id)
Parameters
Type
Description
id
String
The id of the dialog to close.
Use the ui
service to extend various parts of the Symphony client user interface. For example, add buttons on IM (1-1 chat) and chatroom modules or add links to the #hashtag and $cashtag hovercards:
// To use the ui service, you must subscribe to it from your application
var uiService = SYMPHONY.services.subscribe("ui");
Extension apps can receive stream participant information when an end user clicks on a button added by the app. For more information, refer to Receiving Conversation and User Data.
The following methods are available on the ui
service:
openIMbyStreamID
openIMbyUserIDs
registerExtension
unregisterExtension
The following events are fired by the ui
service:
themeChangeV2
Open an existing conversation in a new module.
Released in version 20.10.
function openIMbyStreamID(streamID, messageId)
Parameter
Type
Possible Values
Description
streamID
String
The stream ID or conversation ID to be opened.
messageID
String
Either a messageID, or the null value
The messageId can be used in addition to the streamId to focus on a specific message of the conversation. Use "null" as parameter value to jump to the latest message of the conversation.
Open a conversation with one or more users in a new module.
Released in version 20.10.
function openIMbyUserIDs(userIds)
Parameter
Type
Possible Values
Description
userIds
String[]
Array of userIds.
Add an action button to the Symphony user interface.
Action buttons are added to various places in the UI, such as in the header of chat modules, in #hashtag or $cashtag hovercards, on the profile of users and more.
function registerExtension(uiClass, id, serviceName, options)
Parameter
Type
Description
uiClass
String
The location within the Symphony application where the action button should be placed. Possible values:
- single-user-im
: Button added to the header of 1-1 chats
- multi-user-im
: Button added to the header of group chats
- room
: Button added to the header of chatrooms
- hashtag
: Link added to the hovercard that appears when hovering over a hashtag (e.g. #symphony)
- cashtag
: Link added to the hovercard that appears when hovering over a cashtag (e.g. $GOOG)
- settings
: Link added to detail card of the application in the Marketplace
- profile
: Link added to the user profile page and profile hovercard
id
String
A unique identifier for this extension (can be used to unregister).
serviceName
String
The name of a local service implemented by your application that will be invoked when a user clicks on your action button.
options
Object
An object, containing:
icon: the url of an image that will be displayed on the action button. Recommended format is a square SVG.
label: a label associated with the action button.
data: an opaque block of data that will be passed along with the trigger event
You must implement the trigger
method on your application service in order to handle clicks on the registered action buttons:
// The application service that will be used to handle clicks on UI extensions
var helloControllerService = SYMPHONY.services.register("hello:controller");
// The application service that will handle the filter on UI extensions
var helloFilterService = SYMPHONY.services.register("hello:filter");
// Displays a button in the header of 1-1 chats
uiService.registerExtension(
"single-user-im",
"hello-im",
"hello:controller",
{
label: "IM Button",
data: {"datetime": Date()}
}
);
// Implement the trigger method on your application service
helloControllerService.implement({
trigger: function(uiClass, id, payload, data) {
if (uiClass == "single-user-im") {
console.log('IM button was clicked on ' + data.datetime + '.');
}
Remove a previously registered action button.
This will remove all instances of a particular extension - for example, from all single chat modules or all #hashtag and $cashtag hovercards.
function unregisterExtension(uiClass, id)
Parameter
Type
Possible Values
Description
uiClass
String
single-user-im
multi-user-im
room
hashtag
cashtag
settings
The location within the Symphony application where the action button was registered.
id
String
The id of the UI extension that should be removed.
uiService.unregisterExtension('single-user-im', 'hello-im');
This event is fired when the user's font size or theme is changed.
Use the listen method on this service to subscribe to this event and specify a callback that will be executed when the event is fired. The callback should change the styling of your application to match the user's new theme.
var uiService = SYMPHONY.services.subscribe("ui");
uiService.listen("themeChangeV2", function() {
themeColor = data.themeV2.name;
themeSize = data.themeV2.size;
// Styling is achieved by specifying the appropriate classes on the app module's body element.
document.body.className = "symphony-external-app " + themeColor + " " + themeSize;
});
Symphony’s Extension API allows apps to extend the Symphony client user interface by adding buttons on the IM, MIM, profile, and chatroom modules. This capability can be used to build apps that act upon the room and user identity details, such as a click-to-call integration.
Your app must be an authenticated Extension App.
Extension apps can receive stream participant information when an end user clicks on a button added by the app.
Your button will receive information from the user object in a MIM, IM and a room of up to 20 users.
Please note that the current user’s information isn’t returned, only the information of the other user(s) in the profile of a user, an IM, MIM or room.
This is the information that you will receive if your button is pressed inside of an IM or on a users Profile:
{
threadId, //id of the conversation. Also known as streamId or conversationId
userCount, //number of users returned
isCrossPod, //if cross pod, returns true
user : { //user information
id, //user identifier
emailAddress, //user email
username, //user name
displayName, //user display name
firstName, //user first name
lastName, //user last name
phone, //user phone number
mobile, //user mobile phone number
}
}
You must implement the trigger()
method on your application service in order to handle clicks on the registered extensions. This is a sample trigger method that will allow you to receive a user's phone number and email address as an authenticated extension app:
// The application service that willbe used to handle clicks on UI extensions
var helloControllerService = SYMPHONY.services.register("hello:controller");
// Displays a button on 1-1 instant messages
uiService.registerExtension(
"single-user-im",
"hello-im",
"hello:controller",
{
label: "IM Button",
data: {"datetime": Date()}
}
);
// Implement the trigger method on your application service
helloControllerService.implement({
trigger: function(uiClass, id, payload, data) {
if (uiClass == "single-user-im") {
console.log('IM button was clicked on ' + data.datetime + '.');
// This acquires the user's phone number from the payload
var userPhone = payload.user.phone;
// You can do this for any field in the prior section, such as emailAddress
var userEmail = payload.user.emailAddress;
// You can now pass the user's phone number and email to your system.
}
}
});
This is the information that you will receive if your button is pressed inside of a MIM or a room:
{
isCopyDisabled, //if copy is disabled in the room returns true
isWriteAllowed, //if the user can send a message in the room, returns true
isCrossPod, //if it is a cross pod room, returns true
roomName, //room name
threadId, //id of the conversation. Also known as streamId or conversationId
userCount, //number of users returned
users : [ { //users information
id, //user id
isCrossPodUser, //if this is a cross pod user, returns true
isOwner, //if the user is owner of the room, returns true
emailAddress, //user email
username, //user name
displayName, //user display name
firstName, //user first name
lastName, //user last name
phone, //user phone number
mobile, //user mobile phone number
}]
}
{
isCopyDisabled,
isWriteAllowed,
isCrossPod,
roomName,
threadId,
userCount
}
You can add a new method to the service that is handling button clicks, called filter()
, on any UI extension that you are implementing. Before a button is shown, that method is passed the uiClass, the id, and the payload. If filter()
returns false
, the button is not shown. If the method is unimplemented or it returns any value other than false, the button is shown.
The filter function returns the same data returned by the ui Service here. All data except for the the user's phone number is returned in cases where you are using an authenticated app. The user phone number is only returned for 1x1 IMs and User Profiles.
Based on the information returned, you can choose to selectively display the button. For example, you can display the button only if a user's phone number is present, or if a user is not a cross-pod user.
// Implement the filter function on your application service
helloFilterService.implement(
filter: function (type, id, data) {
return !!(data.user && data.user.phone);
}
}
});