Wednesday, March 28, 2012

CRM 2011 Tool Tip: Customizations using System Settings. Application and Form Mode.

With the release of Rollup 7 to CRM 2011, the Read-Optimized form mode has now been made available for the individual user to choose. However if you want to make the change globally, or not allow an individual user to choose what type of mode you have to adjust the System Settings.


Navigate to the System Settings pane you see above. Choose the Customization tab.


Application Mode - Application mode setting. In System Settings you can choose whether    Microsoft Dynamics CRM will launch in application mode. Application mode hides the address, tool, and menu bars on the browser.

Form Mode - Within form mode you can choose to default the form to editable or read only. You can also disable users from selecting the other option. However since Read Optimized is new, let's go deeper into that.


. However, if your IFrame calls in to the client API to get some form data, you might get errors on the form because read-optimized forms don’t have client API support.

So far my testing has revealed the following:
  • If your instance of CRM has global javascript files as Web Resources or in fact if you rely on Web Resources in any facet, the form will auto select the Editable form.
  • When removing Web Resources, the form goes into read-optimized view.
  • When pointing to an aspx page, the form is currently failing. The aspx page I am trying to load will invoke a silverlight xap file.
This topic is still in progress and I will update accordingly.


Tuesday, March 20, 2012

CRM 2011 and OData: Retrieve Example Using Async and Sync Methods

I've found in my transition from CRM 4 to CRM 2011 the ability to use REST and SOAP to be the two biggest improvements to developers. The benefit of both is essentially making the CRM 2011 UI fluid similiar to AJAX. In fact, this example used an AJAX call.

In this example I have successfully used oData to query the db and return a value which can be used to validate ribbon buttons, data, etc.

At the moment this is only configured to retrieve but we can easily extend to create and update.

retrieveRecord: function (id, odataSetName, successCallback, errorCallback) {
        //GetGlobalContext function exists in ClientGlobalContext.js.aspx so the
        //host HTML page must have a reference to ClientGlobalContext.js.aspx.
        var context = this.GetCrmContext();
        //Retrieve the server url, which differs on-premise from on-line and
        //shouldn't be hard-coded.
        var serverUrl = context.Page.context.getServerUrl();
        //The XRM OData end-point
        var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc";

        //id is required
        if (!id) {
            alert("record id is required.");
            return;
        }
        //odataSetName is required, i.e. "AccountSet"
        if (!odataSetName) {
            alert("odataSetName is required.");
            return;
        }
        if (successCallback != null) {
            //Asynchronous AJAX function to Retrieve a CRM record using OData
            $.ajax({
                type: "GET",
                contentType: "application/json; charset=utf-8",
                datatype: "json",
                url: serverUrl + ODATA_ENDPOINT + "/" + odataSetName + "(guid'" + id + "')",
                beforeSend: function (XMLHttpRequest) {
                    //Specifying this header ensures that the results will be returned as JSON.            
                    XMLHttpRequest.setRequestHeader("Accept", "application/json");
                },
                success: function (data, textStatus, XmlHttpRequest) {
                    if (successCallback) {
                        successCallback(data.d, textStatus, XmlHttpRequest);
                    }
                },
                error: function (XmlHttpRequest, textStatus, errorThrown) {
                    if (errorCallback)
                        errorCallback(XmlHttpRequest, textStatus, errorThrown);
                    else
                        errorHandler(XmlHttpRequest, textStatus, errorThrown);
                }
            });
        }
        else {
            var request = new XMLHttpRequest();
            request.open("GET", serverUrl + ODATA_ENDPOINT + "/" + odataSetName + "(guid'" + id + "')", false);
            request.setRequestHeader("Accept", "application/json");
            request.setRequestHeader("Content-Type", "application/json; charset=utf-8");
            request.send();
            var $objJquery = jQuery.parseJSON(request.responseText);
            return $objJquery.d;
        }
    }
 GetCrmContext: function () {
        var params = null;
        var myXrm = null;
        // Get XRM object (if possible)
        try {
            if (typeof (Xrm) != 'undefined' && Xrm != null) {
                myXrm = Xrm;
            }
        }
        catch (ex) {
            // DO NOTHING
        }
        return myXrm;
    }


The method has 4 parameters, 2 of which are required:

1.       The guid of the record. This is found by using the following command:

a.       var _id = Xrm.Page.data.entity.getId();

2.       string of the entity set. i.e. new_AccountSet. As with all javascript, proper capitalization is required.

3.       (optional) success Callback method. If you want to run async, provide a method and be ready to handle the return object.

4.       (optional) failure Callback method. Also, if async, provide a method for failure.



NOTE: If you do not set callback methods, then the method will try to run the query inline. If successful you’ll have to provide it something to set:

                var _id = Xrm.Page.data.entity.getId();
                var objAccount = retrieveRecord(_id, "new_AccountSet", null, null);

If you choose to provide a success method, if might look something like this:

function retrieveEntityCompleted(data, textStatus, XmlHttpRequest) {
    _recordData = data;
}