Monday, December 14, 2009

Modal Window redraws itself on postback

This is a known issue with IE 7/8, that a window opened using window.showModalDialog redraws itself upon postback.

The solution to this problem is that the following statement be added to the <head> tag of the page:

<base target = "_self" />

This should do the trick.

Error message when you try to log on to the Microsoft Dynamics CRM Web site from the Microsoft Dynamics CRM 4.0 server: "Request IP Address has differ

Recently on a IFD deployment, i faced this issue "Request IP Address has different address family from network address". I got to know from (here) that this is a knows issue.

When you try to log on to the Microsoft Dynamics CRM Web site from the Microsoft Dynamics CRM 4.0 server, you receive the following error message:
Request IP Address has different address family from network address.
This problem occurs if the following conditions are true:
1. You run Microsoft Dynamics CRM 4.0 on a Windows Server 2008-based computer.
2. You use Internet-Facing Deployment (IFD) for Microsoft Dynamics CRM 4.0.

The fix for this is simple as posted here. Open your hosts files (:\windows\system32\drivers\etc\hosts) and add a line for your server name there with an IPv4 address. Reset IIS and this is it.

This solves the problem. :)
I hope this helps!!

Unauthorized 401 status error after deploying custom web application

Very recently while working on custom solution deployment on ms crm 4.0, i encountered a 401 Unauthorized error. The web site was deployed to work On-Premise and was deployed as a new virtual directory within the ISV folder, authentication was set to integrated windows authentication. I recieved the following error though the login credentials used were correct.

