Archive for the ‘CRM 2011’ Category

getUserPrivilege

Returns an object with three Boolean properties corresponding to privileges indicating if the user can create, read or update data values for an attribute.

Xrm.Page.getAttribute("attributeschema").getUserPrivilege().canCreate
Xrm.Page.getAttribute("attributeschema").getUserPrivilege().canUpdate
Xrm.Page.getAttribute("attributeschema").getUserPrivilege().canRead

MS CRM 2011 supports integration with Windows Azure, by integrating it we can register plug-in’s and can pass the execution context to the azure solution.

MS Dynamics CRM 2011 has been integrated with the Windows Azure platform by coupling the Microsoft Dynamics CRM event execution pipeline to the Windows Azure Service Bus so effectively during save/update operation we can send processed information to Azure Service Bus. For detailed information look here

MS CRM 2011 can send messages to service bus using 4 different contracts:
1) Queue
2) One-way
3) Two-way
4) REST

In my blog, i’m going to work with the Two-Way Listener. Two way contract requires an active listener and if there is no active listener on an endpoint, the Microsoft Dynamics CRM post to the service bus fails. A Two way contract can return a string value from the listener to MS CRM 2011.

Here, I’m going to write the Two way listener code in the Azure Worker Role. Worker roles are applications that are developed to run asynchronously and to work on long-running or perpetual tasks independent of user interaction or input. It is similar to a windows service and uses a simple while(true) loop and a sleep statement.

To work with the Windows Azure Worker role, first you need to install the Windows Azure Sdk. Here is the link to download the Sdk.

Once the installation is done, you should get "Windows Azure Cloud Service" in your visual studio.

VS 2010

VS Azure

To create and deploy Worker Role, you should have the Windows Azure account. You can create a free azure trail account here.

First login into your Windows Azure account and create a Service Bus NameSpace. In my sample, my Servie Bus NameSpace is "CrmAzureIntegration"

SevBus

SerBusNS

Once the Service Bus Namespace is created, you should able to see "Default Issuer and Default Key"

SerBusNSKey

Creating a Worker Role

Open Visual Studio 2010, Click on New Project and select "Windows Azure Cloud Service" Click on Ok.

CloudServ

Select Worker Role and give it a Name.

WorkerRole

Add the following References to your Worker Role Project:
1) Microsoft.xrm.sdk
2) Microsoft.ServiceBus
3) System.ServiceModel
4) System.Runtime.Serialization

Inherit the "ITwoWayServiceEndpointPlugin" to your Worker Role as shown below.

Inherit

Add the following code to your worker role. You can also get the code from crm sdk

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Threading;
using Microsoft.ServiceBus;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.Storage;
using Microsoft.Xrm.Sdk;

namespace CrmAzureWorkerRole
{
    public class WorkerRole : RoleEntryPoint, ITwoWayServiceEndpointPlugin
    {
        public string Execute(RemoteExecutionContext executionContext)
        {
            string returnValue = "Service Call Successful";
            return returnValue;
        }

        public override void Run()
        {
            // This is a sample worker implementation. Replace with your logic.
            Trace.TraceInformation("CrmAzureWorkerRole entry point called", "Information");

            while (true)
            {
                Thread.Sleep(10000);
                Trace.TraceInformation("Working", "Information");
            }
        }

        public override bool OnStart()
        {
            // Set the maximum number of concurrent connections 
            ServicePointManager.DefaultConnectionLimit = 12;

            // For information on handling configuration changes
            // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.

            // Activate the Listener
            ActivateListener();

            return base.OnStart();
        }

