Donnerstag, 16. Juni 2011

Useful Form Scripts #5: Role-Tailored Forms

Useful Form Scripts #5: Role-Tailored Forms

The Problem: (Only) One Form per Entity

There are lots of ways you can customize Dynamics CRM 4.0. For example, you can add custom attributes to system entities, you can add custom entities, you can customize relationships between entities, you can customize forms and views, and you can add custom views.Topics like the one in this article — plus ASP.NET development, Plug-Ins and more — are covered in my one-day live online training class, Extending Dynamics CRM
But you cannot add custom forms: every customizable entity in Dynamics CRM has one form which you can customize, but you can’t add a new form. This one-form-per-entity rule seems restrictive, especially if you’re new to CRM and moving over from a platform without that limitation.
While it’s true you can only create one form per entity, it’s not true that all users must see the same version of the form. With the technique I describe here, you can create a role-tailored experience for your users, which can be an important contributor to user satisfaction, user adoption, and overall CRM success.
Here are a couple scenarios that might call for role-tailored Dynamics CRM forms:
  • In a sales scenario, an outside rep might be the rainmaker, playing golf, doing deals, no need or time for lots of details. The outside rep might be paired with a cube-bound inside rep, who really does need the detail, some of which can be expressed on a more complex form for the opportunity entity.
  • In a service scenario, it might go the other way: a level 1 case is handled by a junior service rep, who handles the simpler cases and escalates the complicated ones to a manager. Escalated cases tend to be more complex, and the manager might require a more detailed form experience when working with service cases.

The Solution: Dynamic Forms Based on Security Roles

I wrote briefly about this problem in an earlier article but I didn’t include the entire code listing. Partly because a few readers asked me for a full sample, and partly because I wanted it for my own reference, here’s a complete listing and a more complete explanation.
JavaScript can be run on a form’s OnLoad event, it can check to see which user’s loading the form, and it can present a dynamically tailored form to a user or group of users. It can also check the current state of the record – the value of certain fields, for example – and incorporate that information to tailor a form appropriately. In the example I’ll show here, an important feature is the WhoAmI method of the CrmService web service. You can use it to query the security role of the current user, and provided you can use security roles to drive the form experience, this technique can work pretty well.
The following code can go in the OnLoad event of an un-customized Opportunity form. (I’ll list and explain it in three separate sections, but if you want to copy it to the clipboard and try it yourself, remember that the way I wrote it, all three sections need to be pasted, successively, into the OnLoad event of Opportunity.)

Section 1: test for user’s security roles and show appropriate version of form:

