Friday 22 October 2010

Microsoft Dynamics CRM 2011 & Bing Maps Integration

















Following on from a previous post, here is the code to plot Microsoft Dynamics CRM 2011 records using Bing Maps:



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
&nbsp;
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>REST JQuery Map</title>       
    <meta http-equiv="X-UA-Compatible" content="IE=8" />
    <script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.3"></script>
    <script src="../ClientGlobalContext.js.aspx"></script>
    <script src="Scripts/jquery1.4.1.min.js" type="text/javascript"></script> 
    <script src="Scripts/RESTJQueryMap.js" type="text/javascript"></script>
</head>
<body style="border-width:0px;padding-left:0px;padding-top:0px;margin-left:0px;margin-top:0px;margin-bottom:0px;margin-right:0px">
    <div id="map" style="position:relative; width:650px; height:450px;"></div>
    <script language="javascript" type="text/javascript">
    // <![CDATA[


    //Create a new Bing Map
    var map = null;
    var results = null;
    map = new VEMap('map');
    map.SetCredentials("***KEY***");


    //Search CRM records
    function Search() {
        //Search London
        retrieveMultiple("AccountSet", "?$filter=substringof('London',Address1_City)", SearchCompleted, null);
    }


    //Callback - Search Success
    function SearchCompleted(data, textStatus, XmlHttpRequest) {
        //Display Map
        map.LoadMap();
        //map.LoadMap(new VELatLong(50.6, -0.3, 0, VEAltitudeMode.RelativeToGround), 10, VEMapStyle.Road, false, VEMapMode.Mode2D, true, 1);


        //Plot CRM Accounts on the map
        if (data && data.length > 0) {


            for (var i = 0; i < data.length; i++) {
                
                //Get Location of CRM Record and plot on map
                var geocodeRequest = "http://dev.virtualearth.net/REST/v1/Locations/" + data[i].Address1_PostalCode + "?output=json&jsonp=GeocodeCallback&key=***KEY***";
                CallRestService(geocodeRequest);
            }
        }


        map.SetCenterAndZoom(new VELatLong(51.51, -0.11), 13);


    }


    function GeocodeCallback(result) {
        //alert("Found location: " + result.resourceSets[0].resources[0].name);


        if (result && result.resourceSets && result.resourceSets.length > 0 && result.resourceSets[0].resources && result.resourceSets[0].resources.length > 0) {            
            // Add a pushpin at the found location
            var latlong = new VELatLong(result.resourceSets[0].resources[0].point.coordinates[0], result.resourceSets[0].resources[0].point.coordinates[1]);
            var pushpin = new VEShape(VEShapeType.Pushpin, latlong);
            map.AddShape(pushpin);


        }
    }


    function CallRestService(request) {
        var script = document.createElement("script");
        script.setAttribute("type", "text/javascript");
        script.setAttribute("src", request);
        document.body.appendChild(script);
    }


    window.onload = Search;
    // ]]>
</script>
</body> 
</html>

This sample also uses the Bing Maps REST Locations API to return longitude and latitude data for a given address!

You will need to replace ***KEY*** with your own Bing Maps API key. You can create a new developer account the following URL:
http://www.bingmapsportal.com/

Thursday 21 October 2010

Microsoft Dynamics CRM 2011 Map Integration

This blog post will explore how you can create an interactive map displaying a list of CRM Accounts for a user provided City:






















From the Microsoft Dynamics CRM 2011 Beta SDK:

What is REST?
REST stands for Representational State Transfer. REST is an architectural style in which every resource is addressed using a unique URI. In Microsoft Dynamics CRM a resource may be an entity collection or a record.  REST works the way the Internet works. You interact with resources using standard HTTP verbs such as GET, POST, MERGE, and DELETE. Various libraries can be used to process the HTTP requests and responses. REST provides a standard interface that you can use with any programming language. REST allows for either synchronous or asynchronous processing of operations. The capability to perform asynchronous operations makes REST well suited for Ajax and Silverlight clients.

By using REST and JQuery we can query the Microsoft Dynamics CRM data and return a collection of records. This example is based on sample code that can be found within the CRM 2011 beta SDK:
sdk\samplecode\cs\generalprogramming\dataservices\restjquerycontacteditor


Step 1 - Get a Map API Developer Account
For this example I am using Yahoo! Map Web Service, but you can use whatever service you prefer (Google, Bing etc)

Step 2 - HTML Page
You will need to build a web page that displays a map using the Web Service account created in Step 1.

Yahoo! Maps provide several API examples that you can use to get started. The following is my HTML page which provides a text box to enable users to search for Accounts (or any record type) by City:

JQueryMap.htm