        public void ActivateListener()
        {
            ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.Http;

            // Add service bus namespace
            string serviceNamespace = "crmazureintegration";

            // Add Default issuer name
            string issuerName = "owner";

            // Add Service bus Default Key 
            string issuerKey = "<Add key here>";

            string servicePath = "WorkerRole";

            // Leverage the Azure API to create the correct URI.
            Uri address = ServiceBusEnvironment.CreateServiceUri(
                Uri.UriSchemeHttps,
                serviceNamespace,
                servicePath);

            // Create the shared secret credentials object for the endpoint matching the 
            // Azure access control services issuer 
            var sharedSecretServiceBusCredential = new TransportClientEndpointBehavior()
            {
                TokenProvider = TokenProvider.CreateSharedSecretTokenProvider(issuerName, issuerKey)
            };

            // Using an HTTP binding instead of a SOAP binding for this endpoint.
            WS2007HttpRelayBinding binding = new WS2007HttpRelayBinding();
            binding.Security.Mode = EndToEndSecurityMode.Transport;

            // Create the service host for Azure to post messages to.
            ServiceHost host = new ServiceHost(typeof(WorkerRole));
            host.AddServiceEndpoint(typeof(ITwoWayServiceEndpointPlugin), binding, address);

            // Create the ServiceRegistrySettings behavior for the endpoint.
            var serviceRegistrySettings = new ServiceRegistrySettings(DiscoveryType.Public);

            // Add the service bus credentials to all endpoints specified in configuration.

            foreach (var endpoint in host.Description.Endpoints)
            {
                endpoint.Behaviors.Add(serviceRegistrySettings);
                endpoint.Behaviors.Add(sharedSecretServiceBusCredential);
            }

            // Begin listening for messages posted to Azure.
            host.Open();
        }
    }
}

Now we need to create a plug-in to call the Execute method which will initiate posting the execution context to the service bus.

Add the following References to your Plug-in:
1) Microsoft.xrm.sdk
2) System.Runtime.Serialization

You can get the following plug-in code from the crm sdk.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk;

namespace CrmAzurePlugin
{
    public class CrmAzureIntegration : IPlugin
    {
        private Guid serviceEndpointId;
        Entity entity = null;

        public CrmAzureIntegration(string config)
		{
			if (String.IsNullOrEmpty(config) || !Guid.TryParse(config, out serviceEndpointId))
			{
				throw new InvalidPluginExecutionException("Service endpoint ID should be passed as config.");
			}
		}

        public void Execute(IServiceProvider serviceProvider)
        {
            // Retrieve the execution context.
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

            if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
            {
                // Obtain the target business entity from the input parameters.
                entity = (Entity)context.InputParameters["Target"];
            }

            // Extract the Organization Service
            IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            // Extract the tracing service.
            ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
            if (tracingService == null)
                throw new InvalidPluginExecutionException("Failed to retrieve the tracing service.");

            IServiceEndpointNotificationService cloudService = (IServiceEndpointNotificationService)serviceProvider.GetService(typeof(IServiceEndpointNotificationService));
            if (cloudService == null)
                throw new InvalidPluginExecutionException("Failed to retrieve the service bus service.");

            try
            {
                tracingService.Trace("Posting the execution context.");
                string response = cloudService.Execute(new EntityReference("serviceendpoint", serviceEndpointId), context);
                
                if (!String.IsNullOrEmpty(response))
                {
                    // Update response back to the record
                    UpdateResponse(response, (Guid)entity.Attributes["crm_integrationid"], service);
                }
                tracingService.Trace("Done.");
            }
            catch (Exception e)
            {
                tracingService.Trace("Exception: {0}", e.ToString());
                throw;
            }
        }
        
        public void UpdateResponse(string response, Guid recordId, IOrganizationService service)
        {
            Entity integrationEntity = new Entity("crm_integration");
            integrationEntity["crm_response"] = response;
            integrationEntity.Id = recordId;
            service.Update(integrationEntity);
        }

    }
}

In the above code, once i get the response i’m going to update the crm record with the response string.

Now open your Plug-in Registration tool and Register the Service Endpoint as shown below :

Click on Register New Service EndPoint.

Register

Add Name, Service Bus Namespace, Path, Select Contract as Two Way and click "Save and Configure ACS"

ACS

Add Service Bus Default key in Management Key

Certificate

Download the certificate and Issuer Name from CRM. Login into crm, navigate to Settings and click on customizations, select Developer Resources