Server Error in '/ISV/CustomApplication' Application.
The request failed with HTTP status 401: Unauthorized. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. Exception Details: System.Net.WebException: The request failed with HTTP status 401: Unauthorized.Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below. Stack Trace:
[WebException: The request failed with HTTP status 401: Unauthorized.]
System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall) +551137
System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters) +204
Microsoft.Crm.Metadata.MetadataWebService.GetDataSet() +31
Microsoft.Crm.Metadata.DynamicMetadataCacheLoader.LoadDataSetFromWebService(Guid orgId) +301
Microsoft.Crm.Metadata.DynamicMetadataCacheLoader.LoadCacheFromWebService(LoadMasks masks, Guid organizationId) +40
Microsoft.Crm.Metadata.DynamicMetadataCacheFactory.LoadMetadataCache(LoadMethod method, CacheType type, IOrganizationContext context) +418
Microsoft.Crm.Metadata.MetadataCache.LoadCache(IOrganizationContext context) +324
Microsoft.Crm.Metadata.MetadataCache.GetInstance(IOrganizationContext context) +386
Microsoft.Crm.BusinessEntities.BusinessEntityMoniker..ctor(Guid id, String entityName, Guid organizationId) +115
Microsoft.Crm.Caching.UserDataCacheLoader.LoadCacheData(Guid key, ExecutionContext context) +323
Microsoft.Crm.Caching.ObjectModelCacheLoader`2.LoadCacheData(TKey key, IOrganizationContext context) +389
Microsoft.Crm.Caching.BasicCrmCache`2.CreateEntry(TKey key, IOrganizationContext context) +82
Microsoft.Crm.Caching.BasicCrmCache`2.LookupEntry(TKey key, IOrganizationContext context) +108
Microsoft.Crm.BusinessEntities.SecurityLibrary.GetUserInfoInternal(WindowsIdentity identity, IOrganizationContext context, UserAuth& userInfo) +344
Microsoft.Crm.BusinessEntities.SecurityLibrary.GetCallerAndBusinessGuidsFromThread(WindowsIdentity identity, Guid organizationId) +194
Microsoft.Crm.Authentication.CrmWindowsIdentity..ctor(WindowsIdentity innerIdentity, Boolean publishCrmUser, Guid organizationId) +279
Microsoft.Crm.Authentication.WindowsAuthenticationProvider.Authenticate(HttpApplication application) +605
Microsoft.Crm.Authentication.AuthenticationStep.Authenticate(HttpApplication application) +125
Microsoft.Crm.Authentication.AuthenticationPipeline.Authenticate(HttpApplication application) +66
Microsoft.Crm.Authentication.AuthenticationEngine.Execute(Object sender, EventArgs e) +513
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +92
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +64
Version Information: Microsoft .NET Framework Version:2.0.50727.1433; ASP.NET Version:2.0.50727.1433


The ms crm stack trace showed: LoadDataSetFromWebService() got exception: The request failed with HTTP status 401: Unauthorized.

On searching the web i found out here, that CRM is using an HttpModule for multitenancy. The http module will be called with an anonymous user account and will fail when it's requesting the metadata webservice. So, to make the custom application working, i will need to remove the HttpModule from being loaded. This could be done by making modification the config file of the custom application. Add the remove tags as mentioned below and this would do the trick.

<httpModules>
<remove name ="CrmAuthentication"/>
<remove name ="MapOrg"/>
</httpModules>

This would remove only the specific modules. To remove all the modules you can use a generic clear tag.

<httpModules>
<clear/>
</httpModules>

I hope this helps!!

Custom message on the web page during server side processing

Recently, while working on MS CRM based solution, I encountered a typical aspx page loading issue. I had added a button in ISV config of my custom entity, which would load an aspx page. There was a code block on the page load which would fetch records from the server and display them on the page. The processing time increased enormously since there were a lot many records to retrieve and hence, the page loaded real slow :(. And while the user kept waiting for the page to show up, there was really no intimation to the user that the page processing is going on and the page would finally appear :)

I found a very simple solution to this problem through Meta tag usage on the page. Below is a quick sample showing a custom message on the web page while some processing happens at the server side. Though there were other approaches also for this problem, I found this the simplest and easiest to implement.

Follow the below mentioned steps to use the Meta refresh tag.

1. Add the Meta refresh Tag in the HTML code for the web page in the header tag of the page.

<! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<Title>Copying Event</Title>
<! ----- dynamically filled META REFRESH element ----->
<Meta id="mtaRefresh" runat="server" />
</head>
<Body>
<form id="frmMain" runat="server">
<! ----- "please wait" display ----->
<Center>
<div id="divWait" Visible="false" runat="server"><b>Processing, please wait...</b></div>
</Center>
</form>

</Body>
</html>

Also Notice that a div tag is added in the body section of the page that would display the required “Processing, please wait” message to the user.

2. On the server side code of the page, in Page_load event, add the following code to refresh the URL.

protected void Page_Load(object sender, EventArgs e)
{
if (Request.QueryString["doAction"] == null)
{
string sRefreshURL = Request.Url.ToString();
// use META REFRESH to start loading next page
mtaRefresh.Attributes.Add("http-equiv", "refresh");
mtaRefresh.Attributes.Add("content", "0;url=" + sRefreshURL + "&doAction=1");

divWait.Visible = true;
}
else
{

divWait.Visible = true;
divWait.InnerText = "Done";
}
}


That’s it!! This code block would ensure that the page displays a wait message till the page load event is being processed. Happy Coding!!

Sunday, December 13, 2009

Customizing Workplace

We might be faced with a customer requirement, where it is required that we customize workplace to show say, Type as "Phone Call" instead of "All" and Due as "Today" instead of "All". This customization is possible, although it is unsupported by Microsoft.

The activities page displayed as default view in Workplace can be found at: <install dir>\CRMWeb\Workplace\

Modifying the code as follows does the trick:
function window.onload()
{
// ---------- Add the following code to the OOTB method ----------
// Eg. Set Due = Today as default
crmDateSelector.selectedIndex = 1;

// Eg. Type = Phone Call as default
crmTypeSelector.selectedIndex = 3;
// ---------------------------------------------------------------
}

Please note that above code is unsupported.

Monday, December 7, 2009

Tool to enhance Developer Productivity - XRMLinq

It is designed to increase Developer productivity by reducing the amount of time and effort needed to effectively use Dynamics CRM. Instead of learning the ins and outs of the complex Software Development Kit (SDK) and the complexities of CRM you can use this library, putting CRM into a context that you are already familiar with.

Example:
CRM SDK
QueryExpression q = new QueryExpression();
q.EntityName = "account";
q.ColumnSet = new AllColumns();

q.Criteria = new FilterExpression();
q.Criteria.FilterOperator = LogicalOperator.And;
q.Criteria.AddCondition("name", ConditionOperator.Like, "A%");

q.AddOrder("name", OrderType.Ascending);

BusinessEntityCollection r = sdk.RetrieveMultiple(q);
foreach (account account in r.BusinessEntities)
{
Console.WriteLine(account.name);
}

XrmLinq
CrmDataContext context = new CrmDataContext(sdk);

var accounts = (from a in context.Accounts
where a.Name.StartsWith("A")
orderby a.Name
select a).ToList();

foreach (var account in accounts)
{
Console.WriteLine(account.Name);
}

The tool requires developer license to work. Read about it at:

http://www.xrmlinq.com/

Customization Best Practices

Just came across this list of custmization best practices to follow in the CRM SDK 4.0.10.
Using Custom Entities and Attributes
ISV solutions can save data by using:
• Custom attributes on existing entities
• Custom entities
• A separate database
• Use entity rename to make system entities more meaningful
You should use a unique prefix for custom entities and attributes. For example, use an abbreviation of your company name to avoid conflicts with other ISV add-ins.
Using Isv.Config.xml
Microsoft Dynamics CRM ships with a sample configuration file in the wwwroot_Resources folder. To prepare for installation of ISV add-ins you should rename this file and then create your own configuration file. By doing this you will not confuse the sample UI changes with the UI changes you need for your customization.
ISVs should merge changes into the existing ISV.Config file and not overlay the existing configuration file.
When Do I Add vs. When Do I Customize an Entity?
You should customize a system entity, such as the account entity, rather than replace it because there are many built-in features that you would have to recreate. For example, the account entity has lookup fields to associated customers. You can use the entity rename feature to make the system entity more meaningful to your business.
When Do I Use a Workflow .NET Assembly?
Workflow rules cannot create an instance of an entity. You can use a .NET assembly to do this.
When do I use plug-ins vs. workflow?
As a developer who is interested in extending or customizing Microsoft Dynamics CRM, you have several methods of performing your tasks. In addition to adding client-side JavaScript code to a form, or adding custom ASP.NET page, you can choose to write a plug-in or create a custom workflow by using the Web interface that calls a custom workflow activity. But how do you decide when you should use a plug-in or when you should use a workflow? The technology that you choose depends on the task that you have to perform.
For example, you must use a synchronous plug-in if you need to execute custom code immediately before or after the core platform operation executes and before the result of the operation is returned from the platform. You cannot use a workflow or asynchronous plug-in in this situation because they are queued to execute some time after the core operation executes to completion, and so you are not guaranteed when they will run. On the other hand, if you want to add custom functionality to Microsoft Dynamics CRM Online, only workflows are supported because you cannot add your DLL to the server.
You can read the entire article at:
http://msdn.microsoft.com/en-us/library/dd548494.aspx

The key specified to compute a hash value is expired, only active keys are valid.

Ever accessed Microsoft Dynamics CRM and got the error “The key specified to compute a hash value is expired, only active keys are valid.” The error logged in the trace is as follows:

MSCRM Error Report:
--------------------------------------------------------------------------------------------------------
Error: Exception of type 'System.Web.HttpUnhandledException' was thrown.

Error Number: 0x8004A106

Error Message: The key specified to compute a hash value is expired, only active keys are valid. Expired Key : CrmKey(Id:d0879dd3-7f9d-de11-a5c4-0003ff392bd3, ScaleGroupId:00000000-0000-0000-0000-000000000000, KeyType:CrmWRPCTokenKey, Expired:True, ValidOn:09/09/2009 20:32:01, ExpiresOn:10/12/2009 20:31:55, CreatedOn:09/09/2009 20:32:01, CreatedBy:NT AUTHORITY\NETWORK SERVICE.

Error Details: The key specified to compute a hash value is expired, only active keys are valid. Expired Key : CrmKey(Id:d0879dd3-7f9d-de11-a5c4-0003ff392bd3, ScaleGroupId:00000000-0000-0000-0000-000000000000, KeyType:CrmWRPCTokenKey, Expired:True, ValidOn:09/09/2009 20:32:01, ExpiresOn:10/12/2009 20:31:55, CreatedOn:09/09/2009 20:32:01, CreatedBy:NT AUTHORITY\NETWORK SERVICE.

Source File: Not available

Line Number: Not available

Request URL: http://localhost/Contoso/loader.aspx

Stack Trace Info: [CrmException: The key specified to compute a hash value is expired, only active keys are valid. Expired Key : CrmKey(Id:d0879dd3-7f9d-de11-a5c4-0003ff392bd3, ScaleGroupId:00000000-0000-0000-0000-000000000000, KeyType:CrmWRPCTokenKey, Expired:True, ValidOn:09/09/2009 20:32:01, ExpiresOn:10/12/2009 20:31:55, CreatedOn:09/09/2009 20:32:01, CreatedBy:NT AUTHORITY\NETWORK SERVICE.]
at Microsoft.Crm.CrmKeyService.ComputeHash(CrmKey key, Guid scaleGroupId, HashParameterBase[] parameters)
at Microsoft.Crm.CrmKeyService.ComputeHash(CrmKey key, HashParameterBase[] parameters)
at Microsoft.Crm.Application.Security.WRPCContext..ctor()
at Microsoft.Crm.Application.Controls.AppPage.ValidateWrpcContext()
at Microsoft.Crm.Application.Controls.AppPage.OnInit(EventArgs e)
at System.Web.UI.Control.InitRecursive(Control namingContainer)
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

[HttpUnhandledException: Exception of type 'System.Web.HttpUnhandledException' was thrown.]
at System.Web.UI.Page.HandleError(Exception e)
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.ProcessRequest()
at System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context)
at System.Web.UI.Page.ProcessRequest(HttpContext context)
at ASP.contoso_loader_aspx.ProcessRequest(HttpContext context)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)


Simply restart the asynchronous service and reset IIS. This should make it work!!!

Sunday, December 6, 2009

Unable to publish workflows

After installing Rollup 7 on my MS CRM server, I could not publish any of the workflows. The error in the trace files read “Type System.Globalization.CultureInfo is not marked as authorized in the application configuration file.”
Add the following entries to Microsoft Dynamics CRM web config did the trick to make it work&#61514;

<authorizedType Assembly="mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" Namespace="System" TypeName="Void" Authorized="True"/>
<authorizedType Assembly="mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" Namespace="System.Reflection" TypeName="AssemblyFileVersionAttribute" Authorized="True"/>
<authorizedType Assembly="mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" Namespace="System.Reflection" TypeName="AssemblyVersionAttribute" Authorized="True"/>
<!--
* CRM SE 5934 V4: Workflow Designer and Runtime Do Not Handle Decimal Numbers Correctly
-->
<authorizedType Assembly="mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" Namespace="System.Globalization" TypeName="CultureInfo" Authorized="True"/>

Here’s the link where I got this from:
http://social.microsoft.com/Forums/en/crmdevelopment/thread/e0a8b1f3-5bbf-4040-ab5c-2a67df362b5b

Field-Level Security in Microsoft Dynamics CRM

Recently I came across the blog article Field-Level Security in Microsoft Dynamics CRM : Options and Constraints (at http://blogs.msdn.com/crm/archive/2009/11/16/field-level-security-in-microsoft-dynamics-crm-options-and-constraints.aspx) which talks about restricting attribute level access to certain users based on the different access channels for users to Microsoft Dynamics CRM.

On the client side, one of these ways can be used –
a. Passing an indicator to the field to display to the user that they user cannot view the attribute e.g. ‘******’, ‘Restricted’, ‘Not Authorized’.
b. Making fields read only or hidden if a user is not authorized to view them.

Server Side Restriction –Two strategies are basically proposed:

1. Role based filtering - If the user does not have a role that is entitled to access the attribute then it will be restricted.
An advantage of role based filtering (and a reason for its simplicity) is that the access to an entity’s attributes can be defined without needing to access the data in the entity’s records, and that the definition will apply uniformly across the instances of an entity for a particular user.


2. Rule based Filtering- Building extra rules into the query that is used to request information from the CRM platform. Alternatively, it may be necessary to filter after retrieval, though this results in the additional performance overhead associated with having retrieved data that will be filtered out before information is displayed to the user.


Consider a scenario in which business rules require that all records with a customer turnover:
• Greater than or equal to £1M have the profit field restricted
• Less than £1M have the profit field displayed

Because the platform cannot return the profit for one subset of the customers but hide it for the others, it is necessary to query the platform to return the profit for all customer records and then to filter out the values of the returned records set afterwards before returning it for display at the client.

Following is a brief explanation of the recommended mechanisms for restricting attribute level access in case of server-side access to entities:

1. Plug-ins and CRM SDK web service: Scenarios that require restricted access to data must account for the different ways that forms are used (Create, Retrieval, and Update).

During the creation of a record, for example, it is important to prevent accidental or malicious entry of unauthorized data. This can be accomplished by implementing the appropriate logic within a Pre-Create Plug-In. To prevent a particular user from setting specific attributes, remove those attributes from the InputParameters collection.


DynamicEntity updateValues = (DynamicEntity)context.InputParameters.Properties["Target"];
If( ([USER NOT AUTHORISED] ) && updateValues.Properties.Contains[“ATTRIBUTE_NAME”])
{
updateValues.Properties.Remove[“ATTRIBUTE_NAME”]);
}

During Retrieval, it is important to hide data from unauthorized users. This can be accomplished by using either a Pre-Retrieve or Post-Retrieve Plug-In. It is also necessary to provide a Pre-Update Plug-In to prevent unauthorized users from updating record fields that those users are not authorized to view or to update

2. Access via Views/Advanced Find - The underlying queries that these user interface elements use to retrieve data are sent to the platform via one of two types of messages, either by FetchXML requests sent through the Execute message or by RetrieveMultiple messages. Both types of messages must be filtered to ensure data security.

3. CRM Metadata web service – If the metadata service is used, it is not possible to hide the fact that information of a certain type is captured for a particular entity, and therefore other mechanisms should be used so as to hide the actual data captured against that attribute.

4. Export-to-Excel feature ensures that users can only export the data to which they have access. After enabling field-level security by using the plug-in approach, it is important to consider the Export-to-Excel capability to prevent data restricted through the UI from being accessed in this way.
Applying field-level security is more problematic with dynamic worksheets however, because the data is retrieved directly by the Excel worksheet at the client from the database by using the filtered views that the CRM product creates and exposes. There is no supported way to intercept or alter the data returned by the CRM provided Filtered Views. As a result, the best option available is to prevent direct access to the database Filtered Views altogether by the user from the client

5. To secure data that can be accessed by using reports, available options include:
• Disabling Reports access for roles that require field-level security.
• Disabling report creation for roles that require field-level security. Provide pre-defined custom reports for those users.

6. To secure direct access to Database Filtered Views, available options include:
• Denying user membership to the CRM Reporting Group.

7. Access via Outlook Synchronization and the Offline Client - If any of the data shared with Outlook (i.e. contacts, appointments, tasks) or the offline client needs to be limited at a field level for particular users, then those users must not be able to use the CRM Outlook add-in.