Wednesday, January 21, 2009

CRM 4.0 Creating Interactive Plug-ins




The Plug-ins model is a truly robust mechanism which makes it very easy to enforce business rules and control the way your organization data flows.
However, this model does have a few limitations. One in specific which I find very annoying is the fact you can’t return interactive html to the user and must be satisfied with static textual information. I truly believe ms should consider changing this behavior in their next version.

In order to overcome this limitation I made a small yet unsupported modification to the error dialog page that pops up when you throw an
Invalid plug-in exception. The dialog resides in inside /CRMWeb/_common/error/dlg_error.aspx

In order to make the dialog support HTML messages I added the following function that transforms the ErrorMessage Text to HTML and also added an onload event in the html body tag.


function unsupported()
{
var ErrorMessage = document.getElementById("ErrorMessage");
ErrorMessage.innerHTML = ErrorMessage.innerText;
}



<body onload="unsupported()">


To test this “theory” I created a simple duplicate detection check on the contact entity first and last name.
If a duplicate is found, I construct an HTML error message with a link to the duplicate record and
pass it as an InvalidPluginExecutionException parameter.

To test this out simply register the plug-in on the contact pre create / update event.



using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk.Query;

namespace TestPlugin
{
public class DuplicateHandler : IPlugin
{
#region IPlugin Members

public void Execute(IPluginExecutionContext context)
{
if (context.InputParameters.Properties.Contains(ParameterName.Target))
{
#region Check Duplicate Contact [FirstName LastName]

DynamicEntity contact = context.InputParameters.Properties[ParameterName.Target] as DynamicEntity;
String firstName = String.Empty;
String lastName = String.Empty;

if (contact.Properties.Contains("firstname"))
{
firstName = contact.Properties["firstname"].ToString();
}

if (contact.Properties.Contains("lastname"))
{
lastName = contact.Properties["lastname"].ToString();
}


QueryExpression query = new QueryExpression(context.PrimaryEntityName);
query.ColumnSet.AddColumn(context.PrimaryEntityName + "id");

ConditionExpression firstCondition = new ConditionExpression();
firstCondition.AttributeName = "firstname";
firstCondition.Operator = ConditionOperator.Equal;
firstCondition.Values = new object[] { firstName };

ConditionExpression lastCondition = new ConditionExpression();
lastCondition.AttributeName = "lastname";
lastCondition.Operator = ConditionOperator.Equal;
lastCondition.Values = new object[] { lastName };

FilterExpression whereFilter = new FilterExpression();
whereFilter.Conditions.Add(firstCondition);
whereFilter.Conditions.Add(lastCondition);
whereFilter.FilterOperator = LogicalOperator.And;

query.Criteria = whereFilter;

ICrmService Service = context.CreateCrmService(true);
BusinessEntityCollection resultCollection =
(BusinessEntityCollection)Service.RetrieveMultiple(query);

#endregion

#region Retrieve Duplicate HTML

if (resultCollection.BusinessEntities.Count > 0)
{
contact dupContact = resultCollection.BusinessEntities[0] as contact;

String linkStyle = "text-decoration:underline;color:blue;";

String contactUrl = String.Format(
"{3} {4}" ,
linkStyle,
context.OrganizationName,
dupContact.contactid.Value.ToString(),
firstName,
lastName
);

String errorMessage = String.Format("Duplicate Contact: {0}",contactUrl);

throw new InvalidPluginExecutionException(errorMessage);

}

#endregion
}

#endregion
}
}
}

14 comments:

Jean-Francois Menard said...

Hi,

Is there a way to do something similar for pre-Assign or pre-SetState messages?

It looks like CRM doesn't use the same page to display the error. Any idea why?

Thank you

Adi Katz said...

Add an alert(location.href) at the top of the /_static/common/scripts/global.js file, throw an InvalidPlugExecutionException and observe the error page location. Navigate to that page and apply the same method presented here.

Anonymous said...

Hi
The error file which gets executed when the exception is thrown from the Plugin is errorHandler.aspx instead of dlg_error.aspx
I have thrown InvalidPluginExecutionException
from the plugin so that the Message is displayed in Dialog Box but its not happening so.
Can you please help me out...

Adi Katz said...

The dialog should be this page: /CRMWeb/_common/error/dlg_error.aspx
Put an alert(); in the page code and see if it fires.

nishantrana said...

Thanks for such an informative article !!!

Saritha said...

Hi, Thanks for the wonderful post. I was wondering if there is any way to redirect to a custom page instead of throwing the InvalidPluginExecutionException.

Thanks,

Adi Katz said...

You have to throw the exception in order to see the dialog and halt the plug-in execution. However, since the dialog is now able to support html you can integrate a script that can either create a dynamic iframe or redirect the page to another location.

Anonymous said...

Hi Adi,

With Reference to your above reply of keeping the alert(); in dlg_error.aspx.

I did so but its not firing. By throwing "InvalidPluginExecutionException", I would like to have the error message thrown in a new pop-up dialog window instead of the same window turning into an Error Message Window.

The path dlg_error.aspx and errorHandler.aspx is /CRMWeb/_common/error/dlg_error.aspx


Please help me out...
Sorry for late follow up.

Thanks for your reply :)

EktAnkur said...

Hi,

Did you get the solution I also need to throw error in dlg_error.aspx.

Thanks for your Post.

Regards,
Ekta

Prathmesh said...

Hi,

Did you get the solution for the above issue. I am facing the same problem while throwing the exception from Plugin.

MetalDestroyer said...

Hi,

It seems MS CRM 4.0 uses 2 differents error pages.
- dlg_error.aspx fire on create/update/delete events (and perhaps more)
- errorHandler.aspx fire on win/lose events from opportunity for example.

dlg_error display a popup while errorHandler override the form display by a blank page with the error message and 2 buttons (retry and cancel).

I don't find any details on how CRM manage to choose one of the following aspx form if IPluginExecutionException fire (SDK and on the web).

Israel Pradeep-Drenched in Blessings of Almighty God said...

how do we register this PlugIn in the PlugIn Registration Tool?

Anonymous said...

Hi,

Can you send me the solution for the above plugin, so that i can play in and around.

Anonymous said...

Is it possible to execute the SQL Stored Procedure from PRE CREATE plugin ? ? Kindly, guide me.