Once everything is added, Click on Configure ACS and finally click on Save and Verify Authentication.

Now you need to register the plugin and pass the service endpoint id from the unsecure configurtaion. In my case i’ve added the plugin on create of entity record.

Pluginstep

You can get the service endpoint id once you Register the Service Endpoint as shown below:

ServiceEndoint

Now we can go ahead and test the application. First we need to Activate the listener, so Run the worker role and set the breakpoint at "Execute" method.

Breakpoint

Once the listener is active, go to crm and create the entity record to trigger the Execute method.

crminte

Once u save the record, Execute method in Worker role gets triggered and sends back the "Service Call Successful" string to crm and updates the record in "Response" attribute as shown below.

breaktrigger

RecCreated

Deploy Worker Role to Azure Cloud Service

Right Click on your Azure Project and select Package and Click "Package" button as shown below

pack

Now open your Windows Azure account, click on "Cloud Services" and select New. Add the URL name and click on "Create Cloud Service"

cloudUI

Once your cloud service is created, Click on "Configure" and select "Upload A New Production Deployment"

Add the Deployment Name and select the Package and Configuration files which you have published earlier and click Ok.

prod

It takes few minutes to create your service and start running.

Once everything is finished and if your cloud service is running perfectly, that means now your listener is activated and you can go head and create your entity record in crm to trigger your service.

Happy coding 🙂

Create record in MS CRM 2011 Online using C# Code

Add "Microsoft.Xrm.Sdk" reference from your sdk bin folder.

Then add the following namespaces
1) using Microsoft.Xrm.Sdk;
2) using Microsoft.Xrm.Sdk.Client;
3) using System.ServiceModel.Description;
4) using System.Web.Services.Protocols;

Finally add the below code :


try
            {
                ClientCredentials cre = new ClientCredentials();
                cre.UserName.UserName = "<username>.onmicrosoft.com";
                cre.UserName.Password = "<password>";

                Uri serviceUri = new Uri("https://<orgname>.api.crm5.dynamics.com/XRMServices/2011/Organization.svc");

                OrganizationServiceProxy proxy = new OrganizationServiceProxy(serviceUri, null, cre, null);
                proxy.EnableProxyTypes();
                IOrganizationService service = (IOrganizationService)proxy;

                Entity ent = new Entity("<entitySchemaName>");
                ent.Attributes["new_name"] = "Created from Console App4";
                service.Create(ent);
            }
            catch (SoapException ex)
            {

            }
            catch (Exception ex)
            {

            }

Happy Coding 🙂

When retrieving records using OData in MS CRM 2011 the page size is fixed to Max 50 records. So in my example I’m going to push the first 50 records in an array and retrieve next page results using the URI (“__next”) provided in the JSON response object.

In my case i’m going to retrieve records in Account entity. Here is the code:

relatedAccounts = [];

function onload() {
    var serverUrl = Xrm.Page.context.getServerUrl();
    var oDataUri = serverUrl + "/xrmservices/2011/OrganizationData.svc/AccountSet?$select=AccountId,Name,&$filter=StateCode/Value eq 0";
    GetRecords(oDataUri);
    var totalRecords = relatedAccounts.length;
}

function GetRecords(url) {
    jQuery.ajax({
        type: "GET",
        contentType: "application/json; charset=utf-8",
        datatype: "json",
        url: url,
        async: false,
        beforeSend: function (XMLHttpRequest) {
            XMLHttpRequest.setRequestHeader("Accept", "application/json");
        },
        success: function (data, textStatus, XmlHttpRequest) {
            if (data && data.d != null && data.d.results != null) {
                AddRecordsToArray(data.d.results);
                FetchRecordsCallBack(data.d);
            }
        },
        error: function (XmlHttpRequest, textStatus, errorThrown) {
            alert("Error :  has occured during retrieval of the records ");
        }
    });
}

function AddRecordsToArray(records) {
    for (var i = 0; i < records.length; i++) {
        relatedAccounts.push(records[i]);
    }
}