<!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>     
    <title>REST JQuery Map</title>     
        
    <meta http-equiv="X-UA-Compatible" content="IE=8" />     


    <script type="text/javascript" src="http://api.maps.yahoo.com/ajaxymap?v=3.8&appid=***YOURAPPID***">
    </script>  
    <script src="../ClientGlobalContext.js.aspx"></script>
    <script src="Scripts/jquery1.4.1.min.js" type="text/javascript"></script> 
    <script src="Scripts/RESTJQueryMap.js" type="text/javascript"></script>
    <script language="javascript" type="text/javascript">


// <![CDATA[


    //Search Button Event Handler
    function SearchButton_onclick() {


        //Search where City contains data
        if (SearchText.value) {
            retrieveMultiple("AccountSet", "?$filter=substringof('" + SearchText.value + "',Address1_City)", SearchCompleted, null);
        }
        //Retrieve all Accounts
        else {
            retrieveMultiple("AccountSet", null, SearchCompleted, null);
        }
    }


    //Callback - Search Success
    function SearchCompleted(data, textStatus, XmlHttpRequest) {


        //Create a new Yahoo! Map
        var map = new YMap(document.getElementById('map'));
        map.addTypeControl();
        map.addZoomLong();
        map.addPanControl();
        map.drawZoomAndCenter(SearchText.value + ", United Kingdom", 11);


        //Plot CRM Accounts on the map
        if (data && data.length > 0) {


            for (var i=0; i<data.length; i++) {
                //Get CRM data and add a marker to represent the Account    
                var marker = createMarker(data[i].Address1_PostalCode, i, data[i].Name, data[i].AccountId);
                map.addOverlay(marker);
            }
        }
        else {
            alert('No records!');
        }
    }
    
    //Helper URL: developer.yahoo.com/maps/ajax/#ex4
    function createMarker(geopoint, num, name, id) {
        var myImage = new YImage();
        myImage.src = 'https://***YOURORG***.crm4.dynamics.com//WebResources/epuk_accountIcon';
        myImage.size = new YSize(16, 16);
        myImage.offsetSmartWindow = new YCoordPoint(0, 0);
        var marker = new YMarker(geopoint, myImage);
        var swtext = "<a target=\"_blank\" href=\"" + serverUrl + "/main.aspx?etc=1&id=%7b" + id + "%7d&pagetype=entityrecord\">View</a>" + name + "</A>"
        YEvent.Capture(marker, EventsList.MouseClick, function () { marker.openSmartWindow(swtext) });
        return marker;
    }
// ]]>
</script>
<style type="text/css">
#map {
height: 100%;
width: 100%;
}
</style>
</head>

<body style="background:lightblue;">
    <h3 style="font-family:Arial;">Search for Accounts by City</h3>
    <div>
        <label style="font-family:Arial;">City: </label>
        <input type="text" style="width:200px;" id="SearchText" />
        <input type="button" id="SearchButton" value="Search" style="width:80px;"   onclick="return SearchButton_onclick()" />
    </div>
    <div>
    <table style="width: 100%; height: 100%;">
<tr>
<td>
<div id="map"></div>
</td>
</tr>
    </table>
    </div>         
</body> 
</html>

You must remember to replace ***YOURAPPID*** and ***YOURORG*** with appropriate values for your Developer Account and Microsoft Dynamics CRM 2011 organisation.

Step 3 - JScript Files
The HTML page above refers to several JScript files, including:

ClientGlobalContext.js.aspx - The GetGlobalContext function exists in ClientGlobalContext.js.aspx. This function provides the Server URL.

JQuery1.4.1.min.js - A popular JScript library that includes capabilities to perform asynchronous data operations using the $.ajax object. jQuery1.4.1.js comes standard in a Visual Studio 2010 Web Application.

RESTJQueryMap.js - A generic library that can perform CRUD operations using jquery1.4.1.min.js. (This can be found within the sample code highlighted at the top of this post)


Step 4 - Web Resources
You need to create web resources for all the bold files list above, within Microsoft Dynamics CRM 2011:

Be sure to “Save” and “Publish” all Web Resources. Use the Text Editor when creating JScript files, to avoid any syntax errors, when they run.

Name the Web Resources as follows:
1)    /RESTJQueryMap.htm (Web Page) – Refers to the html file above and is the web resource used for running the sample.
2)    /Scripts/jquery1.4.1.min.js (Script) – Refers to the minimized jQuery library.
3)    /Scripts/RESTJQueryMap.js (Script) – Refers to the custom ODataJQuery library.

You should also create a web resource for the CRM record type you would like to plot on the map. In this example I will upload the CRM Account icon and create the following web resource:
AccountIcon.gif (GIF)

Step 5 - Test Solution
Open the HTML web resource and click the Preview button to test:


















Step 6 - Add to Dashboard
Add the Web Resource to a new Microsoft Dynamics CRM 2011 Dashboard: