Monday, February 25, 2008

Hiding Views in CRM 4.0 Using Plug-in

There are many new moving parts added to CRM 4.0 and I am learning something new every day from the community, the projects that I am working on and the people around me. What I have learned this week by working with Sean McNellis at Microsoft is to hide Views using a simple CRM 4.0 plug-in.

As many of you may already know that Microsoft took off the capabilities of sharing views which many people used in CRM 3.0 to hide unwanted System Views from the user's view list. There have been many posts in the CRM newsgroup community on how to hide the views.

My colleague, Jeremy Hofmann found a clever way of hiding the views by updating a bit field in SavedQueryBase table against the MSCRM database. Unfortunately manipulating with the MSCRM database is not encouraged and also not supported by Microsoft. So what can we do?

What I have learned is that we can manipulate with the SavedQuery entity through a simple plug-in to control what shows up on the user's view drop down list. There are many neat things that you can do through the savedquery class offered by the SDK, you can read about it in the 4.0 SDK.

In this post, I will show you how to hide some Contact System views by writing a simple plug-in. I will intercept the retrieve operation that CRM used for getting the views and then append more condition logics to the its query expression.

Before:

ScreenHunter_01 Feb. 25 22.10

C# Code:

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

namespace CrmAddon.Crm.Plugin
{
public class HideContactViewsPlugin : IPlugin
{
public void Execute(IPluginExecutionContext context)
{


            // Query the SavedQueryBase table to retrieve the Query Id
Guid[] Views = new Guid[] {
new Guid("9818766E-7172-4D59-9279-013835C3DECD"), //NA-Contacts: No Orders in Last 6 Months
new Guid("9C241A33-CA0B-4E50-AE92-DB780D5B2A12"), //NA-Contacts: Responded to Campaigns in Last 6 Months
};

if (context.InputParameters != null && gHiddenViews.Length > 0)
{
if (context.InputParameters.Properties.Contains(ParameterName.Query))
{
QueryExpression qe = (QueryExpression)context.InputParameters.Properties[ParameterName.Query];

//only apply this action if the query is for 'views' or saved queries
if (qe.EntityName == "savedquery")
{
if (qe.Criteria != null)
{
if (qe.Criteria.Conditions != null)
{


                                //Append more condition logic to the default query that's used by CRM. In this case, I filtered on the savedqueryid to exclude the views from the Ids identified above.


ConditionExpression queryCondition = new ConditionExpression("savedqueryid", ConditionOperator.NotIn, Views);
qe.Criteria.Conditions.Add(queryCondition);
context.InputParameters.Properties[ParameterName.Query] = qe;
}
}
}
}
}
}
}
}


Plug-in Registration:


Message: RetrieveMultiple
Primary Entity: savedquery
Stage of Execution: Pre Stage
Execution Mode: Synchronous


Sample Registration Snapshot:


...


<Step


    CustomConfiguration = ""


    Description = "Hide Contact View Plug-in"


    FilteringAttributes = ""


    ImpersonatingUserId = ""


    InvocationSource = "0"


    MessageName = "RetrieveMultiple"


    Mode = "0"


    PluginTypeFriendlyName = "Hide Contact Views"


   PluginTypeName = "CrmAddon.Crm.Plugin.HideContactViewsPlugin"


    PrimaryEntityName = "savedquery"


    SecondaryEntityName = ""


    Stage = "10"


    SupportedDeployment = "0" >


</Step>

...



After:



image



As you can see that the "Contacts: No Orders in Last 6 Months" and "Contacts: Responded to Campaigns in Last 6 Months" views are removed from the drop down list. The new CRM 4.0 SDK is really powerful and you can definitely do more with it. E.g. hide/show the view based on the user id or team, etc...



I hope this will help you on your next CRM project. :)

10 comments:

Unknown said...

Hi Darren,



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


