“Software is a great combination between artistry and engineering. When you finally get done and get to appreciate what you have done it is like a part of yourself that you've put together. I think a lot of the people here feel that way.” -- Bill Gates

Showing posts with label CRM Javascript SDK. Show all posts
Showing posts with label CRM Javascript SDK. Show all posts

Friday, February 5, 2010

Microsoft Dynamics CRM 4.0 JavaScript SDK

Ascentium released a Javascript SDK for Microsoft Dyanmics CRM 4.0 in the year 2008. This was the best JS SDK for CRM 4.0 that I came across. It abstracts all the rote code that one has to write for preparing the soap request, sending the request, handling the response in both synchronous and asynchronous ways. You can find the complete details of what this library in this blog post. You can find the download the code from Ascentium here.


Though the SDK from Ascentium covers all the functionality it has a few shortcomings:
It works only for CRM On Premise installation.
It does not provide a way to login as a different user.

Though it looks like a small issue, the above two limits the usage of this SDK in a lot of scenarios.

While working one of the projects at Proteans, I came across this library. Going through the code, I realized I just need to change few functions to get it working for all kinds of deployment and also provide an ability to login as different users.

In summary, I modified three functions and added one new function. I am posting the changes here. I will also release the sdk with complete changes at a later date. Thanks to Proteans for letting me work on this.

CRMService: This function now takes additional optional parameters in User Name, Passsword, AuthType. (These parameters are optional only for on premise deployment).
function Ascentium_CrmService(sOrg, sServer, sUserName, sPassword, iAuthType) {
/// CrmService object constructor. allows for calling crm webservice CRUD operations directly from client script.
/// CRM Organization Name. Not required if used on a CRM form.
/// (optional) URL of CRM Server. Url of current window is used if this is null.
/// (optional) UserName of CRM User. User Name is optional if OnPremise installation.
/// (optional) Password of CRM User. Password is optional if OnPremise installation.
/// (optional) Authentication Type to the CRM Server. Defaulted to OnPremise(AuthType = 0) installation this is null.

this.org = sOrg;
this.server = sServer;
this.username = sUserName;
this.password = sPassword;
this.authtype = iAuthType;

if (sOrg == null) {
if (typeof (ORG_UNIQUE_NAME) != "undefined") {
this.org = ORG_UNIQUE_NAME;
}
else {
//Error Handler
alert("Error: Org Name must be defined.");
}
}

// URL was not provided, assume the JS file is running from the CRM Server
if (sServer == null) {
this.server = window.location.protocol + "//" + window.location.host;
}

// Defaulting to OnPremise installation if AuthType is not present
if (iAuthType) {
this.authtype = iAuthType;
} else {
this.authtype = 0;
}

// If either UserName or Password is not present, default to OnPremise CRM installation.
if (sUserName == null || sPassword == null) {
this.username = null;
this.password = null;
this.authtype = 0;
}
}

ExecuteRequest: This function now is modified to take care of both On Premise and IFD deployments.
Ascentium_CrmService.prototype._ExecuteRequest = function(sXml, sMessage, fInternalCallback, fUserCallback, sUrl) {
// Create the XMLHTTP object for the Update method.
var oXmlHttp = this.CreateXmlHttp();

try {
//We need to handle the cases when username and password are present for each of the deployment scenarios
if (this.authtype == 0) {
if (this.username) {
oXmlHttp.open("POST", this.server + "/mscrmservices/2007/crmservice.asmx", (fUserCallback != null), this.username, this.password);
}
else {
oXmlHttp.open("POST", this.server + "/mscrmservices/2007/crmservice.asmx", (fUserCallback != null));
}
}
else {
// We also need to execute the request to get the CRM Ticket as part of header. In this scenario we use sURL to pass the discovery service URL
if (sUrl) {
oXmlHttp.open("POST", sUrl, (fUserCallback != null), this.username, this.password);
} else {
oXmlHttp.open("POST", this.server + "/mscrmservices/2007/crmservice.asmx", (fUserCallback != null), this.username, this.password);
}
}
oXmlHttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
oXmlHttp.setRequestHeader("Content-Length", sXml.length);

// In Case of AuthType 2, when getting the CRM Ticket sMessage is ""
if (sMessage) {
oXmlHttp.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/" + sMessage);
}

if (fUserCallback != null) {
//asynchronous: register callback function, then send the request.
var crmServiceObject = this;
oXmlHttp.onreadystatechange = function() { fInternalCallback.call(crmServiceObject, oXmlHttp, fUserCallback) };
oXmlHttp.send(sXml);
}
else {
//synchronous: send request, then call the callback function directly
oXmlHttp.send(sXml);
return fInternalCallback.call(this, oXmlHttp, null);
}
}
catch (er) {
}
}

GetHeader: Modified to work for both On Premise and IFD deployments. In case we are going to extend this for Microsoft Online CRM deployment and integrate with Passport, this is the function to change.
Ascentium_CrmService.prototype._GetHeader = function() {

var sEnv = ''
sEnv += '';

var sxml1 = '';
sxml1 += '' + this.authtype + '';

var sxml2 = ('' + this._crmXmlEncode(this.org) + '');
sxml2 += '00000000-0000-0000-0000-000000000000';
sxml2 += '';

// Use CRM's GenerateAuthenticationHeader function if it is available.
if (typeof (GenerateAuthenticationHeader) != "undefined") {
return sEnv + GenerateAuthenticationHeader();
}
else {
// For an on Premise Deployment
if (this.authtype === 0) {
return sEnv + sxml1 + sxml2;
}
else {
// For an IFD Deployment
var sxml = sEnv + '';
sxml += ('' + this._crmXmlEncode(this.org) + '');
sxml += ('' + this._crmXmlEncode(this.username) + '');
sxml += ('' + this._crmXmlEncode(this.password) + '');
sxml += '';
var url = this.server + "/MSCRMServices/2007/SPLA/CrmDiscoveryService.asmx";
//Make a call to crm discvoery service to get CRM Ticket
var crmTicket = ('' + this._ExecuteRequest(sxml, "", this._GetHeaderCallBack, null, url) + '');
return sEnv + sxml1 + crmTicket + sxml2;
}
}
}
GeHeaderCallBack: This function is added to handle the response in case of IFD deployment and return the CRM ticket.
Ascentium_CrmService.prototype._GetHeaderCallBack = function(oXmlHttp, callback) {
///(private) Get Header Call Back method.Returns the CRM Ticket in case of IFD deployment

//oXmlHttp must be completed
if (oXmlHttp.readyState != XMLHTTPREADY) {
return;
}

//check for server errors
if (this._HandleErrors(oXmlHttp)) {
return;
}

//Get the CRM Ticket from the returned xml
var oResult = oXmlHttp.responseXML.selectSingleNode("//CrmTicket");

//return CRM Ticket if sync, or call user callback func if async
if (callback != null) {
callback(oResult.text);
}
else {
return oResult.text;
}
}


I will update you as and when I get to publish the complete script. Until then you can download the code from Ascentium and make the above modifications.

‎

Read more!