Monday, July 23, 2012

CRM2011 and Cross Domain calls Part 2

In my last blog, we discussed what is JSONP and how to make cross domain calls using JSONP? In this blog, I will share how to make cross domain calls using jQuery and Ajax. I will use the currency exchange and twitter APIs to make JSONP calls. As of version 1.5 jQuery supports JSONP calls.

How to make a JSONP call using jQuery

Here is the code. The code is using the twitter APIs. The code will retrieve the tweets by Donna Edward( CRM MVP) and display the text of the first tweet.
<HTML><HEAD><TITLE></TITLE>
<SCRIPT src="http://servername/SandpitAmreek//WebResources/new_/jquery1.7.2.js"></SCRIPT>

<SCRIPT type=text/javascript>

function callTheJsonp()

{

//debugger;

// the url of the script where we send the asynchronous call 

//var url = "http://openexchangerates.org/api/latest.json?callback=?";

jQuery.getJSON("https://api.twitter.com/1/statuses/user_timeline.json?callback=?", { include_entities: "true", include_rts: "true", screen_name: "edwardsdna" },
 function(data) {
   alert(data[0].text);
});


}

} </SCRIPT>

<META charset=utf-8></HEAD>
<BODY onload=callTheJsonp() contentEditable=true>hey hey</BODY></HTML>

Run the code and we will received the similar to the following screen.
image

How to make a JSONP call using AJAX

Here is the code call the JSONP call using AJAX.
<HTML><HEAD><TITLE></TITLE>
<SCRIPT src="http://auntmsftv23/SandpitAmreek/WebResources/new_/jquery1.7.2.js"></SCRIPT>

<SCRIPT type=text/javascript>

function callTheJsonp()

{

debugger;

// the url of the script where we send the asynchronous call 

var url = "http://openexchangerates.org/api/latest.json?callback=parseRequest";

$.ajax({

   url: url,

   dataType: "jsonp",

   jsonpCallback: "parseRequest"

});


}

// this function should parse responses.. you can do anything you need.. 

// you can make it general so it would parse all the responses the page receives based on a data field

function parseRequest(data)

{

try // try to output this to the javascript console 
{
   alert("AUD: " + data.rates.AUD);} 

catch(an_exception)
// alert for the users that don't have a javascript console
{
     alert(data);
} 

} </SCRIPT>

<META charset=utf-8></HEAD>
<BODY onload=callTheJsonp() contentEditable=true>Testing Cross Domain Calls</BODY></HTML>

Run the code and we will received message USD/AUD exchange rate .
image

Monday, July 16, 2012

CRM2011 and Cross Domain calls Part 1

What are cross domain calls?

These are the calls we make from a web page using JavaScript to access resources at a different URL. This URL can be separate website or domain or port name. This can be calling a same URL using ip address instead of using computer name as I discussed in my previous post.
In IE, these calls are not allowed. If you try to call a web service using ajax, you will get an error message “Access is denied”. The problem is XMLHttpRequest object used in the AJAX calls. This object stops us making calls to other domains. AJAX communication is restricted to the server of origin. It is by design and it protects as against the malicious hack these calls can cause. But sometimes these calls are essential. In day to day basis we do need to make these calls.  For example, if you would like display exchange rates in CRM2011, we have to make a call to some external API/web service. In this blog we will discuss how to make cross domain calls in JavaScript.
I found this free currency exchange API http://openexchangerates.org/api/latest.json. This URL will return the exchange rates for about 150 currencies in JSON format. I have created an html web resource in CRM2011 to retrieve the exchange rates using JQuery AJAX call. Here is the code. I have also added a reference to JQuery web resource to this html web resource. 
<HTML><HEAD><TITLE></TITLE>
<SCRIPT src="https://crm5org01a03.crm5.dynamics.com//WebResources/new_/jquery1.7.2.js"></SCRIPT>

<SCRIPT type=text/javascript>

function callExchangeRate()
{

//debugger;
jQuery.support.cors = true;
var myurl = "http://openexchangerates.org/api/latest.json";
   $.ajax({
        type: "GET",
        contentType: "application/json; charset=utf-8",
        datatype: "json",
        url: myurl,
        beforeSend: function (XMLHttpRequest) { XMLHttpRequest.setRequestHeader("Accept", "application/json"); },
        success: function (data, textStatus, XmlHttpRequest) {
            // Use for a single selected entity
            alert(data.rates.AUD); // this line will display the exchange rate of AUD aganst USD
        },
        error: function (xmlHttpRequest, textStatus, errorThrown) {
            alert("Status: " + textStatus + "; ErrorThrown: " + errorThrown);
        }
    });

}

</SCRIPT>

