Desktop integration
Triggering the right tab in Salesforce console from the desktop
Salesforce is providing lots of integration capabilities. Most of them are based on APIs that you can consume from the browser (ex : Chatter API) or from the server (SOAP or REST Api).
In some circumstances there could be a need to do browser side integration.
Salesforce is already providing Canvas Apps, a way to integrate 2 applications in the browser (Salesforce with a third party). But what if you need to integrate on a desktop a non-browser application with Salesforce ?
Let's imagine you have a dedicated CTI client deployed on your desktop. It is not running in the browser. Such tools can integrate with the browser by calling a Salesforce URL, pushing the context as parameters. On an incoming call, this
will trigger a new tab in the browser. Each time you have a call, you will add a tab. Great to avoid erasing a pending work, but if you are running Salesforce console you will have multiple times the console. Or if you target a
Visualforce URL to manage the record popup based on the context, how can you convert the new browser tab into the right console tab ? And how to avoid opening lots of browser tabs ?
The browser technology prevent tabs from closing themself if it is not initated by a user. Try to access a custom URL that host a simple script doing window.close() , this will not close the tab.
The solution is to open the URL on the incoming call, always in the same targeted tab. How can you achieve this ? Imagine you are running Internet Explorer. Your CTI client on the desktop could run the following lines of code
(illustrated in Javascript, but can be easily translated into C#)
var url = 'https://yourdomain.my.force.com/apex/yourVisualforcePage?param1=value1&phone=1234567890'; var shellApplication = new ActiveXObject('Shell.Application'); var windows = new Enumerator(shellApplication.Windows()); var w = null; while(!windows.atEnd()){w=windows.item();windows.moveNext()};// Retrieve the last window if(w)w.Navigate(url, 0, 'CTI');
The url has to be dynamically created to push the context as URL parameters. The last line of code will target the browser tab named 'CTI'.
But we still have a remaining technical tab visible in the browser. Not very user friendly.
We can move it into the console, inside a named iframe. Instead of opening the tab, the page will be loaded into the iframe. We can host this iframe in a console component (prerequisite : your application is running as a console app).
The component will always be up and running, even if it is not manually open, the page is loaded.
Now, how to manage the screen pop ? Using the Api console. It can run perfectly from the component, but not from the iframe inside the component. We need to notify the component to start the popup with the context, each time the iframe
is loaded.
You cannot use window.postMessage() (an html5 features allowing to post data to another window) as the iframe window has no reference to another window (and what if multiple Salesforce instances are running, how can you broadcast your
context to all of them).
Your iframe and your console component are 2 pages running on the same domain. You can transmit data using storage event (localStorage), this will broadcast the context to all Salesforce open tabs on that domain. The console component
will be notified from any change on the storage, you need to ensure that the value is changed for the expected key, and let's start the popup !
Code for the Visualforce page that will be loaded into the iframe named 'CTI' :
<apex:page showHeader="false" standardStylesheets="false" docType="html-5.0" > <script type="text/javascript"> localStorage.setItem('JLA', window.location.search); </script> </apex:page>
This page is just writing the context into the localStorage. This will trigger the storage event on all other page
Code for the console component that will host the iframe page and run the popup
<apex:page> <apex:includeScript value="/support/console/34.0/integration.js"/> <script type="text/javascript" > var handle_storage = function (storageEvent) { /* StorageEvent { key; // name of the property set, changed etc. oldValue; // old value of property before change newValue; // new value of property after change url; // url of page that made the change storageArea; // localStorage or sessionStorage, depending on where the change happened. } */ if(storageEvent.key=='JLA' && storageEvent.newValue!=''){ //prevent triggering other events. IE9 triggers the event with empty value on the same window after removing the item var str = storageEvent.newValue; var objURL = {}; str.replace(new RegExp( "([^?=&]+)(=([^&]*))?", "g" ), function( $0, $1, $2, $3 ){objURL[ $1 ] = decodeURIComponent($3);} ); //If your context is in the 'phone' parameter, use fromobjURL["phone"] in a SOQL query to retrieve the targeted recordID //... imagine the variable 'targetURL' is built based on your soql result (removed from this code to make it shorter) var targetURL='/001g000000ia2Pc'; sforce.console.openPrimaryTab(undefined, targetURL, true, 'the Title'); localStorage.removeItem('JLA');//allow re-issuing same url for testing purpose } }; window.addEventListener("storage", handle_storage, false); </script><iframe name="CTI" width="5" height="5" frameborder="0" ></iframe> </apex:page>
Now you have a very simple, lightweight, browser technology in Salesforce that can react to desktop initiated events.
You can leverage it for simple browser to browser integration too, for instance if you just want to popup a console tab from an external web application. A custom weblink will access that visualforce page, that will trigger in cascade all
the above logic. No need to have any dedicated javascript or api or anything like this developed in the calling app. As you know Canvas apps must run over https. With this technology your external app does not require ssl.