Thursday 17 October 2013

contentIFrame in CRM 2013

contentIFrame in CRM 2013

So, Just as usual, was trying to validate my JS using IE 10 Developer Tool and there I was shocked.

The contentIFrame object returned NULL... Have they used something other than an IFrame.. Come on that's not possible..

Well they changed the name.. This is where I hate the developers.. Why should you change something just for the sake of it!! :(

So now, you can access the from elements and test your JS (without creating a webresource/ adding a debugger and all such pains) by using 'contentIFrame0'

Eg: contentIFrame0.Xrm.Page.ui.setFormNotification("Test","ERROR","test")
       contentIFrame0.Xrm.Page.getAttribute('subject').getValue()

Hope it helps!! :) Happy Coding!!

Wednesday 16 October 2013

As Cool as Actions!! : CRM 2013

Actions in CRM 2013


CRM 2013 is blah blah blah.. Everyone knows that.. we would have checked it in many blogs, many sites.. But one feature that stood out for me was 'Actions'. Come on how cool can CRM get?

Now I don't want to use the same old steps to achieve a requirement in CRM 2013. Well, lets analyze some advantages of actions with an example. 

Here is a typical requirement that one might face..


1. Open an Opty, enter the required details.
2. Click the 'Command Button' (Ribbon buttons are our father's property actually) titled as 'Approve Opty'.
3.  Send an e-mail to the concerned Account saying that your opty is now activated and read for processing.
4. Create a task to Send an e-mail to the concerned sales person saying that the opty that you created for the account is now under process.
56. Change the status for the opty.

The above steps should happen when the user 
 1. Clicks the 'Approve Opty' Button in the form.
 2. Clicks the 'Approve Opty' button in the grid.
 3. Runs a console app which does bulk 'approval' job.
 4. A plugin that is executed on changing the status of the Opty.

So how to do it in CRM 2011?

 1. Create a ribbon button.
 2. Add JS to the button to, say, change a fields value (A Dummy Field, of course).
 3. Based on this field's value, a workflow will trigger to send email.
 4. Synchronous JS REST Code to create a new Task Record.
 5. Update JS REST call to update the current Opty Record and refresh the form.

huffff.... I just wasted hours writing all the pieces of code and I just implemented the button, still I have got the console app and the plugin, not to the mention tweaking some code for the Grid Button.

Here comes, the one I loved - ACTIONS in CRM 2013

 1. Create a Command Button.
 2. Create an Action for the Opty entity, with input parameter as the string to be set as body in the emails
 3. Create Output Parameter to get the created Task's GUID.
 4. Create Steps to send Emails, create tasks and to assign output parameter values.
 5. Update the status of the Opty.

So how many lines of code that I have written here? ZERO

Now I need to call the newly created action in my button, plugin, console app.

For a button - Create a SOAP request.
For the plugin / console app - Create a simple OrganizationRequest Object and pass the two string input as parameters.

Ha.. that's it.. I have saved..hmm.. let us say a min of 2 hours (Come on I did this for the first time!!! )..

Not to mention that I can export this action to another org..... 
 

Actions -- Way to Go!! :)


Okay, enough of designs, lets jump into the development part.. Here is the code that I used..

