Exchanging data between CRM Forms and IFRAMEs

One of the most common requirements when we add content in IFRAMEs or Web Resourses in CRM is to have the ability to communicate with the calling or source CRM form to perform all sorts of operation. This article explains how this can be achieved using the postMessage JavaScript messaging mechanism.

What does postMessage do?

It’s a JavaScript method which was initially created to facilitate cross-origin communication between web pages. There are often valid scenario in which we need to display a page from another website in a new window or in an IFRAME. That’s the easy part. The more complicated part is when there is a requirement to perform some operations on the source page based on a action that occurs on the external page in the new window or IFRAME.

In the example below, consider we have a contact form that displays an external page in an IFRAME. When a button in clicked in the IFRAME, information (example, a lookup value) is passed to the CRM form, the value is set on the screen and the form is saved.

Step 1 – Write methods for the CRM Form: Register Listener, Message Handler  

The code below contains two methods, the first one (RegisterListener) sets a method that will be called if the postMessage is invoked (UpdateContactLookup). 


SADAX.Contact =
{
   RegisterListener: function () {

     if (window.addEventListener) {
       window.addEventListener('message', SADAX.Contact.UpdateContactLookup, false);
     }
     else { // IE8 or earlier
       window.attachEvent('onmessage', SADAX.Contact.UpdateContactLookup);
     }
   },

   UpdateContactLookup: function (event) {

     var origin = "";
     if (event.domain) origin = event.domain; // IE
     else if (event.origin) origin = event.origin; // FireFox - Chrome

     if ("https://source" == origin) {

       if (event.data != null) {

         var entity = SadaxJSON.parse(event.data);
         if (entity) {

           var value = new Array();
           value[0] = new Object();
           value[0].id = entity.Id;
           value[0].entityType = entity.LogicalName;

           var lookup = Xrm.Page.getAttribute("sadax_referencecontactid");
           lookup.setValue(value);
Xrm.Page.data.save();
}
}
else
{
alert("This message has been posted by an unknown source ('" + origin + "', expected 'https://source').");
return;
}
} 
}

Step 2 – Register the listener on Form’s OnLoad event

image

Step 3 – Write Code for the IFRAME to post the Message from the IFRAME

After the business logic execution, the postMessage method is called on the parent CRM form as follows:

function SetContactLookup(entityReference)
{
   var entity = {};
   entity.Id = entityReference.id;
   entity.LogicalName = entityReference.entityType;

   window.parent.postMessage(SadaxJSON.stringify(entity), "https://source");
}

Comments / Wrap up

At step 3, using the postMessage on the window.parent will cause the SADAX.Contact.UpdateContactLookup method to be fired on the parent CRM form. Notice that the method receives an event object as a parameter. The content of the object slightly differs based on the browser being used (IE vs rest of the world). Mostly, you should pay attention to the event.domain or event.origin attributes. This is used to validate the website that posted the message is safe (i.e. the one you are expecting).

There is also an event.data attribute that contains parameters that are sent from one page to another. In this case, we are using a renamed JSON library to stringify our custom object types. Passing an object without stringifying it would work, but we found it didn’t work well in all browsers/version. The reason for the renamed JSON library is again browser compatibility reasons. If IE8 is out of scope for you, you probably do not need this.

Notes:

  • This also work for web resources opened in a different window using window.open or Xrm.Utility.openWebResource
  • IE8 doesn’t allow to postMessage to other windows, only to iframes.
  • I haven’t tried this in CRM Online so you can try it by yourself if need be.

CRM 2013 – Leveraging Actions to get around JavaScript cross-domain challenges

Last year, I wrote about the challenge of cross-domain calls from JavaScript with CRM 2011. The issue was related to fact that from a security perspective, you could not have JavaScript functions executing on a CRM form event or when a ribbon button is clicked calling web services outside of the CRM domain. I proposed a few workarounds here but the bottom line is that in all cases, there was some sort of a negative impact in each solution. With CRM 2013, actions processes are a great way to get around the browsers’ cross domain restriction.

In this article, I am providing an example of how actions can be used to make a request to a web service outside of the CRM domain from a record’s form event.

Scenario

When users call an incident management center, the agents need to capture the temperature at the time of the incident in the city where it occurred. In order to do so, we decide to create a button on the command bar that agents can click on and that will perform the following tasks:

  • Read the City and Country information from the form
  • Call an Action that
    • Takes the City and Country as parameters
    • Use an external web service to get the temperature in the city
    • Returns the temperature as output parameters
  • Sets the temperature field value on the form

Note: In order to achieve this directly with JavaScript, performing Step 2 of the action would require to make a cross-domain call from JavaScript.

Process Configuration

To start things off, let’s start be creating an Action of type process. This action will have 2 input values (City and Country) and 2 output values (Temperature in Fahrenheit and in Celsius).

The steps are really simple. The action needs to a custom workflow activity that will connect to a weather web service and return the temperature in two output values of its own.

Custom Workflow Activity

Here you can see what the custom workflow activity code looks like. It’s very straight forward in that it only has 3 steps:

  1. Read the city and country inputs
  2. Initialize the web service client object and make the web service call
  3. Set the values back in the output fields to be available in the process

Calling Action from JavaScript

This is the last piece of the puzzle. At this point, we simply need to call the action created from a JavaScript event (ribbon button clicked, on change of a value etc.). In order to achieve this, create a function that is called when the client even occurs. That function should execute the Action, read the output values and set the field values on the form. This has to be done with a SOAP call. There are two ways to do this. You can:

  • use Deepak’s example as show in his blog post here or
  • use the CRM 2013 Sdk.Soap.js action message generator published by the Microsoft CRM SDK team to generate a request and response class you can use in your call to call the action using JavaScript.

Wrap up

This is another great example of how actions can be leveraged to work around challenges that we’ve dealt with in the pre-CRM 2013 era. Keep in mind that in order to call an external web service from the custom workflow activity, there are server security elements to take into consideration (e.g. can my server talk to the web service? etc.)

Hope this helps!

Making Cross Domain AJAX calls from CRM Form JScript

One of the cool things about JScript in CRM 2011 is that you can now use the jQuery $.ajax method to call, most notably the CRM REST endpoint. Now, one of the things that is sometimes required in customers’ solution is the ability to call other REST endpoints from a CRM form. An example of that use case would be to have a button that calls a web service in order to perform some sort of operation (e.g. modify your current record, or just send data over another systems for synchronization etc.) in a synchronous and interactive way. Another example would be to automatically fill fields on a form with information coming from another system (retrieve them via web service and set the values on the form). There are two main issues with doing that :

  • First, some browsers will simply not allow jQuery AJAX requests to run if they are made to a domain different than that of the running web application. The reason behind is that not all browsers can create XMLHttpRequest objects that have a withCredentials property. That is used to specify that we want to include the user credentials in a cross domain request. To always have that working, jQuery has the solution for us. Use the following statement in the code before making the AJAX call: jQuery.support.cors = true;
  • The other more complicated issue is that most browsers simply won’t allow JavaScript to make a cross domain request for security reasons. In most contexts, this sounds normal and reasonable.

Here are a few workarounds if you are facing the second issue.

  1. Try to transfer your logic to the server side. Can you do what you need by using a dialog or a workflow with a custom activity (server side code)? Can your logic be performed on a synchronous plugin (server side code)? If one of these solutions is acceptable, then this is what you should do.Applies to: CRM Online, On Premise, ADFS / IFD
  2. Use IIS URL Rewrite rules on your server. To illustrate, let’s say your CRM server URL is http://MyCrmServer/MyOrg and your external REST endpoint is http://RestEndpoint/RestService?params. You can create a URL rewrite rule that will transform all requests coming in as http://MyCrmServer/RESTENDPOINT/RestService?params to http://RestEndpoint/RestService?params. In other words, whenever IIS sees the string MyCrmServer/RESTENDPOINT in a URL it replaces it with RestEndpoint. It is simple to configure with Regular Expressions.

    This is a good solution because it handles the request on the server side. From a Jscript perspective, it is as if you are querying the CRM server and the request is transformed once it reaches the server. You can read more about IIS URL Rewrite here. The downside of this approach is that every single page that is opened from the CRM web app triggers a URL rewrite rule evaluation. Microsoft says the algorithm are optimized for performance and I have never experienced visible delays due to using the rewriter. Also worth noting that obviously, this is not supported for CRM Online since we don’t have access to Microsoft’s CRM Servers.

    Applies to: CRM On Premise, ADFS / IFD (if provider gives access to CRM Server)

  3. Enable Cross Domain browsing on the browser. In IE, this is blocked by default. You can change the settings by going to Internet Options Security and Custom Level Settings. In most case, this solution is not acceptable for an enterprise’s perspective for security reasons.


    Applies to: CRM On Premise, ADFS / IFD, (CRM Online? Not confirmed but it should work)

I personally think making requests to a cross domain web site via JavaScript should not be done if there are acceptable other options simply because it adds overhead somewhere based on the solution you go with. (e.g. if you go with solution 2 on premises, you have to make sure all your CRM servers have rewrite rules create and enable etc.).

[*** Update | 13/06/2014 ***] If you are using CRM 2013, see this article on how to use Action processes to get around this challenge.

Hope this helps!