Wednesday, January 26, 2011

Bing Map Integration with CRM 2011

Integrating the BING Maps in MS CRM 2011:

1) Create an account in Bing map
2) Download the SDK
3) Understand the REST architecture
4) Integrate the MAP using the REST architecture

Understanding the basics of REST architecture:

What is Rest?
Representational State Transfer : REST is a way to access documents and resources using a simple remote software architecture, represented by an API. The Web (World Wide Web) is an application of the REST architecture.

This architecture is defined as follows:
- States and functions of a remote application are considered as resources.
- Each resource is only accessible using a standard address. These are hyper links. Under HTTP these are URIs.
- Resources have a standard interface in which the transactions and data types are precisely defined. XML or JSON for example, or HTML.

RESTful web services:
A RESTful web service (also called a RESTful web API) is a simple web service implemented using HTTP and the principles of REST. It is a collection of resources, with three defined aspects:

1. the base URI for the web service, such as http://example.com/resources/
2. the Internet media type of the data supported by the web service. This is often JSON, XML or YAML but can be any other valid Internet media type.
3. the set of operations supported by the web service using HTTP methods (e.g., POST, GET, PUT or DELETE).

What is JSON?
JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write.
JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others. These properties make JSON an ideal data-interchange language.

JSON is built on two structures:

A collection of name/value pairs. In various languages, this is realized as an object, record, struct, dictionary, hash table, keyed list, or associative array.
An ordered list of values. In most languages, this is realized as an array, vector, list, or sequence.
These are universal data structures. Virtually all modern programming languages support them in one form or another. It makes sense that a data format that is interchangable with programming languages also be based on these structures.


Ah lots of theory!, Lets start in action.Integrating the Map in CRM 2011.

1) Create a HTML resource file like the one below.(the goal is to show the Account (In CRM terms) on the map)



  

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=8" />
    <title>Bing Maps REST with Jquery</title>
    <script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
    <script type="text/javascript" src="ClientGlobalContext.js.aspx"></script>
    <script type="text/javascript">
/**************************************************************************
This Function is for getting an object Id from the CRM
        IMP: Pass record object-type code and unique identifier as parameter 
        option is selected in the CRM.
By doing this you will get the context obj ID from the CRM.
Parse the URL to retrieve the GUID as in the below code.
**************************************************************************/
        var objId;
        function getParameters(values, unescapeValues) {
            var objArr;
            var parameters = new Array();
            var vals = ('?' == values.charAt(0) ? values.substr(1) : values).split("&");
            for (var i in vals) {
                var entityIdArr;
                objArr = vals[i].split("=");
                if (objArr[0] == 'id') {
                    objId = objArr[1].substr(3, 36);
                }
            }
        }

/**************************************************************************
This Function is for constructing the JSON object and 
        retrieve the result from the web service
**************************************************************************/
        var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc";
        var context = Xrm.Page.context || GetGlobalContext();
        var serverUrl = context.getServerUrl();
        
/**************************************************************************
Constructing the JSON object and query 
**************************************************************************/
        function loadMapfromCRM() {
            getParameters(location.search, true);
            if (objId != null) {
                retrieveAccountRecord(objId);
            }
        }

        function retrieveAccountRecord(Id) {
            var retrieveAccountReq = new XMLHttpRequest();
            retrieveAccountReq.open("GET", ODATA_ENDPOINT + "/AccountSet(guid'" + Id + "')", true);
            retrieveAccountReq.setRequestHeader("Accept", "application/json");
            retrieveAccountReq.setRequestHeader("Content-Type", "application/json; charset=utf-8");
            retrieveAccountReq.onreadystatechange = function () {
                retrieveAccountReqCallBack(this);
            };
            retrieveAccountReq.send();
        }

        function retrieveAccountReqCallBack(retrieveAccountReq) {
            if (retrieveAccountReq.readyState == 4 /* complete */) {
                if (retrieveAccountReq.status == 200) {
                    //Success
                    var retrievedAccount = JSON.parse(retrieveAccountReq.responseText).d;
                    address = retrievedAccount.Address1_City + "," + retrievedAccount.Address1_StateOrProvince;
                    map.Find(null, address, null, null, 0, 1, false, false, false, false,
                    function (shapeLayer, results, places, moreResults, error) {
                        var place = places[0];
                        var pushpin = new VEPushpin('1', place.LatLong, //latitude, longitude
                                                    'you can place your icon resource url as you see in the webresource', 
                                                    retrievedAccount.Name, //Title
                                                    address  //Notes
                                                    );
//For adding the pushpins
                        map.AddPushpin(pushpin);

//For setting the Zoom level
                        map.SetCenterAndZoom(place.LatLong, 7);

                    });
                }
                else {
                        errorHandler(retrieveAccountReq);
                        alert("retrieveAccountReqCallBack function failure END");
                }
            }
        }
    </script>
</head>
<body>
    <div id="map" style="position:absolute; top:0px; left:0px; width:100%; height:100%;"/>
</body>
</html>
<script type="text/javascript">
    //Load the map
    var map = new VEMap("map");
    map.SetCredentials("YOUR CREDENTIALS<KEY>");
    map.LoadMap();
    
    //Resize the map
    window.onresize = function (event) { map.Resize(document.body.clientHeight); };
    window.onresize(null);
    loadMapfromCRM();
</script>


2) Go to Customizations -> Web Resources -> Add a HTML new web resource and choose the one you created as above

3) Create a tab in one of the entity forms and add the Web Resource and *IMPORTANT* select an option "Pass record object-type code and unique identifier as parameter"

4) Thats all yours! you can test the MAP.