function FetchRecordsCallBack(records) {
    if (records.__next != null) {
        var url = records.__next;
        GetRecords(url);
    }
}

urlrecords

TotalRecords

DetailedRecords

In my example below i’m going to retrieve link entities opportunity and orders in Contact entity.

retrieve

Below is the fetch xml code:

string fetchXml = @"<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='true'>
                                    <entity name='contact'>
                                        <attribute name='fullname' />
                                        <attribute name='contactid' />
                                        <link-entity name='opportunity' from='customerid' to='contactid' link-type='outer'>
                                            <attribute name='name' />
                                            <attribute name='description' />
                                        </link-entity>
                                        <link-entity name='salesorder' from='customerid' to='contactid' link-type='outer'>
                                            <attribute name='name' />
                                            <attribute name='ordernumber' />
                                        </link-entity>
                                    </entity>
                                </fetch>";

            var result = service.RetrieveMultiple(new FetchExpression(fetchXml));

Hope is helps 🙂

In one of my requirement i need to update the selected records in the home page contact entity grid by clicking on the custom ribbon button. Below is the code to add a ribbon button in the home page contact entity and update the selected records.

To update the records using OData, you need to add jquery1.4.1.min and json2 files. You can get those files from sdk
path – \sdk\samplecode\js\restendpoint\jqueryrestdataoperations\jqueryrestdataoperations\scripts.

Create a new solution, add the contact entity and export the solution. Open the customizations.xml file and replace the <RibbonDiffXml> with following code.

<RibbonDiffXml>
        <CustomActions>
          <CustomAction Id="Isv.contact.MainTab.CustomAction" Location="Mscrm.HomepageGrid.contact.MainTab.Collaborate.Controls._children" Sequence="41">
            <CommandUIDefinition>
              <Button Id="Isv.Grid.Contact.MainTab" Command="Isv.Grid.Contact.UpdateRecord.Command" LabelText="$LocLabels:Isv.Grid.Contact.UpdateRecord.LabelText" ToolTipTitle="$LocLabels:Isv.Grid.Contact.UpdateRecord.ToolTip" ToolTipDescription="$LocLabels:Isv.Grid.Contact.UpdateRecord.ToolTip" TemplateAlias="o1" Image16by16="/_imgs/ribbon/SendShortcut_16.png" Image32by32="/_imgs/ribbon/SendShortcut_32.png" />
            </CommandUIDefinition>
          </CustomAction>
        </CustomActions>
        <Templates>
          <RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
        </Templates>
        <CommandDefinitions>
          <CommandDefinition Id="Isv.Grid.Contact.UpdateRecord.Command">
            <EnableRules>
              <EnableRule Id="Isv.Grid.Contact.OneSelected.EnableRule" />
            </EnableRules>
            <DisplayRules>
              <DisplayRule Id="Isv.Grid.Contact.UpdateRecord.DisplayRule" />
            </DisplayRules>
            <Actions>
              <JavaScriptFunction Library="$webresource:crm_json2.js" FunctionName="IsNAN">
              </JavaScriptFunction>
              <JavaScriptFunction Library="$webresource:crm_jquery1.4.1.min.js" FunctionName="IsNAN">
                </JavaScriptFunction>
              <JavaScriptFunction Library="$webresource:crm_contact.js" FunctionName="updateRecord">
                <CrmParameter Value="SelectedControlSelectedItemIds" />
              </JavaScriptFunction>
            </Actions>
          </CommandDefinition>
        </CommandDefinitions>
        <RuleDefinitions>
          <TabDisplayRules />
          <DisplayRules>
            <DisplayRule Id="Isv.Grid.Contact.UpdateRecord.DisplayRule">
              <CrmClientTypeRule Type="Web" />
            </DisplayRule>
          </DisplayRules>
          <EnableRules>
            <EnableRule Id="Isv.Grid.Contact.OneSelected.EnableRule">
              <SelectionCountRule AppliesTo="SelectedEntity" Minimum="1" />
            </EnableRule>
          </EnableRules>
        </RuleDefinitions>
        <LocLabels>
          <LocLabel Id="Isv.Grid.Contact.UpdateRecord.LabelText">
            <Titles>
              <Title languagecode="1033" description="Update Contacts" />
            </Titles>
          </LocLabel>
          <LocLabel Id="Isv.Grid.Contact.UpdateRecord.ToolTip">
            <Titles>
              <Title languagecode="1033" description="Update the selected Contact records" />
            </Titles>
          </LocLabel>
        </LocLabels>
      </RibbonDiffXml>

