May
08

Dynamics CRM4.0: Client-side validation scripts

Everybody who customized CRM in the slightest will know that adding a client side validation script on one or more fields on a form is quite tedious and repetitive work.
 
Therefor we've looked a little bit deeper at the problem here at Orbit One, to be able to serve our customers quicker, better and more reliable.
 
Let me try and scetch the situation in CRM to add 1 field validator. There are a few steps that you need to do.
  1. Open CRM
  2. Goto Settings
  3. Click on the Customization link in the menu (left side)
  4. Select the entity for which you want to validate a field
  5. Click Forms and Views in the new screen (again on the left side)
  6. double click on the Form item
  7. double click on the Field you want to validate
  8. Select the tab Events
  9. Click on the Edit button
  10. Write your validation logic in JavaScript
  11. Do not forget to enable the event with the checkbox above your validation code
  12. Click the OK button
  13. Click the OK button
  14. Click the Save and Close button on top
  15. Click the Actions button on top and select publish.

Steps 7 through 14 are the steps you'll need to do for every field if you do the validations in bulk.

What we do now is the following. We add some code to the OnLoad event of the form as following:

  1. Open CRM
  2. Goto Settings
  3. Click on the Customization link in the menu (left side)
  4. Select the entity for which you want to validate a field
  5. Click Forms and Views in the new screen (again on the left side)
  6. Double click on the Form item
  7. Click on the Forms Properties button on the right side
  8. Click on the Edit button
  9. Write code here:
    var scriptTag = document.createElement("<script src='/Custom/validation.js' type='text/javascript'>" );
    var array = new Array();
    array = document.getElementsByTagName("head");
    array[0].insertAdjacentElement("afterBegin", scriptTag);
  10. Do not forget to enable the event with the checkbox above your validation code
  11. Click the OK button
  12. Click the OK button
  13. Click the Save and Close button on top
  14. Click the Actions button on top and select publish.
Now that we have done this, lets take a look at the validation.js file. It should load every validation we have, so lets start by adding something that will make the onload of document trigger:
var oldOnload = document.onload;
if(oldOnload != undefined && oldOnload != null && typeof(oldOnload) == "function"){
 if(crmForm.crmFormRootElem.value == "contact"){
  document.onload = function(){ oldOnLoad(); contactFormLoad(); }();
 }
} else {
 if(crmForm.crmFormRootElem.value == "contact"){
  document.onload = function(){ contactFormLoad(); }();
 }
}
 
What does this code do ??? Well, the onload function is stored in the oldOnload variable. Then we check if the oldOnload function is actually a defined, if it is not null and that the type is a function. If this is the case there already was an onload function, so we do not want to lose that one. So what happens next is we create a new document.onload function that calls the oldOnLoad and then our own onload function contactFormLoad. If there is not already an onload function for the document we just use our contactFormLoad.
 
The contactFormLoad function looks like this:
function contactFormLoad(){
 overloadOnChange("address1_telephone1", address1_telephone1_onchange);
 overloadOnChange("emailaddress1", emailaddress1_onchange);
 overloadOnChange("websiteurl", websiteurl_onchange);
 
 hookOnSave(contactFormSave);
}
 
As you can make up from the code above, we're going to validate a phonenumber, an email address and a website url.
 
Then we are going to make sure the onsave also gets triggered so all our validation logic is run at the save, just to make sure.
 
The overloadOnChange method: where most of the magic happens.
 
function overloadOnChange(fieldName, newOnChangeFunction){
 if(fieldName == null)
  return;
 var control = crmForm.all[fieldName];
 if (control != null){
  if(typeof(newOnChangeFunction) == "function"){
   if (typeof(control.onchange) == "function"){
    if (control.onchange != null) {
     var oldOnChangeFunction = control.onchange;
     control.onchange = function() { newOnChangeFunction(); oldOnChangeFunction(); };
    } else {
     control.onchange = function(){ newOnChangeFunction(); };
    }
   } else {
    control.onchange = function(){ newOnChangeFunction(); };
   }
  }
 }
}
 
