Monday 3 December 2007

Providing different default views for different CRM users

Or, how to modify CRM grid pages via client script.

CRM provides 2 standard ways to add custom client script into the CRM application; via form events, and through menu items and buttons in ISV.Config. Neither of these allows you to write code to control the default views for CRM entities

However, there is a way round this. The approach is to create a host HTML (or ASP .Net) page that has no interface components itself, but contains the CRM page in an IFrame. This will appear exactly the same as the standard CRM page, but allows you to write client script in your host page that controls the CRM page, and hence change which view is displayed.

Note that this approach has to be considered as unsupported, as it involves programmatic control over a picklist outside of a CRM form.

Building the host page
There are 2 aspects of the host page; providing the HTML to host the IFrame without adding extraneous borders or padding, and adding client script that runs once the IFrame has loaded.

The following HTML shows how to host the IFrame:

<body style="margin:0" onload="Init();">

<iframe onreadystatechange="ors();" id="ifr" src="about:blank" width="100%" height="100%" frameborder="0" leftmargin="0">
</body>

In this example I’m creating a generic page that can host most CRM grid pages, and I’m setting the src property of the IFrame programmatically:

function Init()
{
var etc = getQS('etc');
if (etc != null)
{
document.all.ifr.src = '/_root/homepage.aspx?etc=' + etc;
}
}

This src is the standard way to display most entity grids, with etc as the object type code of the entity.

I’m using the onreadystatechange event to determine whether the IFrame has loaded:

function ors()
{
if (event.srcElement.readyState == 'complete')
{
var sView = getQS('view');
if (sView != null)
SetView(sView);
document.all.ifr.style.visibility = 'visible';
}
else
{
document.all.ifr.style.visibility = 'hidden';
}
}


This checks the readyState property of the IFrame, which will equal ‘complete’ when the IFrame contents have loaded, and the IFrame contents after they have been modified.

The following code shows how to modify which view is selected:

function SetView(sView)
{
var ifDoc = document.frames['ifr'].document.all; // access IFrame contents
var oSel = ifDoc['SavedQuerySelector']; // picklist control to select view
if (oSel != null)
{
var v = GetSelectValue(oSel, sView);
if (v)
{
oSel.DefaultValue = v;
oSel.DataValue = v;
oSel.FireOnChange(); // need to fire this event to apply changes
}
}
}
function GetSelectValue(oSel, sText) // helper function to select item in picklist
{
for (var i=0;i<osel.options.length;i++)

{
if (oSel.options[i].text == sText) return oSel.options[i].value;

}
}

The code uses another helper function to access parameters passed on the query string. This function is oversimplified in that it doesn’t cope with all possible encoding issues, but is sufficient for this example:

function getQS(name)
{
var ret = '';
if (window.location.search != null && window.location.search.length > 1)
{
var aQS = window.location.search.substring(1).split('&');
if (aQS != null)
for (var i=0;i<aQS.length;i++)
if (aQS[i].indexOf(name + '=') == 0)
ret = aQS[i].substring(name.length + 1).replace('%20', ' ');
}
return ret;
}

Putting this all together, we have a page that will display an entity grid, and change the default view, based on 2 query string parameters. For example

defaultViewChanger.htm?etc=1&view=Active%20Accounts

Will display the account grid, and set the view to the Active Accounts view (%20 is the encoding of a space, which is not permitted in a url).

Setting different default views for different users
We now have a page that can programmatically change the default view. There are 2 ways this can be used to provide different default views to different users: a programmatic way that identifies the current user (and probably team or role membership) and hence determines the default view, or via permissions in SiteMap.

I’m not intending the cover the programmatic route in detail here; my preference is to convert the page to an ASP .Net page that identifies the current user and their role, and populates the parameter to SetView in server code; an alternative approach in client code can be found here.

Another approach is to make use of the Privilege element in SiteMap to display different navigation links to different sets of users based on their permissions, as described in more detail here. Let’s assume we have 2 groups of users who want different default views of the account entity, group A (who are members of security role ‘roleA’) want to see My Active Accounts, and group B (members of ‘roleB’) who want to see Active Accounts.

We can create a dummy entity in CRM called exc_acctsecurity and grant roleA write rights on the exc_acctsecurity entity, and grant roleB assign rights. Then we can modify SiteMap, replacing the SubAreas for the account entity to the following:

<SubArea Id="nav_accthostA" Title="Accounts" Url="http://server/defaultViewChanger.htm?etc=1&amp;view=My%20Active%20Accounts" Icon="/_imgs/ico_18_1.gif">
<Privilege Entity="exc_acctsecurity" Privilege="Write" />
</SubArea>
<SubArea Id="nav_accthostB" Title="Accounts" Url="http://server/defaultViewChanger.htm?etc=1&amp;view=Active%20Accounts" Icon="/_imgs/ico_18_1.gif">
<Privilege Entity="exc_acctsecurity" Privilege="Assign" />
</SubArea>

Note the use of XML encoding (&amp; instead of &).

One drawback of this approach is that anybody in the System Administrator role will see both SubAreas.

A complete example of the HTML and SiteMap can be found on CodePlex.

5 comments:

Matt Wittemann said...

David: Looks like a great bit of code - only I can't get it to work. No errors, it just always returns the default view, regardless of the parameters passed to it. Any hints? I'm testing it without the security settings in the sitemap...
Thanks,
Matt

David Jennaway said...

Matt

Are you using the download from CodePlex, or assembling the code from the blog page ? I've just tested to CodePlex download and it's fine for me.

The most likely problem is encoding on the query string. Try browsing directly to the following url (with changes to the view if required):
http://server/defaultViewChanger.htm?etc=1&view=Active%20Accounts

The above should work, but wouldn't if it had ?etc=1&amp;view=Active%20Accounts

(Note that all my ampersand (&) encoding gets a bit messed up when posting)

Matt Wittemann said...

Thanks, David. I was using the download from CodePlex. I'm pretty sure the URLs were correctly formed, but I'll give it another go and let you know if it doesn't work out.

Ben Harper said...

Works great from IE, but when you try and use this page in the shortcut of an outlook folder (we use the outlook client as apposed to directly through a web browser?) it fails with "An error has occurred see you administrator.

Can you see any reason why this solution would work so beautifuly in Ie but not in the outlook client?

Cheers and thanks for a great work around.

Ben Harper said...

Sorry, in the homepage field field of the outlook crm folder (there is no shortcut field!!).
Cheers