<META charset=utf-8></HEAD>
<BODY onload=callExchangeRate() contentEditable=true>Testing Cross Domain Calls</BODY></HTML>


When we run this web resource, we will receive the following error message.image
image

How to make a cross domain calls

So how can we get around this problem? There are few solutions to this problem. I will discuss the following 2 options.
  1. JSONP
  2. XDomainRequest (Cross Domain Request)

Using JSONP

JSONP stands for JSON with padding but it has nothing to do with JSON. This is an old technique of using <script> tag to load external resources. JSONP dynamically creates a <script> tag to get the data instead of using XMLHttpRequest object. The <script> tag will load whatever is returned from the URL specified as its “src” attribute. The other cool feature of JSONP calls is Callback function.
For example look at the following URL
http://openexchangerates.org/api/latest.json. If we use this URL in <script> tag it will attached the response to “src” tag. But if we use http://openexchangerates.org/api/latest.json?callback=parseRequest, the response will be wrapped in a parseRequest  method. We can use this method to process the response.
I have created an html web resource using this technique. Here is the code.
<HTML><HEAD><TITLE></TITLE>
<SCRIPT type=text/javascript>
function callTheJsonp()
{
//debugger;

// the url of the script where we send the asynchronous call 

var url = "http://openexchangerates.org/api/latest.json?callback=parseRequest";

// create a new script element

var script = document.createElement('script');

// set the src attribute to that url

script.setAttribute('src', url);

// insert the script in out page

document.getElementsByTagName('head')[0].appendChild(script);

}

// this function should parse responses.. you can do anything you need.. 

// you can make it general so it would parse all the responses the page receives based on a response field

function parseRequest(data)
{
   try // try to output this to the javascript console 
   {
       alert("AUD: " + data.rates.AUD); 
   }
   catch(an_exception)
   // alert for the users that don't have a javascript console
   {
      alert(data); 
   } 

} </SCRIPT>

<META charset=utf-8></HEAD>
<BODY onload=callTheJsonp() contentEditable=true>Testing Cross Domain Calls</BODY></HTML>

When we open the html web resource, it will display the following message.
image
Note: To work with JSONP, the web service has to be JSONP compatible. If you are creating your own web services and you would like to consume them in JavaScript using JSONP, make sure they are  JSONP compatible. The currency conversion API, I am using in this example supports JSONP so does flicker, twitter and Google APIs etc.
In my next blog I will explain how to make JSONP calls using JQuery and AJAX and also how to use XDomainRequest.

Friday, July 6, 2012

How to change the form header colour based on the field value in CRM2011

This is a very common request from the customers to colour code the entity forms based on the field values. For example, a cold opportunity should be represented by red colour or a hot opportunity should be represented by green. This can be achieved as explained in the blog by Gonzalo Ruiz.In this blog I will be doing the same but using CSS web resources. The solution is inspired by the CEBlog.

Scenario

I am changing the account form header based on the  value of customertypecode option set field.
  • If the option set value is 1 then change the form header to red.
  • If the option set value is 2 then change the form header to green.
  • Else keep the default colour.

Solution

The solution consists of 3 web resources
  • CSS web resource for red colour
I have named this web resource “new_/ColoredHeaderRed.css”. Here is the code.
.ms-crm-Form-HeaderContainer{
 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#FF0000, endColorstr=#fff6f8faE);
 }
  • CSS web resource for green colour
I have named this web resource “new_/ColoredHeaderGreen.css”. Here is the code.
.ms-crm-Form-HeaderContainer{
 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#008000, endColorstr=#fff6f8faE);
 }
  • JavaScript web resource to attach the relevant CSS file to the form
I have named this web resource “new_/ColoredHeaderScript.js”. The code is using a switch statement to attach an appropriate CSS file to the form, based on the value of customertypecode field. I am not using default in my switch statement as I don’t want to create a CSS file for default colour. Here is a code.
function loadCSS()
{
   // get the value picklist field
    var relType = Xrm.Page.getAttribute("customertypecode").getValue();
    var filename;
    switch (relType) {
           case 1:
                 filename="/WebResources/new_/ColoredHeaderRed.css";
                 attachCSS(filename);
                 break;
           case 2:
                 filename="/WebResources/new_/ColoredHeaderGreen.css";
                 attachCSS(filename);
                 break;
     }
}// end function

function attachCSS(filename){
       var fileref = document.createElement("link");
       fileref.setAttribute("rel", "stylesheet");
       fileref.setAttribute("type", "text/css");
       fileref.setAttribute("href", filename);
       document.getElementsByTagName("head")[0].appendChild(fileref);
}
Now call the loadCSS()  function on account form load event.
image
Save and publish the changes and test the solution.

Results