This section calls the UserHasRole function, which is in section 2.
if (UserHasRole("Outside Sales")) {
    //hide default Administration and Notes tabs: = "hidden"; = "hidden";
    //hide default probability, price list, rating and currency fields = "hidden"; = "hidden"; = "hidden"; = "hidden"; = "hidden"; = "hidden"; = "hidden"; = "hidden";

Section 2: The UserHasRole function

This convenient wrapper function and the one in the next section are taken, unchanged, from Jim Wang’s excellent article, Check current user’s security role using JavaScript.
function UserHasRole(roleName) {
    //get Current User Roles, oXml is an object
    var oXml = GetCurrentUserRoles();
    if (oXml != null) {
        //select the node text
        var roles = oXml.selectNodes("//BusinessEntity/q1:name");
        if (roles != null) {
            for (i = 0; i < roles.length; i++) {
                if (roles[i].text == roleName) {
                    //return true if user has this role
                    return true;
    //otherwise return false
    return false;

Section 3: GetCurrentUserRoles

This part does the heavy lifting. It constructs a SOAP message in the form of a big honkin’ text string — that’s the xml variable — that eventually gets passed through to the RetrieveMultiple method of the CrmService web service and returns the result. To really understand what’s going on in here, use the Visual Studio debugging technique I described in Useful Form Scripts #3.
function GetCurrentUserRoles() {
    var xml = "" +
 "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
 "<soap:Envelope xmlns:soap=\"\" xmlns:xsi=\"\" xmlns:xsd=\"\">" +
 GenerateAuthenticationHeader() +
 " <soap:Body>" +
 " <RetrieveMultiple xmlns=\"\">" +
 " <query xmlns:q1=\"\" xsi:type=\"q1:QueryExpression\">" +
 " <q1:EntityName>role</q1:EntityName>" +
 " <q1:ColumnSet xsi:type=\"q1:ColumnSet\">" +
 " <q1:Attributes>" +
 " <q1:Attribute>name</q1:Attribute>" +
 " </q1:Attributes>" +
 " </q1:ColumnSet>" +
 " <q1:Distinct>false</q1:Distinct>" +
 " <q1:LinkEntities>" +
 " <q1:LinkEntity>" +
 " <q1:LinkFromAttributeName>roleid</q1:LinkFromAttributeName>" +
 " <q1:LinkFromEntityName>role</q1:LinkFromEntityName>" +
 " <q1:LinkToEntityName>systemuserroles</q1:LinkToEntityName>" +
 " <q1:LinkToAttributeName>roleid</q1:LinkToAttributeName>" +
 " <q1:JoinOperator>Inner</q1:JoinOperator>" +
 " <q1:LinkEntities>" +
 " <q1:LinkEntity>" +
 " <q1:LinkFromAttributeName>systemuserid</q1:LinkFromAttributeName>" +
 " <q1:LinkFromEntityName>systemuserroles</q1:LinkFromEntityName>" +
 " <q1:LinkToEntityName>systemuser</q1:LinkToEntityName>" +
 " <q1:LinkToAttributeName>systemuserid</q1:LinkToAttributeName>" +
 " <q1:JoinOperator>Inner</q1:JoinOperator>" +
 " <q1:LinkCriteria>" +
 " <q1:FilterOperator>And</q1:FilterOperator>" +
 " <q1:Conditions>" +
 " <q1:Condition>" +
 " <q1:AttributeName>systemuserid</q1:AttributeName>" +
 " <q1:Operator>EqualUserId</q1:Operator>" +
 " </q1:Condition>" +
 " </q1:Conditions>" +
 " </q1:LinkCriteria>" +
 " </q1:LinkEntity>" +
 " </q1:LinkEntities>" +
 " </q1:LinkEntity>" +
 " </q1:LinkEntities>" +
 " </query>" +
 " </RetrieveMultiple>" +
 " </soap:Body>" +
 "</soap:Envelope>" +
var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
    xmlHttpRequest.setRequestHeader("SOAPAction", "");
    xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
var resultXml = xmlHttpRequest.responseXML;
    return (resultXml);

How it Works

It’s easiest to test this if you’re running CRM as a system administrator — or more precisely, if one of the security roles assigned to your user account is the “System Administrator” role. You can test it yourself by following these steps:
1. Start by copying the code blocks in each of those three sections, one after the other, into the OnLoad event code of Opportunity. Make sure the “Event is enabled” checkbox is selected, then publish the customizations.
2. Add a custom security role, “Outside Sales”. If you’re in the System Administrator role, it doesn’t even matter if this custom role has any permissions or not — all that matters for the purpose of testing this code is that you’re assigned to a role with that name. Open an existing or a new opportunity – should be the same experience either way — and you should see the simplified version of the form:
3. Next, open up your user form and remove that role. The next time you open the opportunity form you’ll see the “normal” opportunity form…or whatever version of it you’d normally see:
Remember: the code specifically refers to the Price List, Rating, and Probability fields, so if you’re working with an already-customized version of the form you’ll need to tweak it a little bit.
This example is somewhat simplistic and not intended as an exercise in best-practice CRM form design, but it does illustrate some interesting points:
  • You don’t really have to display the Administration and Notes tabs — you can save a record fine without them, since all the required fields have default values filled in.
  • Hiding fields is a little hinky: = "hidden";
    hides the label of the opportunityratingcode field, and = "hidden";
    hides the data. Plus, whereas when you hide a section the sections underenath it move up so there’s no holes on the form, that isn’t the case with fields. There are certainly other ways to do this, but worst case you could create separate sections with no labels to contain fields you wanted to hide, and then just hide the sections.

Twitter Delicious Facebook Digg Stumbleupon Favorites More

Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Free Samples By Mail