This is a chunck of code that can be found inside CRM itself.
They use this to hook the clientside code to the fields if and only if the event is enabled checkbox is checked.
I don't want to check every checkbox on every field I want to validate. That's way to repetitive. So in the code above we flipped the positions of our new eventhandler and there old eventhandler, so that ours is triggered before theirs.
This seems like good practice to us because that way we can still use the validation out of the box in CRM like everybody does, but we can also use our method... So this is actually only added value. Nothing breaks in the inner workings of CRM.
 
 
And the hookOnSave part is this:
function hookOnSave(onsaveFunction){
 if (crmForm != null) {
  if(typeof(crmForm.onsave) == "function"){
   var oldOnsave = crmForm.onsave;
   if(oldOnsave != null) {
    crmForm.onsave = function(){ onsaveFunction(); oldOnSave(); };
   } else {
    crmForm.onsave = function(){ onsaveFunction(); };
   }
  } else {
   crmForm.onsave = function(){ onsaveFunction(); };
  }
 }
}
 
Again with the old function that's being saved and so forth and so on ... just as in the previous function we'll do our onsave trigger followed by CRM's own onsave.
 
In our onsave method - contactFormSave - we trigger our functions again:
 
function contactFormSave(){
 try{
     TryFireOnChange("address1_telephone1");
     TryFireOnChange("emailaddress1");
     TryFireOnChange("websiteurl");
     event.returnValue = isFormValid();
 }
 catch (e) {
     displayError("crmForm", "onsave", e.description);
 }
}
 
Followed by the TryFireOnChange. This function has been added because we've had a minor issue in migrating the script. One of the fields in our staging environment was missing in the production environment. So when the field you try to validate the field but it doesn't exist then the catch get hit and nothing happens.
 
function TryFireOnChange(fieldName){
 try {
  if(document.crmForm.all[fieldName] != undefined) {
   document.crmForm.all[fieldName].FireOnChange();
  }
 }
 catch (e){}
}
 
I am not going to elaborate on the validation functions itself because this is not really the scope of this post.
 
 
The PRO's to this approach are pretty huge for us:
  • we can migrate the validations from our staging environment to the production environment by just copying one file.
  • we can just edit the file instead of having to click a bunch of times in the CRM web interface
  • we can still do everything through the CRM web interface
  • we can manage the one file in our source control more easily and track everything better.
I hope you enjoyed reading this, and that it makes your work a little bit easier.
 
 
Posted by Wim De Coninck | Leave your feedback

May
04

Language sensitive search results in SharePoint

 

The Problem:

SharePoint returns different search results when I change my browser language

Searching for "the" in English will give no results because the word is ignored. 

Search results in English

Searching for "the" in Dutch does give results.

 Search results in Dutch

This is a feature of SharePoint, it's not a bug.

However, it's not always desirable behaviour. In a multi-lingual site with a variation for each language the search results should be in the language of the current variation.

By default SharePoint will ignore the variation settings and still take the browser language.

There are several posts with the same problem, here and herehere. The suggested solutions range from changing the browser language programmatically to creating a new masterpage with the Culture hard coded, to subclassing the search results web part and changing the Culture settings in the OnInit event.

Unfortunately these solutions either do not work or require a lot of work for a simple change.

The solution:

The Search Core Results web part has a property called "SearchLanguage", which holds the locale of the search results. By default this property is null, and the locale will be inferred from the browser language

To set this property to your language of choice, export the Search Core Results web part.

Towards the end of the file you'll find the property QueryLanguage, which is null by default:

<property name="QueryLanguage" type="string" null="true" />
 

Change this to

<property name="QueryLanguage" type="string">en-GB</property>
 

Now import the web part back to the page and remove the old Search Core Results web part.

The search results will now always be in the language you chose, no matter what the browser language is.

Posted by Mel Gerats | 1 Comment

Orbit One on LinkedIn
Contact us - Raas Van Gaverestraat 83, 9000 Gent, Belgium - Tel. +32 (9) 330.15.00 - Privacy Statement - Sitemap - Sign In Developed with Microsoft Office SharePoint Server 2007