If the option set value is 1.
image
If the option set value is 2.
image

Supported or Non Supported

CSS web resources are not meant to be used to change the style of built in forms. So technically it is unsupported. But, we are not trying to retrieve the value of field or DOM object. We are just attaching a style sheet. If the  Microsoft decided to change the name of the header class, it may stop the code to change the header colour but it won’t break the system.

Saturday, June 30, 2012

CRM2011 Workflow

In this blog I would share some of the OOB features of CRM2011 workflows that we don’t use very frequently. Here are some of those features
  • Workflow Templates
  • Automatically deleting the completed workflow jobs
  • Use of “Activity Count” and “Activity Count Including Process”

Workflow Templates

When we create a workflow , we have an option to activate the workflow as templates.
image
We can also activate an existing workflow as a ”Process Template” as long the workflow is in draft/deactivate state.
One of the biggest benefit of saving/activating a workflow as template is that we can create a copies of existing workflow without creating a workflow from scratch.

Automatically deleting the completed workflow jobs

In CRM, when the workflow is completed, it still appears in the system jobs as shown in the following screen.
image
We can delete these succeeded workflow entries automatically by checking the “Automatically delete completed workflow jobs(to save disk space” on the Administration tab of the workflows as shown in the following screen shot.
image

Use of “Activity Count” and “Activity Count Including Process”

When we use wait condition or check condition in workflows, under the “Local Values” there is a “Process” option  and there are 4 sub options available for “Process” as shown the screen shot. The 4 options are :
  • Execution Time
  • Activity Count
  • Activity Count Including Process
  • Timeout
image4

Execution Time

It will give you the execution time (date and time) of the workflow.

Activity Count

It will return the total number of activities excluding activities created through workflow steps associated to the workflow entity.

Activity Count Including Process

It will return the total number of activities including activities created through workflow steps associated to the workflow entity.

Timeout

It will pause the workflow execution for specified data and time. Look at the following blog for detail explaination
http://www.dynamicscrmtrickbag.com/2009/07/12/waits-timeouts/

Monday, June 25, 2012

Importing notes attachments in CRM2011

Recently I came across a situation, where I have to move an entity records and file attachments from one deployment to another.I have done this in CRM 4.0 using data migration wizard. I was thinking if I can do the same using “Data Import” tool in CRM2011.
After some searching on Google, I found few dynamic forums and a LinkedIn group that were very helpful. Here is a link to the LinkedIn group. Here is scenario. I have 2 separate deployments of CRM2011. They are not connected to each other. I need to move the accounts and file attachments (notes) from one deployment to another.
The solution is consists of 2 parts
  1. On CRM deployment 1, I will write some custom code to create a zip file that will contain 2 CSV file and “Attachments” folder containing all the attachments.
  2. On CRM deployment 2. I will use built-in “Data Import” tool to import this zip file into the system.

Creation of Zip files

In this blog, I won’t be writing any custom code to create a zip file but will explain the structure of zip file.I have created a folder named “Import”. In “Import” folder, I have created 2 files and folder as shown in the screen shot.
image
The account.csv file will look like the following screen
image
The  note.csv will like the following screen.
image
The fieldnames “File Name”, “PhysicalFileName” and “Regarding” are very important here. They are important to import the attachments and linking to the proper account records.
  • Column “Filename” will be mapped to “FileName” field on the notes entity.
  • Column “PhysicalFileName” be mapped to the “Document” field on the notes entity.
  • Column “Regarding” will be mapped to the “Regarding” field in the notes entity.
Attachments folder will contain all the attachments files. Just make sure that the "PhysicalFileName” of the files are unique. If I am creating this zip file through code then I may add the account number as prefix to the files.
Now the next step will be to zip this “Import” folder into “Import.zip” file.

Importing the zip file

Now take this “Import.zip” file to the second deployment and import the file using “Data Import” tool. Here are the steps:
image
image
image
image
If the names of CSV files match the crm entities, “Data Import” wizard will map them to the appropriate CRM entities as shown the screen shot above.
image
Now here you can click on the entities and map the fields between your CSV files and CRM entities. I am going to map the notes entity fields in the next step

image
Make sure these fields are mapped as shown in the above screen shot.

image
image
If you are going to use this process on regular basis, save the data map by providing a “Data Map Name” highlighted by yellow in the above screen shot.

image
Click on finish. Check the record on completion of the import process.

I have attached the sample zip file here. Download it and import into your system to test it.

Monday, June 11, 2012

How to display an entity form based on the option set field (Multiple forms CRM2011)

Sometimes we need multiple forms based on categorisation of an entity. For example, an account can be  a customer or a competitor or a supplier etc. It is possible that we need to capture or display different fields based on the type of an account. Suppose we have different forms  for different types of account. If the user opens an account record, the system should open a appropriate form based on the type of the account.
In this example I am using account entity. I have created 2 custom forms. Now I have 3 account forms (Information, Information1, Information2). In out of the box account entity . there is a “Relationship Type” or customertypecode field to categorise the account entity. It is picklist field. I want to open a different form based on the value of the customertypecode field . Here is the code.
function showForm() {

//if the form is update form
if (Xrm.Page.ui.getFormType()==2)
    // variable to store the name of the form
    var lblForm;

    // get the value picklist field
    var relType = Xrm.Page.getAttribute("customertypecode").getValue();

    // switch statement to assign the form to the picklist value
    //change the switch statement based on the forms numbers and picklist values
    switch (relType) {
        case 1:
            lblForm = "Information1";
            break;
        case 2:
            lblForm = "Information2";
            break;
        default:
            lblForm = "Information";
    }
    //check if the current form is form need to be displayed based on the value
    if (Xrm.Page.ui.formSelector.getCurrentItem().getLabel() != lblForm) {
        var items = Xrm.Page.ui.formSelector.items.get();
        for (var i in items) {
            var item = items[i];
            var itemId = item.getId();
            var itemLabel = item.getLabel()

            if (itemLabel == lblForm) {
                //navigate to the form
                item.navigate();
            } //endif
        } //end for
    } //endif
 }//endif
} //end function
The code is checking if the form is an  “Update” form and opens up the form based on the value of customertypecode field. The code is reloading the form only if the current form is different than the form the system should be displaying.
Call this function on form load event.
Happy programming…