After replacing the code save the file and import the solution into the CRM.

Make sure the jscript file and function name exists in your webresource. Below is my jscript function

function updateRecord(selectedIds) {
    if (selectedIds != null && selectedIds != "") {
        var strIds = selectedIds.toString();
        var arrIds = strIds.split(",");
        for (var indxIds = 0; indxIds < arrIds.length; indxIds++) {
            updateContactRecords(arrIds[indxIds]);
        }
        alert("Selected Records Updated Successfully");
    }
    else {
        alert("No records selected!");
    }
}

function updateContactRecords(contactId) {
    var objContact = new Object();
    objContact.CreditOnHold = true;

    var jsonEntity = window.JSON.stringify(objContact);

    var serverUrl = Xrm.Page.context.getServerUrl();
    var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc/ContactSet";
    var ODataPath = serverUrl + ODATA_ENDPOINT;
    $.ajax({
        type: "POST",
        contentType: "application/json; charset=utf-8",
        datatype: "json",
        url: ODataPath + "(guid'" + contactId + "')",
        data: jsonEntity,
        beforeSend: function (XMLHttpRequest) {
            XMLHttpRequest.setRequestHeader("Accept", "application/json");
            XMLHttpRequest.setRequestHeader("X-HTTP-Method", "MERGE");
        },
        error: function (xmlHttpRequest, textStatus, errorThrown) {
            alert("Status: " + textStatus + "; ErrorThrown: " + errorThrown);
        }
    });
}

Click here to view all the <CrmParameter> values that can be passed.

Close Activities in CRM 2011

Posted: November 10, 2012 in Code Snippets, CRM 2011
SetStateRequest setStateRequest = new SetStateRequest();

// In my case i'm Cancelling Task Activity
setStateRequest.EntityMoniker = new EntityReference(Task.EntityLogicalName, taskId);
            
// Set the State and Status OptionSet Values to Cancelled.
setStateRequest.State = new OptionSetValue(2);
setStateRequest.Status = new OptionSetValue(6);
            
// Execute the Response
SetStateResponse setStateResponse = (SetStateResponse)service.Execute(setStateRequest);

In the same way you can “Cancel” or “Complete” any CRM 2011 Activity by passing the EntityReference accordingly.

Below are the State and Status Code OptionSet values for CRM 2011 Activities.

Task
Fax
Phone Call
Email
Letter
Appointment
Service Activity
Campaign Response
Recurring Appointment

Adding a Custom Tab and Group to SubGrid in CRM 2011

Here i need to add a custom button by creating a new custom tab to the subgrid.

In my case I’m going to add sub grid button for opportunity entity. Create a new solution, add the opportunity entity and export the solution.

Open the customizations.xml file and replace the <RibbonDiffXml> with following code

