Getting scripts into hard-to-reach places (for Sage CRM v7.1sp2 and earlier)

Hints, Tips and Tricks

Technical Hints Tips and Tricks that cover customization and development using Sage CRM. API usage and coding are covered.

Getting scripts into hard-to-reach places (for Sage CRM v7.1sp2 and earlier)

  • Comments 3
  • Likes

Note:  This article discusses techniques relevant for Sage CRM v7.1sp2 and earlier.  If you are using Sage CRM 7.2 please refer to the articles that discuss the new Client Side API.

*** Please note that this article describes a technique that exploits undocumented workings of the product.  Please read this FAQ to understand the risks involved in using this technique ***

A while back I blogged about injecting scripts into system action screens that don't have custom content field.  Well recently I had an epiphany about Translations and using them to embed scripts into screens where there is no custom content field. The technique described in this post is a lot easier to implement than the one in my previous post and also may be more robust.

So for those of you that don't know about translations here is a brief overview, for everyone else please feel free to skip this next paragraph:

CRM is a multi-language product; It allows users of the product to select their own preferred language.  It achieves this through the use of Translations.  Translations are stored in the custom_captions table and are distinguished by the capt_code and capt_family fields.  When CRM wants to write some words on the screen it does a lookup in the custom_caption table using specific values for the capt_code and capt_family fields and then uses the text found in the appropriate language field.  E.g. the capt_us field for American users, capt_de for German users etc.

An easy way to find the translation record that is used for text in CRM is to turn on the Inline Translation Mode (in Administration -> Customization -> Translations) and then click on the hyperlinked asterisk next to the text you are looking for.

It only occurred to me recently that you can not only put text into a translation but HTML markup and script tags.  This means you can embed a script into a translation that gets run (client side) at the time a translation is used on the screen.

This can be exploited to allow us to inject scripts into those hard-to-reach screens that do not have a Custom Content field.

There are however a couple of gotchas.

First is that CRM will remove the first ampersand (&) that it finds in a translation text.  This is a short hand way of specifying that a letter should be underlined.  So when CRM reads in a translation it finds the first ampersand and removes it and then underlines the following letter.  If the first ampersand is an essential part of your script code it will be removed with potentially disastrous results.  A simple way around this problem is to put an ampersand at the beginning of your script that you do not mind if it gets removed.  Use a line like the following:

//& This comment is required to prevent CRM from stripping the &'s out of our code

Also CRM will replace two ampersand's with one.  This is a way of escaping ampersands.  What this means for your scripts is when you want to use the && operator you need to write it as &&&&. 

The second gotcha is that the translation may be used in more than one place and so make sure your code does some checking to ensure it is running on the correct screen.  An easy way to do this is to interrogate the Act parameter in the query string.

The following example script shows you how to remove the "Disable User" button for the admin user on the edit user screen (system action 801). 

Find the translation with Caption Code of “Disable”, Caption Family of “Button” and Caption Family Type of “Tags”.  For each translation put the following code at the end of the translation:

<script>
//& This comment is required to prevent CRM from stripping the &'s out of our code

function GetArg(Arg){
 var v=window.location.href.match(new RegExp('([?&]' + Arg + '=)([^&]*)', 'i'));
 return (v)?v[2]:'';
}

function HideButton(imageName) {
 var imgs = document.getElementsByTagName('IMG');
 var findRegEx = new RegExp('\/' + imageName, 'i');
 var img;
 for(var i=0; i<imgs.length; i++) {
  if (imgs[i].src.search(findRegEx)!=-1) {
   img = imgs[i];
   break;
  }
 }
 img.parentNode.parentNode.parentNode.parentNode.style.visibility = 'hidden'; 
}

window.attachEvent('onload', function () {
 // this if statement ensures we are on the user edit screen
 if (GetArg('Act')==801) {
  if (GetArg('Key11') == 1) HideButton('user_disable.gif');
 }
});
</script>

The GetArg function is an easy way of stripping parameters out of the query string. In this script we use it to check that we are running the script only on edit user screens and not anywhere else the translation may pop up.  We also use it to determine the user in context by querying Key11.  For more information on using Keys in urls see Jeff's post here.

 

Comments
  • Hi Jack,

    I'm working on hiding of the Export to Excel under the print Report screen. This is something important that we need SageCRM to come with for enhancement later.

    At the moment, I'm using this method for hiding of the radio buttons (output to PDF, CSV & Excel) ie. injecting the javascript into the translation area. And now I need to put in some logic as in it is configurable for users to have to right to access those buttons under the report screen. What do you suggest? To add COM API to check if user has the rights to access the output radio buttons in the Report Screen? AJAX or Web service API? Anyone has done anything like this before?

  • Okay, I've got the answer for this. Jack ignore my question as 3rd level support replied me. I've logged this as enhancement too. Thanks :)

  • Hi, you say "If you are using Sage CRM 7.2 please refer to the articles that discuss the new Client Side API."

    But does the client side api allow you to place scripts on pages that don't have a Custom Content field ?

    Example - I want to manipulate the Report Criteria screen.

    Thanks!

    Kevin.