1

Calling publish(<message>) in the publisher.js API with any valid message/data causes Click To Dial to stop working until any sforce.interaction function is called (ie. sforce.interaction.isInConsole()).

For example:

function alertOnClickToDial (eventData) { alert("ClickToDial on number " + JSON.parse(eventData.result).number); }
sforce.interaction.cti.onClickToDial(alertOnClickToDial);

results in this:
Alert

This works until publish() is called:

Sfdc.canvas.publisher.publish({name: "publisher.customActionMessage", payload: {actionName: "CustomActionName", message: "ClickToDial"}});

The alert stops appearing.

Debugging shows that the callback function is never executed.

4

The issue seems to be due to the data in the MessageEvent being sent by the Salesforce InteractionFramework.

The data in a successful MessageEvent should look something like this:

interactionApi/?method=onClickToDial&<some data about the record/phone number>

which indicates the message is for the interaction api and the method is onClickToDial (along with some data about the record/number clicked on).

After calling publish the data now looks like this:

?method=onClickToDial&<some data about the record/phone number>

the "interactionApi/" prefix is missing.

interaction.js:

When you look at interaction.js it's clear why this is a problem.

function processPostMessage(event) {
    // Check if call is for entity feed
    if (isApiMessage(event, ENTITY_FEED_API)) {
        if (entityFeedApi && entityFeedApi.processPostMessage) {
            params = entityFeedApi.processPostMessage(event);
        }
        if (!params) {
            return;
        }
    } else if (isApiMessage(event, INTERACTION_API)) {
        if (event.origin !== frameOrigin || !frameOrigin) {
            // Only trust messages from the adapter frame
            return;
        }

        var message = event.data.replace(INTERACTION_API, ''); // strip off API target
        params = parseUrlQueryString(message);

        // convert string true/false to boolean for methods that needs to return boolean values.
        if (params && (params.result === 'true' || params.result === 'false')) {
            params.result = params.result === 'true';
        }
    } else {
        // return if postMessage is not targeted to interaction API
        return;
    }
}

In particular

else if (isApiMessage(event, INTERACTION_API))

prevents the API from responding to messages that were not directed to it, which seems very sensible, but this means the second onClickToDial call is ignored.

InteractionFramework.js (auto-formatted in Chrome Dev Tools):

The culprit is in InteractionFramework.js.

a.INTERACTION_API = "interactionApi/";
a.isVersion25 = !1; //false

...

a.processPostMessage = function(b) {
    var c, e, d;
    try {
        if (b.origin.toLowerCase() !== a.sofphoneEventOriginUrl)
            a.doPostMessage(null , null , LC.getLabel("InteractionApi", "InvalidSoftphoneIframeOrigin"));
        else if (c = b.data || "",
        !(0 === c.indexOf(a.INTEGRATION_API) || 0 === c.indexOf(a.ENTITY_FEED_API)))
            if (0 === c.indexOf(a.INTERACTION_API) ? (c = c.replace(a.INTERACTION_API, ""),
            a.isVersion25 = !1) : a.isVersion25 = !0,

...

a.doPostMessage = function(b, c, e) {
    b = Sfdc.Url.generateQueryString("", {
        method: b,
        result: c,
        error: e
    });
    b = (a.isVersion25 ? "" : a.INTERACTION_API) + b;
    g ? Sfdc.support.servicedesk.PopoutWindowManager.sendMessage(g, "ProcessApiCallback", {
        postMessageData: b,
        iframeProxyData: null 
    }) : a.softphoneEventSource && a.softphoneEventSource.postMessage(b, a.sofphoneEventOriginUrl)
};

Which reads better like this:

a.isVersion25 = false;

...

// function processPostMessage()
if (0 === messageData.indexOf("interactionApi/")){
    messageData = messageData.replace("interactionApi/", "");
    a.isVersion25 = false;
}
else{
    a.isVersion25 = true;
}

...

// function doPostMessage()
messageData = (a.isVersion25 ? "" : a.INTERACTION_API) + messageData;

a.isVersion25 is initialised to false, but is set every time a message is processed.
If the message data starts with "interactionApi/" then it is stripped and isVersion25 is set to false. The boolean value is then examined in doPostMessage and "interactionApi/" is added to the start again.

When calling the publish method another message is sent with data that looks like this:

{
    "seq":2,
    "src":"client",
    "clientVersion":"35.0",
    "body":{
        "type":"publish",
        "config":{
            "client:{
                "isVF":true,
                "oauthToken":"null",
                "instanceId":"SoftphoneIframe",
                "targetOrigin":"*"
            }
        },
        "event":{
            "name":"publisher.customActionMessage",
            "payload":{
                "actionName":"CustomActionName",
                "message":"ClickToDial"
            }
        }
    },
    "targetModule":"Canvas"
}

This JSON object does not start with "interactionApi/" so a.isVersion25 is set to true. After this a Click To Dial will call into doPostMessage and find the value of a.isVersion25 is true, so leave the message data untouched.

Workaround

It's not very nice, but you can call any sforce.interaction function (such as isInConsole or isVisible) to reset it to a working state as these cause messages starting with "interactionApi/" to be sent.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.