<RibbonDiffXml>
        <CustomActions>
          <CustomAction Id="Mscrm.SubGrid.opportunity.CustomTab.CustomAction" Location="Mscrm.SubGrid.opportunity.ContextualTabs._children" Sequence="40">
            <CommandUIDefinition>
              <Tab Id="Mscrm.SubGrid.opportunity.CustomTab" Command="Mscrm.SubGrid.opportunity.CustomTab" Title="$LocLabels:Mscrm.SubGrid.opportunity.CustomTab.Title" Description="$LocLabels:Mscrm.SubGrid.opportunity.CustomTab.Title" Sequence="500">
                <Scaling Id="Mscrm.SubGrid.opportunity.CustomTab.Scaling">
                  <MaxSize Id="Mscrm.SubGrid.opportunity.CustomTab.CustomGroup.MaxSize" GroupId="Mscrm.SubGrid.opportunity.CustomTab.CustomGroup" Sequence="10" Size="LargeMedium" />
                </Scaling>
                <Groups Id="Mscrm.SubGrid.opportunity.CustomTab.Groups">
                  <Group Id="Mscrm.SubGrid.opportunity.CustomTab.CustomGroup" Command="Mscrm.SubGrid.opportunity.CustomGroup" Sequence="10" Title="$LocLabels:Mscrm.SubGrid.opportunity.CustomGroup.Title" Template="Mscrm.Templates.3.3">
                    <Controls Id="Mscrm.SubGrid.opportunity.CustomTab.CustomGroup.Controls">
                      <Button Id="Mscrm.SubGrid.opportunity.CustomTab.CustomGroup.CustomButton" ToolTipTitle="$LocLabels:Mscrm.SubGrid.opportunity.CustomButton.ToolTip" ToolTipDescription="$LocLabels:Mscrm.SubGrid.opportunity.CustomButton.ToolTip" Command="Mscrm.SubGrid.opportunity.CustomButton" Sequence="10" LabelText="$LocLabels:Mscrm.SubGrid.opportunity.CustomButton.LabelText" Image16by16="/_imgs/Ribbon/Enable_16.png" Image32by32="/_imgs/Ribbon/Enable_32.png" TemplateAlias="o1" />
                    </Controls>
                  </Group>
                </Groups>
              </Tab>
            </CommandUIDefinition>
          </CustomAction>
        </CustomActions>
        <Templates>
          <RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
        </Templates>
        <CommandDefinitions>
          <CommandDefinition Id="Mscrm.SubGrid.opportunity.CustomButton">
            <EnableRules>
              <EnableRule Id="Mscrm.Enabled" />
            </EnableRules>
            <DisplayRules/>
            <Actions>
              <JavaScriptFunction Library="$webresource:crm_accountjscript" FunctionName="RibbonButtonClick" />
            </Actions>
          </CommandDefinition>
          <CommandDefinition Id="Mscrm.SubGrid.opportunity.CustomTab">
            <EnableRules>
              <EnableRule Id="Mscrm.Enabled" />
            </EnableRules>
            <DisplayRules>
              <DisplayRule Id="Mscrm.SubGrid.opportunity.CustomTab.DisplayRule" />
            </DisplayRules>
            <Actions />
          </CommandDefinition>
          <CommandDefinition Id="Mscrm.SubGrid.opportunity.CustomGroup">
            <EnableRules>
              <EnableRule Id="Mscrm.Enabled" />
            </EnableRules>
            <DisplayRules>
              <DisplayRule Id="Mscrm.SubGrid.opportunity.CustomGroup.DisplayRule" />
            </DisplayRules>
            <Actions />
          </CommandDefinition>
        </CommandDefinitions>
        <RuleDefinitions>
          <TabDisplayRules>
            <TabDisplayRule TabCommand="Mscrm.SubGrid.opportunity.CustomTab">
              <EntityRule EntityName="opportunity" Context="SubGridStandard" />
              <EntityRule EntityName="opportunity" Context="SubGridAssociated" />
            </TabDisplayRule>
          </TabDisplayRules>
          <DisplayRules>
            <DisplayRule Id="Mscrm.SubGrid.opportunity.CustomTab.DisplayRule">
              <CrmClientTypeRule Type="Web" />
            </DisplayRule>
            <DisplayRule Id="Mscrm.SubGrid.opportunity.CustomGroup.DisplayRule">
              <CrmClientTypeRule Type="Web" />
            </DisplayRule>
          </DisplayRules>
          <EnableRules>
            <EnableRule Id="Mscrm.Enabled">
              <CrmClientTypeRule Type="Web" />
            </EnableRule>
          </EnableRules>
        </RuleDefinitions>
        <LocLabels>
          <LocLabel Id="Mscrm.SubGrid.opportunity.CustomButton.LabelText">
            <Titles>
              <Title languagecode="1033" description="Custom Button" />
            </Titles>
          </LocLabel>
          <LocLabel Id="Mscrm.SubGrid.opportunity.CustomButton.ToolTip">
            <Titles>
              <Title languagecode="1033" description="Custom Button" />
            </Titles>
          </LocLabel>
          <LocLabel Id="Mscrm.SubGrid.opportunity.CustomGroup.Title">
            <Titles>
              <Title languagecode="1033" description="Custom Group" />
            </Titles>
          </LocLabel>
          <LocLabel Id="Mscrm.SubGrid.opportunity.CustomTab.Title">
            <Titles>
              <Title languagecode="1033" description="Custom Tab" />
            </Titles>
          </LocLabel>
        </LocLabels>
      </RibbonDiffXml>