NOTE: I have given a sample for Account below instead of Opty. Sorry for the inconvenience :(. 

1. Create an Action as given in the below snapshots.

Entity: Account
Category: Action
Unique Name (Important, Note this down): new_ActivateAccount
Activate As: Process

Add two arguments:
One is an Input Paramter called 'AccountActivationTime'.
Another is an Output Parameter called 'TaskCreatedGUID'

 

 Next is to create steps. As most of us are good in workflows, I'll just show the snapshot on what should be achieved.

 



2. Get the SOAP Request.

   Use the pretty tool 'SOAPLogger' of SDK to generate the request. You need to key in the below code to get its request and response.

 OrganizationRequest orgRequest = new OrganizationRequest("new_ActivateAccount");
 orgRequest["AccountActivationTime"] = "Test String";
 orgRequest["Target"] = new EntityReference("account", new Guid("1A53F2C4-E22C-E311-A2B4-D89D676580C8"));
 OrganizationResponse orgResponse = slos.Execute(orgRequest); 


The request generated will be as below:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <request xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">
        <a:Parameters xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
          <a:KeyValuePairOfstringanyType>
            <b:key>AccountActivationTime</b:key>
            <b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">Test String</b:value>
          </a:KeyValuePairOfstringanyType>
          <a:KeyValuePairOfstringanyType>
            <b:key>Target</b:key>
            <b:value i:type="a:EntityReference">
              <a:Id>1a53f2c4-e22c-e311-a2b4-d89d676580c8</a:Id>
              <a:LogicalName>account</a:LogicalName>
              <a:Name i:nil="true" />
            </b:value>
          </a:KeyValuePairOfstringanyType>
        </a:Parameters>
        <a:RequestId i:nil="true" />
        <a:RequestName>new_ActivateAccount</a:RequestName>
      </request>
    </Execute>
  </s:Body>
</s:Envelope>


So here, we just to replace the GUID and the string parameter as variables. Below is the final Request which we are going to use in our button.

"<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <request xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">
        <a:Parameters xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
          <a:KeyValuePairOfstringanyType>
            <b:key>AccountActivationTime</b:key>
            <b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">"+accountActivationTime+"</b:value>
          </a:KeyValuePairOfstringanyType>
          <a:KeyValuePairOfstringanyType>
            <b:key>Target</b:key>
            <b:value i:type="a:EntityReference">
              <a:Id>"+accountGUID+"</a:Id>
              <a:LogicalName>account</a:LogicalName>
              <a:Name i:nil="true" />
            </b:value>
          </a:KeyValuePairOfstringanyType>
        </a:Parameters>
        <a:RequestId i:nil="true" />
        <a:RequestName>new_ActivateAccount</a:RequestName>
      </request>
    </Execute>
  </s:Body>
</s:Envelope>"


The expected response will be as below:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <ExecuteResponse xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <ExecuteResult xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">
        <a:ResponseName>new_ActivateAccount</a:ResponseName>
        <a:Results xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
          <a:KeyValuePairOfstringanyType>
            <b:key>TaskCreatedGUID</b:key>
            <b:value i:type="a:EntityReference">
              <a:Id>e31282f9-f636-e311-b74f-d89d67652150</a:Id>
              <a:LogicalName>task</a:LogicalName>
              <a:Name i:nil="true" />
            </b:value>
          </a:KeyValuePairOfstringanyType>
        </a:Results>
      </ExecuteResult>
    </ExecuteResponse>
  </s:Body>
</s:Envelope>


(You can find that your action worked, when you executed the soap logger, A task/email would have been created. :) )

3. Create the JS and upload as webresource

 I have uploaded the below JS:

function ActivateAccountRequest(accountGuid, activationTime) {
    var requestMain = ""
    requestMain += "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
    requestMain += "  <s:Body>";
    requestMain += "    <Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">";
    requestMain += "      <request xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\">";
    requestMain += "        <a:Parameters xmlns:b=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">";
    requestMain += "          <a:KeyValuePairOfstringanyType>";
    requestMain += "            <b:key>AccountActivationTime</b:key>";
    requestMain += "            <b:value i:type=\"c:string\" xmlns:c=\"http://www.w3.org/2001/XMLSchema\">" + activationTime + "</b:value>";
    requestMain += "          </a:KeyValuePairOfstringanyType>";
    requestMain += "          <a:KeyValuePairOfstringanyType>";
    requestMain += "            <b:key>Target</b:key>";
    requestMain += "            <b:value i:type=\"a:EntityReference\">";
    requestMain += "              <a:Id>" + accountGuid + "</a:Id>";
    requestMain += "              <a:LogicalName>account</a:LogicalName>";
    requestMain += "              <a:Name i:nil=\"true\" />";
    requestMain += "            </b:value>";
    requestMain += "          </a:KeyValuePairOfstringanyType>";
    requestMain += "        </a:Parameters>";
    requestMain += "        <a:RequestId i:nil=\"true\" />";
    requestMain += "        <a:RequestName>new_ActivateAccount</a:RequestName>";
    requestMain += "      </request>";
    requestMain += "    </Execute>";
    requestMain += "  </s:Body>";
    requestMain += "</s:Envelope>";

    var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
    xmlHttpRequest.Open("POST", Xrm.Page.context.getServerUrl() + "/XRMServices/2011/Organization.svc/web", false);
    xmlHttpRequest.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
    xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    xmlHttpRequest.send(requestMain);

    if (xmlHttpRequest.status == 200) {
        if (xmlHttpRequest.responseXML != null && xmlHttpRequest.responseXML.getElementsByTagName("a:Id").length > 0) {
            var msg = xmlHttpRequest.responseXML.selectSingleNode('//a:Id').nodeTypedValue;
            alert(msg);
        }
    }
}


4. Create the button in Account Form

Complex part, is it? NopeThe UI has changed, but the XML remains the same. So you can just use the popular tools like CRM 2011 Visual Ribbon Editor, Pragma Tool Kit or the Ribbon Workbench (If you are lazy) or you can go ahead and create ribbons the usual way of modifying the Ribbon XML (I went for the tool..lol). 

I have created a button called 'Activate Account' and have created a JS action to call the above function.
That's it. Publish the customizations and there it works like a charm!! :) .. So how to use it in Console and Plugins?? I'll update it in my next post.!

Happy Coding!! :)