namespace CrmAddon.Crm.Plugin
{
public class HideContactViewsPlugin : IPlugin
{
public void Execute(IPluginExecutionContext context)
{


// Query the SavedQueryBase table to retrieve the Query Id
//NA-Contacts: No Orders in Last 6 Months
//NA-Contacts: Responded to Campaigns in Last 6 Months
Guid[] Views = new Guid[] { new Guid("9818766E-7172-4D59-9279-013835C3DECD"), new Guid("9C241A33-CA0B-4E50-AE92-DB780D5B2A12")};


if (context.InputParameters != null && gHiddenViews.Length > 0)
{

if (context.InputParameters.Properties.Contains(ParameterName.Query))
{
QueryExpression qe = (QueryExpression)context.InputParameters.Properties[ParameterName.Query];
//only apply this action if the query is for 'views' or saved queries
if (qe.EntityName == "savedquery")
{
if (qe.Criteria != null)
{
if (qe.Criteria.Conditions != null)
{
//Append more condition logic to the default query that's used by CRM. In this case, I filtered on the savedqueryid to exclude the views from the Ids identified above.
ConditionExpression queryCondition = new ConditionExpression("savedqueryid", ConditionOperator.NotIn, Views);
qe.Criteria.Conditions.Add(queryCondition);
context.InputParameters.Properties[ParameterName.Query] = qe;
}
}
}
}
} // end of last if

} // end of Execute() method
} // end of class
} // end of namespace

Unknown said...

Darren,

Can you explain what are these using statements referencing.

using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Metadata;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.SdkTypeProxy.Metadata;

I assume that,
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;
are referencing the dll files in \sdk\bin folder of crm 4.0 sdk.

But What is lines below referencing?

using Microsoft.Crm.SdkTypeProxy.Metadata;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.Sdk.Metadata;

Unknown said...

Hi Darren,

Can you explain how you obtained these to Guid numbers.

{new Guid("9818766E-7172-4D59-9279-013835C3DECD"), new Guid("9C241A33-CA0B-4E50-AE92-DB780D5B2A12")};

Are these Guids static for every installation of CRM 4.0. Or am I going to see unquie Guids on mY CRM 4.0 server?

Unknown said...

Hi Darren,


What is gHiddenViews object, where does it come from. What class is gHiddenViews from. Where do you instantiate this object. Please Explain. Is it part of IPluginExecutionContext context, which is an input paramter to Execute method.

if (context.InputParameters != null && gHiddenViews.Length > 0)

Thanks,

Darren Liu said...

Hi nitaadmin,

Sorry for the typo, the "gHiddenView" is the "View".

The Guid number is the ID of the views and it's static in all environments for the system views.

These references already included in the dlls in the bin folder.
Microsoft.Crm.SdkTypeProxy.Metadata
Microsoft.Crm.Sdk.Query;
Microsoft.Crm.Sdk.Metadata;

Unknown said...

Hello. Could you please tell me where I would place this code in CRM? Can I just dowload the customiations for the Contact entity on place it in that and then upload it back again? Thanks!

Unknown said...

How to attach the sample configuration file to the plug-in when importing it into CRM using PlugIn Registration Tool? Thanks!

Unknown said...

Hi Darren,

Thanks for the useful information.

Could you pls let me know whether can we set different default views to different users based on their role thru plugin?

Thanks in advance.

Chris Condron said...

A quick update to allow the displayed views to be controled in the UI or in the plugin registration.

Thanks for the code!
-Chris

public class SavedQuery_PreRetrieveMultiple : IPlugin

{
public string UnsecureConfiguration { get; private set; }
public string SecureConfiguration { get; private set; }

public SavedQuery_PreRetrieveMultiple(string UnsecureConfiguration, string SecureConfiguration)
{
this.SecureConfiguration = SecureConfiguration;
this.UnsecureConfiguration = UnsecureConfiguration;
}
public void Execute(IPluginExecutionContext context)
{
if (context.InputParameters != null)
{
if (context.InputParameters.Properties.Contains(ParameterName.Query))
{
QueryExpression qe = (QueryExpression)context.InputParameters.Properties[ParameterName.Query];
//only apply this action if the query is for 'views' or saved queries
if (qe.EntityName == "savedquery")
{
if (qe.Criteria != null)
{
if (qe.Criteria.Conditions != null)
{
//Append more condition logic to the default query that's used by CRM.
var filter = (UnsecureConfiguration ?? string.Empty).Equals(string.Empty, StringComparison.OrdinalIgnoreCase) ? "%zHidden%" : UnsecureConfiguration;
ConditionExpression queryCondition = new ConditionExpression(){
AttributeName ="name",
Operator = ConditionOperator.NotLike
};
queryCondition.Values = new string[] {filter};
qe.Criteria.Conditions.Add(queryCondition);
context.InputParameters.Properties[ParameterName.Query] = qe;
}
}
}
}
}

}
}

Asafus said...

Dear Chris, how to use/init the Un/SecuredConfiguration members? What is the benefits from it?

Asaf.