Wednesday, May 30, 2012

Why and How to use relative path in CRM2011 JavaScript

As CRM developers, we need to use the paths of the URLs, web services and other web resources in JavaScript code. I am talking about CRM URLs for e.g. we will use the following URLs to work with  REST(OData) organization data service

https://hp7.crm5.dynamics.com/xrmservices/2011/organizationdata.svc/ (CRM Online)
http://15.218.138.225/HPDECSStaging/xrmservices/2011/organizationdata.svc/ (On premise)

The highlighted part of the URLs represents the server and organization name of the CRM.
Instead of using the hard coded server name, we should use some generic function that can give us the server and  the organization name of the CRM. It will help to deploy the same code for different organizations, servers and deployments without making any changes.
Most of the Microsoft samples use Xrm.Page.context.getServerUrl() function to retrieve the server URL.I have written the following function to display message box with  the oData organization data service URL.

function test()
{
alert (Xrm.Page.context.getServerUrl() + "/xrmservices/2011/organizationdata.svc");
}
I executed this function on CRM Online and On-Premise deployments. I used the following URLs to start the CRM in the browser
I received the following results.

On CRMOnline, the function returned the following message.
image
This worked as expected.

On on-premise deployment, It returned the following message.
image
It returned the server name (aun.....23) instead of IP address(http://15.218.138.225/HPDECSStaging). I was using an IP address in the browser and it returned the server name. If I try to use this URL to retrieve any data, I will get an error message “access denied”. For this javascript to work properly, the server name part of URL in the browser has to match the server name part of the URL returned by Xrm.Page.context.getServerUrl() method. Xrm.Page.context.getServerUrl() returns the servername stored in the deployment manager. Have a look at the following screen shot.

image
So, if you are using the ip address or localhost in a browser to start the CRM and you are using Xrm.Page.context.getServerUrl() to generate the URL, then you will always get ”access denied” error in the JavaScript.
To overcome this problem, we should use the relative path as shown below
"/xrmservices/2011/organizationdata.svc"
There is one more catch to it. It will work with CRM Online as organization name is a part of the server URL(https://hp7.crm5.dynamics.com).
But to make it work with on-premise deployment, we need to add organization name in the relative path. something  like this
“/” + orgname + "/xrmservices/2011/organizationdata.svc".
You can read the organization name from the context by using Xrm.Page.context.getOrgUniqueName() method. Now we have two different URLs one for on premise and one online deployments. When we are writing JavaScript libraries, we want them to work with every deployment without making any changes. Here is the solution, use  Xrm.Page.context.prependOrgName() method . It will sort the organization name problem. Here is my new test function.
function test()
{
alert (Xrm.Page.context.prependOrgName("/xrmservices/2011/organizationdata.svc"));
}
The function will return “/xrmservices/2011/organizationdata.svc” for CRMOnline deployment. “orgname/xrmservices/2011/organizationdata.svc for On premise deployment. In short, use relative paths with  Xrm.Page.context.prependOrgName(), when working with the URLs in javascript. It will work with online, on premise and hopefully IFD. I did not test the code on IFD deployment.