March 30, 2015

JavaScript OData Pagination with synchronous calls

One limit of the OData endpoint is that the response can only include up to 50 records, so if your result set has more records it's necessary to reiterate the request using the url inside the __next property contained inside the returned object.

The following code shows how to fetch the request using synchronous calls, because sometimes you need (or want) to block the user :)
function getODataRecords(ODataUrl) {

    // we return an object with a similar structure as the OData endpoint
    var allRecords = new Object();
    allRecords.results = new Array();
    
    // we loop until we have an url to query
    var queryUrl = ODataUrl;
    while(queryUrl != null) {

        // we build the request
        var ODataRequest = new XMLHttpRequest(); 
        ODataRequest.open("GET", queryUrl, false); // false = synchronous request
        ODataRequest.setRequestHeader("Accept", "application/json"); 
        ODataRequest.setRequestHeader("Content-Type", "application/json; charset=utf-8"); 
        ODataRequest.send();

        if (ODataRequest.status === 200) {
            var parsedResults = JSON.parse(ODataRequest.responseText).d;
            if (parsedResults != null && parsedResults.results != null) {

                // we add the results to our object
                for (var i = 0; i < parsedResults.results.length; i++) {
                    allRecords.results.push(parsedResults.results[i]);
                }

                // check if there are more records and set the new url, otherwise we set to null the url
                if (parsedResults.__next != null) {
                    queryUrl = parsedResults.__next;
                } else {
                    queryUrl = null;
                }
            }
        } else {
            // if the request has errors we stop and return a null result
            queryUrl = null;
            allRecords = null;
        }
    }

    return allRecords;
}

// sample function to return all the accounts
function GetAllAccounts() {
    var serverUrl;
    if (Xrm.Page.context.getClientUrl !== undefined) {
        serverUrl = Xrm.Page.context.getClientUrl();
    } else {
        serverUrl = Xrm.Page.context.getServerUrl();
    }
    var ODataPath = serverUrl + "/XRMServices/2011/OrganizationData.svc";
    var accountQueryUrl = ODataPath + "/AcccountSet?$select=AccountNumber,Name";

    // call our new method
    var retrievedAccounts = getODataRecords(accountQueryUrl);

    // alert each result
    if (retrievedAccounts != null) {
        for (var i = 0; i < retrievedAccounts.results.length; i++) {
            alert(retrievedAccounts.results[i].Name + " - " + retrievedAccounts.results[i].AccountNumber);
        }
    }
}

March 9, 2015

Japan and Australia Data Center Discovery URLs

Microsoft launched the new Data Center in Japan for CRM Online, you can read the announcement by Bob Stutz here:
Japan Datacenter Open for Business: Now Serving Japanese CRM Online Customers.

The Australia Data Center is also operative and available for companies from Australia, New Zealand and Fiji.

Microsoft defined also the Discovery Web Service URL of these data centers, the addresses are:
https://disco.crm7.dynamics.com/XRMServices/2011/Discovery.svc (Japan)
https://disco.crm6.dynamics.com/XRMServices/2011/Discovery.svc (Australia)

The Japanese data center is identified by the crm7 host, the Australian one by crm6. This will affect also the organization url, for example if Contoso Ltd. has two branches, one in Japan and the other one in Australia, their urls will be:
https://contosojapan.crm7.dynamics.com
https://contosoaustralia.crm6.dynamics.com

The relative MSDN page has been updated to include this change, the next table is a recap:

HostLocation
crmNorth America
crm4EMEA
crm5APAC
crm2South America
crm9North America 2 (CRM Online for Government)
crm7Japan
crm6Australia

March 3, 2015

Moment.js and Dynamics CRM: Multi Language support

The current version of Dynamics CRM supports 45 languages (an excel file with the list can be downloaded here: http://1drv.ms/18Ih0Ux) mapped to LCID codes.
This LCID code is also the one returned by the following methods: Xrm.Page.context.getUserLcid and Xrm.Page.context.geOrgLcid (the language of the user and the base language of the organization).

Moment.js is a powerful library to manipulate dates in JavaScript and it has support for internationalization but accepts only a locale string ("en", "it", ...) and not the LCID value returned by the above methods. I created a small JavaScript library (crm_lang.js) that accepts the 45 LCID CRM codes in order to return their corresponding Moment.js locale strings. The library can be downloaded from Technet Gallery:

http://gallery.technet.microsoft.com/scriptcenter/Momentjs-and-Dynamics-CRM-8bd3a264

Moment.js is very useful when comes to manipulate dates (add or subtracts days) and more important for this sample to display the date in various formats:
var now = moment();
alert(now.format());                                // "2015-03-03T08:02:17-05:00" (ISO 8601)
alert(now.format("dddd, MMMM Do YYYY, h:mm:ss a")); // "Tuesday, March 3rd 2015, 3:25:50 pm"
As I wrote before it supports internationalization, but it requires the locale string:
var now = moment();
now.locale("es"); // set the language to Spanish
alert(now.format("LLLL")); // "martes, 3 de marzo de 2015 8:00"
with the library I created, it's easier to set the right locale:
var now = moment();
var userLcid = Xrm.Page.context.getUserLcid(); //suppose is 1043 (Dutch)
var userLocale = CRMLanguages.getMomentLocale(userLcid);
now.locale(userLocale); // or now.locale(CRMLanguages.getMomentLocale(Xrm.Page.context.getUserLcid()));
alert(now.format("LLL")); // "3 maart 2015 08:00"
Moment.js constructor accepts a standard JavaScript date object, this makes easier to works with CRM form values:
var createdOn = Xrm.Page.getAttribute("createdon").getValue(); // get the date value
var m_date = moment(createdOn); // create the Moment.js object
m_date.locale(CRMLanguages.getMomentLocale(Xrm.Page.context.getUserLcid())); // set the language
m_date.add(3, "days"); // we add 3 days to the createdon date
alert(m_date.format("LLL")); // "2015年3月6日午前8時0分" //1041 Japanese
It's also possible to return the date as a standard JS object, the method is toDate()
// following the previous code
var threeDaysLater = m_date.toDate();
Xrm.Page.getAttribute("new_datecheck").setValue(threeDaysLater);
As you can see Moment.js can be very useful, make sure you read the documentation http://momentjs.com/docs/ and if you need a multi language support you can use my library. LLAP!