After replacing the code save the file and import the solution into the CRM.

Make sure the jscript file and function name exists in your webresource. Below is my jscript function

function RibbonButtonClick() {
    alert("Ribbon click fired");
}

Adding a Ribbon Button in Custom Group to SubGrid in CRM 2011

<RibbonDiffXml>
        <CustomActions>
          <CustomAction Id="Mscrm.Isv.opportunity.subgrid.MaxSizeCustomAction" Location="Mscrm.SubGrid.opportunity.MainTab.Scaling._children" Sequence="110">
            <CommandUIDefinition>
              <MaxSize Id="Mscrm.Isv.opportunity.subgrid.MaxSize" GroupId="Mscrm.Isv.opportunity.subgrid.Group0" Sequence="21" Size="LargeLarge" />
            </CommandUIDefinition>
          </CustomAction>
          <CustomAction Id="Mscrm.Isv.opportunity.subgrid.ScaleSizeCustomAction" Location="Mscrm.SubGrid.opportunity.MainTab.Scaling._children" Sequence="130">
            <CommandUIDefinition>
              <Scale Id="Mscrm.Isv.opportunity.subgrid.Popup.1" GroupId="Mscrm.Isv.opportunity.subgrid.Group0" Sequence="85" Size="Popup" />
            </CommandUIDefinition>
          </CustomAction>
          <CustomAction Id="Mscrm.Isv.opportunity.subgrid.SubgridCustomAction" Location="Mscrm.SubGrid.opportunity.MainTab.Groups._children" Sequence="300">
            <CommandUIDefinition>
              <Group Id="Mscrm.Isv.opportunity.subgrid.Group0" Sequence="600" Command="Mscrm.Isv.opportunity.subgrid.Group0" Title="$LocLabels:Mscrm.Isv.opportunity.CustomGroup.LabelText" Template="Mscrm.Templates.Flexible2">
                <Controls Id="Mscrm.Isv.opportunity.subgrid.Controls">
                  <Button Id="Mscrm.Isv.opportunity.form.CustomButton.Button"
                              Command="Mscrm.Isv.opportunity.form.CustomButton.Command"
                              LabelText="$LocLabels:Mscrm.Isv.opportunity.CustomButton.LabelText"
                              ToolTipTitle="$LocLabels:Mscrm.Isv.opportunity.CustomButton.LabelText"
                              ToolTipDescription="$LocLabels:Mscrm.Isv.opportunity.CustomButton.ToolTip"
                              TemplateAlias="o1"
                              Image16by16="/_imgs/Ribbon/Enable_16.png"
                              Image32by32="/_imgs/Ribbon/Enable_32.png" />
                </Controls>
              </Group>
            </CommandUIDefinition>
          </CustomAction>
        </CustomActions>
        <Templates>
          <RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
        </Templates>
        <CommandDefinitions>
          <CommandDefinition Id="Mscrm.Isv.opportunity.subgrid.Command">
            <EnableRules />
            <DisplayRules>
              <DisplayRule Id="Mscrm.Isv.opportunity.subgrid.Notify.DisplayRule" />
            </DisplayRules>
            <Actions />
          </CommandDefinition>
          <CommandDefinition Id="Mscrm.Isv.opportunity.subgrid.Group0">
            <EnableRules />
            <DisplayRules>
              <DisplayRule Id="Mscrm.Isv.opportunity.subgrid.Notify.DisplayRule" />
            </DisplayRules>
            <Actions />
          </CommandDefinition>
          <CommandDefinition Id="Mscrm.Isv.opportunity.form.CustomButton.Command">
            <EnableRules />
            <DisplayRules />
            <Actions>
              <JavaScriptFunction Library="$webresource:crm_accountjscript" FunctionName="RibbonButtonClick" />
            </Actions>
          </CommandDefinition>
        </CommandDefinitions>
        <RuleDefinitions>
          <TabDisplayRules />
          <DisplayRules>
            <DisplayRule Id="Mscrm.Isv.opportunity.subgrid.Notify.DisplayRule">
              <CrmClientTypeRule Type="Web" />
            </DisplayRule>
          </DisplayRules>
          <EnableRules />
        </RuleDefinitions>
        <LocLabels>
          <LocLabel Id="Mscrm.Isv.opportunity.CustomButton.ToolTip">
            <Titles>
              <Title languagecode="1033" description="Custom Button" />
            </Titles>
          </LocLabel>
          <LocLabel Id="Mscrm.Isv.opportunity.CustomButton.LabelText">
            <Titles>
              <Title languagecode="1033" description="Custom Button" />
            </Titles>
          </LocLabel>
          <LocLabel Id="Mscrm.Isv.opportunity.CustomGroup.LabelText">
            <Titles>
              <Title languagecode="1033" description="Custom Group" />
            </Titles>
          </LocLabel>
        </LocLabels>
      </RibbonDiffXml>

