Wednesday, May 30, 2012

Why and How to use relative path in CRM2011 JavaScript

As CRM developers, we need to use the paths of the URLs, web services and other web resources in JavaScript code. I am talking about CRM URLs for e.g. we will use the following URLs to work with  REST(OData) organization data service

https://hp7.crm5.dynamics.com/xrmservices/2011/organizationdata.svc/ (CRM Online)
http://15.218.138.225/HPDECSStaging/xrmservices/2011/organizationdata.svc/ (On premise)

The highlighted part of the URLs represents the server and organization name of the CRM.
Instead of using the hard coded server name, we should use some generic function that can give us the server and  the organization name of the CRM. It will help to deploy the same code for different organizations, servers and deployments without making any changes.
Most of the Microsoft samples use Xrm.Page.context.getServerUrl() function to retrieve the server URL.I have written the following function to display message box with  the oData organization data service URL.

function test()
{
alert (Xrm.Page.context.getServerUrl() + "/xrmservices/2011/organizationdata.svc");
}
I executed this function on CRM Online and On-Premise deployments. I used the following URLs to start the CRM in the browser
I received the following results.

On CRMOnline, the function returned the following message.
image
This worked as expected.

On on-premise deployment, It returned the following message.
image
It returned the server name (aun.....23) instead of IP address(http://15.218.138.225/HPDECSStaging). I was using an IP address in the browser and it returned the server name. If I try to use this URL to retrieve any data, I will get an error message “access denied”. For this javascript to work properly, the server name part of URL in the browser has to match the server name part of the URL returned by Xrm.Page.context.getServerUrl() method. Xrm.Page.context.getServerUrl() returns the servername stored in the deployment manager. Have a look at the following screen shot.

image
So, if you are using the ip address or localhost in a browser to start the CRM and you are using Xrm.Page.context.getServerUrl() to generate the URL, then you will always get ”access denied” error in the JavaScript.
To overcome this problem, we should use the relative path as shown below
"/xrmservices/2011/organizationdata.svc"
There is one more catch to it. It will work with CRM Online as organization name is a part of the server URL(https://hp7.crm5.dynamics.com).
But to make it work with on-premise deployment, we need to add organization name in the relative path. something  like this
“/” + orgname + "/xrmservices/2011/organizationdata.svc".
You can read the organization name from the context by using Xrm.Page.context.getOrgUniqueName() method. Now we have two different URLs one for on premise and one online deployments. When we are writing JavaScript libraries, we want them to work with every deployment without making any changes. Here is the solution, use  Xrm.Page.context.prependOrgName() method . It will sort the organization name problem. Here is my new test function.
function test()
{
alert (Xrm.Page.context.prependOrgName("/xrmservices/2011/organizationdata.svc"));
}
The function will return “/xrmservices/2011/organizationdata.svc” for CRMOnline deployment. “orgname/xrmservices/2011/organizationdata.svc for On premise deployment. In short, use relative paths with  Xrm.Page.context.prependOrgName(), when working with the URLs in javascript. It will work with online, on premise and hopefully IFD. I did not test the code on IFD deployment.

Monday, May 14, 2012

How to trigger a plugin for a many to many relationship

The blog will explain “ How to trigger a plugin for many to many (N:N) relationship. For this blog, I have created a new N:N relationship between account and contact entity. I am displaying this relationship just in account entity. The following screen shot is displaying the properties of the relationship.
a1
To trigger a plugin on association of 2 entities in a n:n relationship, we will use associate plugin message. I am using the developer’s toolkit for writing code from last 6 months. The problem is developer’s toolkit does not support “Associate” message. To create a plugin in developer’s toolkit, you need to choose primary entity and to use “Associate” message primary entity and secondary entity has to be “none”. There is a workaround to register  plugin step on “Associate” message. Here are the steps.
  1. Follow the steps in this blog on how to create a plugin using developer’s toolkit.
  2. In the step 12th of above-mentioned blog, replace the function “ExecutePostAccountCreate” with the following code
    protected void ExecutePostAccount(LocalPluginContext localContext)
    {
    
        if (localContext == null)
        {
            throw new ArgumentNullException("localContext");
        }
    
        // TODO: Implement your custom Plug-in business logic.
        // Obtain the execution context from the service provider.
        IPluginExecutionContext context = localContext.PluginExecutionContext;
        IOrganizationService service = localContext.OrganizationService;
        ITracingService tracingService = localContext.TracingService;
    
        try{
        // The InputParameters collection contains all the data passed in the message request.
        if (!context.InputParameters.Contains("Target")) { return; }
    
        EntityReference ef = (EntityReference)context.InputParameters["Target"];
        if (ef.LogicalName != "account") { return; }
    
        Relationship relationship = (Relationship)context.InputParameters["Relationship"];
        if (relationship.SchemaName != "new_account_contact") { return; }
    
        // Get Related Entities 
        EntityReferenceCollection re = (EntityReferenceCollection)context.InputParameters["RelatedEntities"];
    
        foreach (EntityReference rel in re)
        {
    
            Entity relatedEntity = service.Retrieve("contact", rel.Id, new ColumnSet("address1_city"));
            string city = (string)relatedEntity.Attributes["address1_city"];
            if (city=="Sydney")
            {
                //create a task
                Entity task = new Entity("task");
                task["subject"] = "A new contact from sydney is added to the account";
                task["regardingobjectid"] = ef;
                task["description"] = "A new contact from sydney is added to the account";
                // Create the task in Microsoft Dynamics CRM.
                service.Create(task);
    
            }
        }//endfor
        }
        catch (FaultException ex)
        {
            tracingService.Trace(ex.Message.ToString());
            throw new InvalidPluginExecutionException("An error occurred in the plug-in.", ex);
        }
    
    }//end function
    
  3. Add the following reference to your plugin class.
    using Microsoft.Xrm.Sdk.Query;
  4. The above code will create a task, when we add a contact with “address1_city” field equal to “Sydney” to an account. Just remember we are relating a contact to account using N:N relationship.
  5. Now change the RegisterFile.crmregister file  to register on an “Associate” message. Open the file and look for your plugin definition. It will look something like the following code.
    <Plugin Description="Plug-in to PostAccountCreate" FriendlyName="PostAccountCreate" Name="CRM2011PluginSeries.Plugins.PostAccountCreate" Id="420419bd-9fe7-404e-a5e3-afa582f21dd3" TypeName="CRM2011PluginSeries.Plugins.PostAccountCreate">
              <Steps>
                <clear />
                <Step CustomConfiguration="" Name="PostAccountCreate" Description="Post-Operation of Account Create" Id="b248f264-2b3f-e111-ab54-00155d32042e" MessageName="Create" Mode="Synchronous" PrimaryEntityName="account" Rank="1" SecureConfiguration="" Stage="PostOutsideTransaction" SupportedDeployment="ServerOnly">
                  <Images />
                </Step>
              </Steps>
            </Plugin>
  6. Now change the MessageName="Create" to MessageName="Associate" and PrimaryEntityName="account" to PrimaryEntityName="".
  7. Save the changes and deploy the plugin.
  8. If you get any errors during deployment, just deploy the plugin using plugin registeration tool. Don't forget to leave the "PrimaryEntityName" = none.
  9. Please provide some feedback

Saturday, May 12, 2012

Modifying Duplicate Detection View

Someone asked the question on CRM forum  on “ How to modify the duplicate detection view ?” Here is the answer. Duplicate detection view is combination of static and dynamic columns of an entity. Have a look at the following screen shot.

dd1

  1. “Potential duplicate records” have 2 extra columns than the “My new record” column.  These columns are Status and “Modified On”. These  columns are present on every duplicate detection view. You can’t remove them.
  2. Some of the columns in duplicate detection views come from the “Duplicate Detection Rules” set for an entity. System picks up these columns automatically. You can’t these fields either.
  3. Rest of the columns for e.g. “Primary Contact”, “Address1_City” in the the above screen shot can be changed. we can remove them. we can add more columns to the view. Here is the trick. These columns comes from the “Lookup View” of an entity. we can add/remove columns to the view. Have a look at the “Account Lookup View”.

dd2

Good Luck.