Recently I was asked if it is possible to restrict a Search Select Advanced field based on criteria that may change client-side. The answer was no, it's not possible through standard customization. However it's not impossible...
I want to be very upfront about this blog post: it is about an un-supported technique for hacking functionality into CRM using client-side javascript. Before using any of the techniques described in this post please read Jack’s Hack FAQ to learn more about hacks and the risks involved in using them.
There are two functions that get invoked client-side when a user interacts with an SSA field, they are: window.NavUrl<<fieldname>> and window.ShowPopupOptionsWindow<<fieldname>>. As an excercise in learning, go to a CRM screen that has an SSA field and view the source and find these functions.
You will see that the SearchSQL is written in here. It gets passed back to CRM when displaying the drop down list of values or when the user goes to the Find pop up screen.
This means that we can change the SearchSQL client side... It's within our control. The question is: how?
What we need to do is to re-write the function, replacing the SearchSQL value with what we want it to be. We can then: replace the existing function with the re-written function, and voila! the SearchSQL has been effectively changed. (Note server-side the SearchSQL has not been altered in the meta-data)
To do this we will be exploiting some very useful features of javascript:
-
All functions are variables
-
All functions can be turned into their string representation using
toString().
-
The
eval method can be used to execute any string as if you had typed the javascript yourself.
So, first things first, we need to get the function into a variable so we can mess with it. We could just say:
var s = window.NavUrlcase_primarypersonid.toString();
but we want to use this hack in multiple places and don't want to have to re-write it for each scenario. So it'd be nicer if we could load the fieldname into a variable and then use that to reference the function. So instead we will use:
var s = window['NavUrl' + strFieldname].toString();
So now our variable, s, has the following in it:
function NavUrlcase_primarypersonid(ID,url,fieldname){
var e='';if(ID){ if(document.EntryForm.case_primarypersonid.value == ID) { return false; } else e='&ID='+ID;}else{if(document.EntryForm.case_primarypersonid!=null && document.EntryForm.case_primarypersonid.value!=''){if(confirm('The field is already matched. Do you want to clear the results?')){Clearcase_primarypersonid();}return;}}var u='/jCRM62/eware.dll/Do?SID=111476224537364&Act=1275&Mode=6&CLk=&Key0=4&Key4=1&Key27=40&ViewField=,Pers_FullName,Pers_PhoneFullNumber,
&JumpReturnCol=case_primarypersonid&JumpIdField=Pers_PersonId&JumpNameField=Pers_FullName&SearchEntity=Person
&SearchTable=vSearchListPerson&SearchSql=&searchsqld=&SsDef=20&LinkedField=&TiedField=case_primarypersonid
&SearchText='+encodeURIComponent(document.EntryForm.case_primarypersonidTEXT.value)+e;document.getElementById('SEARCHFRAMEcase_primarypersonid').src=u;
}
Now all we need to do is replace the existing SearchSQL with our own. Because the function is now a string this can be done very easily with regular expression:
s = s.replace(/&SearchSql=[^&]*/i, '&SearchSql=' + escape(searchSQL));
Note we use the built in escape function to put the searchSQL into the correct format. This will handle all the appropriate delimiting for us.
Next we need to replace the existing function with our new function. To do this we will use the eval function to make javascript interpret our string as an actual function definition. But first we need to strip out the function name, as when you assign a function to a variable it should be an anonymous function. To strip the name out we use the replace method again:
s = s.replace('NavUrl' + strFieldname, '');
OK, so now we can use the eval statement like this:
eval('window[\'NavUrl\'+strFieldname]=' + s);
This is the equivalent to us typing out by hand the following:
window.NavUrlcase_primarypersonid = function (ID,url,fieldname){
var e='';if(ID){ if(document.EntryForm.case_primarypersonid.value == ID) { return false; } else e='&ID='+ID;}else{if(document.EntryForm.case_primarypersonid!=null && document.EntryForm.case_primarypersonid.value!=''){if(confirm('The field is already matched. Do you want to clear the results?')){Clearcase_primarypersonid();}return;}}var u='/jCRM62/eware.dll/Do?SID=111476224537364&Act=1275&Mode=6&CLk=&Key0=4&Key4=1&Key27=40&ViewField=,Pers_FullName,Pers_PhoneFullNumber,
&JumpReturnCol=case_primarypersonid&JumpIdField=Pers_PersonId&JumpNameField=Pers_FullName&SearchEntity=Person
&SearchTable=vSearchListPerson&SearchSql=pers_lastname%20LIKE%20%27s%25%27&searchsqld=&SsDef=20&LinkedField=&TiedField=case_primarypersonid
&SearchText='+encodeURIComponent(document.EntryForm.case_primarypersonidTEXT.value)+e;document.getElementById('SEARCHFRAMEcase_primarypersonid').src=u;
}
Now when the CRM UI goes to call the NavUrl function it will call ours, with the modified SearchSql, instead of the built-in one.
Using all the techniques described above we can create a re-usable function like this:
function SetSSASearchSQL(strFieldname, searchSQL) {
if (!window['NavUrl' + strFieldname]) return; // if the function does not exist we must not be in edit mode
// replace SearchSql in the NavUrl function
eval('window[\'NavUrl\'+strFieldname]='
+ window['NavUrl' + strFieldname].toString().replace(/&SearchSql=[^&]*/i, '&SearchSql='
+ escape(searchSQL)).replace('NavUrl' + strFieldname, '') + ';');
// replace SearchSql in the ShowPopup function
eval('window[\'ShowPopupOptionsWindow\'+strFieldname]='
+ window['ShowPopupOptionsWindow' + strFieldname].toString().replace(/&SearchSql=[^&]*/i, '&SearchSql='
+ escape(searchSQL)).replace('ShowPopupOptionsWindow' + strFieldname, '') + ';');
}
We can then test this function using code like this:
window.attachEvent('onload',
function () {
SetSSASearchSQL('case_primarypersonid', "pers_lastname LIKE 's%'");
}
);
So there you have it. Using this technique you can dynamically change SearchSql client side on Search Select Advanced fields.