Click Here for the Post

Thanks

To add a jscript validation for Activate or Deactivate record in CRM, everyone might probably know that we can pass the execution context as the parameter and add the validation code as below:

function OnSave(context) {
    if (context != null && context.getEventArgs() != null) {
        var saveMode = context.getEventArgs().getSaveMode();
        if (saveMode == "5") {
            // Add Deactivate validation code here
            alert("Deactivate Event");
        }
        else if (saveMode == "6") {
            // Add Activate validation code here
            alert("Activate Event");
        }
    }
}

If you observe the above two pictures, the alert message is triggered after the OOB “Confirm Contact Deactivation” message from CRM.

In one of my requirement, i need to add jscript validation before I get the OOB Confirm deactivation message. So to achieve that, first we need to know what function is called on the “Deactivate” ribbon button in CRM.

In sdk folder you can get all the CRM ribbon xml files under sdk\resources\exportedribbonxml location. In my case i need contactribbon xml file. If you open that file using visual studio, we can get the function name of deactivate button in ribbon button Command.

The command name is “Mscrm.Form.Deactivate” and jscript function name is “changeState”

Now create a new solution and add the “contact” entity to edit the ribbon customization.

Export the solution and open the customization xml file. Go to <RibbonDiffXml> and Add the following code under <CommandDefinitions> (Copy the CommandDefinition from the sdk).

Replace the function name and Library as shown above with your own file and import the solution into CRM and publish it.

Before importing the solution make sure the function and library already exists.

You can add your business required validation and call the “changeState” function to trigger OOB Deactivate record. Below is the jscript code :

function DeactivateContact(btnType, typeCode, value) {
    if (Xrm.Page.getAttribute("parentcustomerid").getValue() == null) {
        alert("Please add parent customer");
        return false;
    }
    else {
        changeState(btnType, typeCode, value);
    }
}

From now your custom validation logic will trigger first and then you can call the OOB Deactivate function method (changeState) to show the confirm message as below.

In the same way you can add the custom logic for “Activate” or for any other ribbon button by copying the CommandDefinition from sdk