How to disable and enable fields in CRM 4.0
If you already worked with Microsoft Dyamics CRM 4.0, you know that almost everything you do with JavaScript is considered a "hack" because you manipulate the DOM of the pages by yourself. Because of this I decided to give you some help with differents helper methods I developped at my day job.
I'm currently working on a big development project involving CRM.
Disable a field in CRM 4.0
function DisableField(id)
{
var lookup = id.indexOf('_ledit') > 0;
var objField = lookup ? crmForm.all[id.replace('_ledit', '')] : crmForm.all[id];
if (objField.tagName.toUpperCase() == "INPUT" && objField.type != "radio")
objField.disabled = true;
else if (objField.tagName.toUpperCase() == "INPUT" && objField.type != "checkbox")
objField.disabled = true;
else if (objField.tagName.toUpperCase() == "INPUT" && objField.type != "text")
objField.readOnly = true;
else if (objField.tagName.toUpperCase() == "SELECT")
objField.disabled = true;
else if (objField.tagName.toUpperCase() == "TEXTAREA")
objField.readOnly = true;
else
objField.Disabled = true;
if ((objField.tagName.toUpperCase() == "INPUT" && objField.type != "radio" && objField.type != "checkbox") || objField.tagName.toUpperCase() == "SELECT" || objField.tagName.toUpperCase() == "TEXTAREA")
objField.style.backgroundColor = "#DDDDDD";
}
Enable a field in CRM 4.0
function EnableField(id)
{
var lookup = id.indexOf('_ledit') > 0;
var objField = lookup ? crmForm.all[id.replace('_ledit', '')] : crmForm.all[id];
if (objField.tagName.toUpperCase() == "INPUT" && objField.type != "radio")
objField.disabled = false;
else if (objField.tagName.toUpperCase() == "INPUT" && objField.type != "checkbox")
objField.disabled = false;
else if (objField.tagName.toUpperCase() == "INPUT" && objField.type != "text")
objField.readOnly = false;
else if (objField.tagName.toUpperCase() == "SELECT")
objField.disabled = false;
else if (objField.tagName.toUpperCase() == "TEXTAREA")
objField.readOnly = false;
else
objField.Disabled = false;
if ((objField.tagName.toUpperCase() == "INPUT" && objField.type != "radio" && objField.type != "checkbox") || objField.tagName.toUpperCase() == "SELECT" || objField.tagName.toUpperCase() == "TEXTAREA")
objField.style.backgroundColor = "";
}
How to hide and display fields in CRM 4.0
If you already worked with Microsoft Dyamics CRM 4.0, you know that almost everything you do with JavaScript is considered a "hack" because you manipulate the DOM of the pages by yourself. Because of this I decided to give you some help with differents helper methods I developped at my day job.
I'm currently working on a big development project involving CRM.
How to hide a field in CRM 4.0
Here's a secure method that will correctly hide a field (lookups are a bit tricky).
function HideField(id)
{
var objField = crmForm.all[id];
var lookup = id.indexOf('_ledit') > 0 || objField.tagName.toUpperCase() == "IMG";
var objFieldLookup = objField.tagName.toUpperCase() == "IMG" ? crmForm.all[id + '_d'] : crmForm.all[id.replace('_ledit', '_d')];
objField.style.visibility = 'hidden';
if (lookup)
objFieldLookup.style.visibility = 'hidden';
}
How to show a field in CRM 4.0
Here's a secure method that will correctly show a field
function ShowField(id)
{
var objField = crmForm.all[id];
var lookup = id.indexOf('_ledit') > 0 || objField.tagName.toUpperCase() == "IMG";
var objFieldLookup = objField.tagName.toUpperCase() == "IMG" ? crmForm.all[id + '_d'] : crmForm.all[id.replace('_ledit', '_d')];
objField.style.visibility = 'visible';
if (lookup)
objFieldLookup.style.visibility = 'visible';
}
CRM read-only fields don't get saved in the database
If you have some fields that are only informative fields that you populate yourself by JavaScript, normally you will set them to be readonly because you don't want your users to be able to edit the value. If you do so, your value will no be saved in the database. The main reason for this is because when you post a form in an HTML page with disabled fields, the disabled fields do not get posted. If you want their values to be posted, you need to enable them.
CRM offers you the ability to save the values of the disabled fields. There is a boolean property named ForceSubmit on each field of the form. You can set this property to true if you want its value to be saved.
crmForm.all.fieldname.ForceSubmit = true;
How to know if a user is in a specified role in Microsoft Dynamics CRM 4.0 (JavaScript)
In CRM 4.0, when you want to know if the currently logged in user is in a certain security role with JavaScript, you need
to issue a request to the server. Yes, this is the officially supported way to do this, but i'll explain to you a good solution
to avoid doing a lot of code to call the server with AJAX.
The solution I've developped is a lot more performant because you don't need to call (AJAXly) the server, but it is officially
unsupported because you have to inect some code in the CRM pages at runtime.
HttpModules...
For a lot of things in CRM we've used HttpModules to inject some JavaScript code onto the CRM pages (in the <head> tag).
In your HttpModule you simply have to call the CrmService web reference to get all the roles of the currently logged in user.
After you get this, you build a string that correspond to a JavaScript Array of "typed" objects.
Yeah, in JavaScript everything is untyped; you all declare variables with the "var" keyword. But, you can build custom objects
and set your own properties on it.
Lets say you do this :
var objRole = new Object();
objRole.GUID = [GUID of the CRM security role];
objRole.Name = [Name of the CRM security role];
You get an object representing your security role with all the informations you need.
So, you have to build (with a StringBuilder) the JavaScript string that will outputed to the CRM page at runtime.
Let's do something like this in code :
StringBuilder strJS = new StringBuilder();
strJS.AppendLine("var SECURITY_ROLES = new Array();");
foreach (CrmService.role objRole in securityRoles)
{
strJS.AppendLine("SECURITY_ROLES[SECURITY_ROLES.length] = new Object();");
strJS.AppendLine(String.Format("SECURITY_ROLES[SECURITY_ROLES.length].Name = '{0}';", objRole.name));
strJS.AppendLine(String.Format("SECURITY_ROLES[SECURITY_ROLES.length].GUID = '{0}';", objRole.roleid));
}
string javaScriptString = strJS.ToString();
Then, you just inject thoses lines of JS code : [HeadTag].Append(javaScriptString);
The [HeadTag] is a class that we developped to encapsulate the stream property named "Filter" of the HttpResponse object, accessible
on the HttpModule. This property contains the HTML to is about to be sent to the client so we use this to inject our JavaScript code.
With this solution, in each page in CRM you have a JavaScript array called SECURITY_ROLES containing all the roles of the current user.
So if you want to check if the user is part of a group, you can check that array instead of issuing a lot of AJAX request to the server at runtime.
How to get the username client-side in Microsoft Dynamics CRM 4.0
In CRM 4.0, when you want to know to NT username of the currently logged in user with JavaScript, you need
to issue a request to the server. This sounds (and IS) ridiculous and also no performant.
The solution I've developped is a lot more performant because you don't need to call (AJAXly) the server
to get the username of the current user.
HttpModules...
For a lot of things in CRM we've used HttpModules to inject some JavaScript code onto the CRM pages (in the <head> tag).
For the currently logged in user we just inject this line of .Net code : [HeadTag].AppendFormat("var USERNAME='{0}';", My.User.Name);
The [HeadTag] is a class that we developped to encapsulate the stream property named "Filter" of the HttpResponse object, accessible
on the HttpModule. This property contains the HTML to is about to be sent to the client so we use this to inject our JavaScript code.
With this solution, in each page in CRM you have a JavaScript variable called USERNAME that you can use to have the username !
You can also do the same thing to have the current computer name.
Tips and tricks when using Microsoft Dynamics CRM 4.0
When you use Microsoft Dynamics CRM 4.0 in a development project, you have to think about a lot of things that you don't
think of when doing normal .Net development. Here are some random tips and tricks to help you success in you CRM development :
1. Don't write you JavaScript code directly on the CRM. Write it on .js files.
When you write JavaScript in the ISV.Config file or on the OnSave, OnLoad and OnChange methods, the CRM starts to behave abnormaly.
Half of the time the JavaScript will simply just not be brought to the client-side.
Write .js files that you will include on your aspx pages with a HttpModule to inject script include tags at runtime inside the <head> tag.
2. Write a lot of helper classes (JavaScript)
Because barely everything you do by JavaScript in the CRM is considered a hack, the minimum you can do if you absolutely want to
use this anyway is to encapsulate that hack in some clean methods that you developpers will call. By doing this you simplify the
life of all you developpers and you have centralized methods that can be easily modified if the DOM changes (by a CRM fix or patch).
3. Never delete your entities
If you do so, and then you reimport the entity with the XML customization file, you will get some fatal errors when accessing that entity.
4. Never delete the built-in security roles
If you delete them, you will not be able to add new entities... yes the CRM must have something hard-coded in it !
5. Always use the CrmService web reference that ships with the SDK.
Never reference a custom CrmService. Also, have a common class
that can give you the CrmService web reference after setting the CrmAuthenticationToken, the URL and the credentials.
If you use your own CrmService reference you will certainly have casting problems if in some case you use the GetCrmService() method
in plug-ins. By always using the SDK CrmService web reference you will never have problems. Also, the web reference is huge (about 3MB),
so you don't want a lot of DLLs to be that size.
Creating a framework for Microsoft Dynamics CRM 4.0 (client-side JS and server-side .Net)
Recently I had to create a framework for using (programming with) CRM. The first reason is
that when you want to do some tricky stuff in JavaScript, barely everything is considered a "hack"
in CRM, because you have to manipulate the DOM (Document Object Model) of the CRM pages.
For example, if you want to hide a field, you have to do a crmForm.field_id.style.display = 'none'; This
is officially unsupported but widely used. On the other hand, if you want to disable a field, you can
do crmForm.field_id.Disabled = true; This is supported because the "Disabled" property is CRM proprietary.
The other reason we wanted to create a framework is to create some base classes to encapsulate some functionnalities
and to enforce some development rules. For example, we decided to create 2 base classes for plug-in development.
Synchronous plug-in base class
Because we use synchronous plug-ins to do some server-side validations (before the create/update/delete happens) and
set some values on the DynamicEntity, we decided to create to abstract methods: Validate and ExecutePlugIn. The
Execute function of the plug-in is our real entry point, but the developper does not have the right to touch
this method; he has to override Validate to do his pre-validations and then return True or False wether the validation
passed or failed.
In addition to this, the Validate function is used to add functionnal messages to help the user
understand why the validations failed (if this is the case). If the Validate function returns False,
the real Execute method of the plug-in throws an InvalidPlugInExecution exception with all the messages
concatenated and separated with a distinct sign (like a big dot, a semicolon, an incremental number, etc...).
If the Validate function returns True, the next step in the Execute method is to call the ExecutePlugIn method that will do
the real job (if there is something to execute, because sometimes the plug-in is only there to do validations).
To make sure everything is trapped and logged, the ExecutePlugIn method is surrounded with a try catch. Try Catch's only function
is to log to the event viewer all the details of the exception and informations of the plug-in context and then rethrow the exception.
Asynchronous plug-in base class
Our asynchronous plug-ins are much more simpler than our synchronous plug-ins. The Validate function does not exist; there is only a
ExecutePlugIn method, and that method is surrounded with a try catch that catches all kind of exceptions and log them to the event viewer.
It does not rethrow the exception because when an asynchronous plug-in fails it cannot stop the CRM execution and display a message
to the user so the only thing we have to do is to log the error to the event viewer for further investigation.
HttpModule to manage the visibility of fields in entity forms
Because we wanted to provide a simple way for our developpers to enabled/disable/hide/display fields individualy on every entity form for each
display mode (create/update/bulk edit/disabled/read-only), we created an HttpModule that is supported by a .config file in which we defined
a custom configuration section (which defines every rule for every field). All our rules are based on the CRM security roles.
We choosed an HttpModule because we wanted a way to inject JavaScript code on every CRM page at runtime. We decided that this was a
safer solution than editing CRM ASPX pages. An HttpModule only involves that you modify the web.config file.
HttpModule to include JavaScript files in entity forms
For JavaScript development we decided to write every line of JS in .js files, not directly in the CRM. This has been decided for 2 major reasons.
First, I discovered that when you write a lot of JS code in CRM entities OnLoad and OnSave methods (as well as field's OnChange method) CRM
sonly brings your JavaScript to the client side half of the time. We all know that client-side scripting is weak, but normaly it works more
than half of the time. When we write some .js files and include them on a page, the browser always load them easily so we decided that all JS
code will be placed on .js files. Thoses files are splited so we have between 5 to 20 js files per entity.
Then, to include those .js files with <script src="scripts.js"></script> tags we use an HttpModule to inject those lines of HTML dynamically on each entity
page that needs to have those files. This HttpModule is supported by a .config file that maps .js files with entities where those files needs to be
included. We only include .js files where they need to be included because we want to keep the CRM as performant as possible.
There is more on our framework but with this you have a very good start !