Sunday, May 31, 2009

CRM 4.0 Embedding User Signature in CRM Web Client

One of the main features that the CRM email web client is missing is an automatic stationary signature. Although a user (or an administrator) can create a predefined signature and embed it before sending the email the web client requires the user to fill the TO (or CC) field in advance and then pick out the signature template using the Insert Template button. For most of us who gotten used to using advanced editors like outlook this seems like a major step backwards.

In order to enhance the user experience and demonstrate to strength and simplicity of dynamics while I’m at it I wrote a post that automates the process. The automated signature makes use of CRM’s email template feature. Once the Global email template (signature) is in place you need to extract its id (templateid) and use it in your code.

In order to retrieve the template id you can run this following query against the filteredtemplate view:

SELECT TEMPLATEID FROM FILTEREDTEMPLATE WHERE TITLE = ‘USER SIGNATURE’


The nice thing about dynamics is that most UI features also have an API manifestation. In this case it’s the ability to instantiate email templates by using the InstantiateTemplateRequest. The InstantiateTemplateRequest receives a templateid, objectid and objecttype. The objectid and type are used as context so the template is able to retrieve information that is specific to the recipient entity. Since we are only interested in the user information the objectid and type are filled with the email owner which is the current user.

Copy the following code to the email onload event and enjoy…


function Signature(companyTemplateId)
{
var sig = this;
var emailIframe;
var emailBody;

sig.TemplateId = companyTemplateId;

sig.Load = function()
{
try
{
var xml = '' +
'' +
'' +
GenerateAuthenticationHeader() +
' ' +
' ' +
' ' +
' ' + sig.TemplateId + '' +
' ' + crmForm.ownerid.DataValue[0].typename + '' +
' ' + crmForm.ownerid.DataValue[0].id + '' +
'
' +
'
' +
'
' +
'
' +
'';

var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
xmlHttpRequest.open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xmlHttpRequest.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/Execute");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
xmlHttpRequest.send(xml);

var resultXml = xmlHttpRequest.responseXML;
if (xmlHttpRequest.status == 200)
{
emailBody = resultXml.selectSingleNode("//q1:description").text;
emailIframeReady();
}
}
catch(err)
{
alert(err.description);
}
}

function emailIframeReady()
{
if (emailIframe.readyState != 'complete')
{
return;
}

emailIframe.contentWindow.document.body.innerHTML = emailBody;
}

emailIframe = document.all.descriptionIFrame;
emailIframe.onreadystatechange = emailIframeReady;
}

function OnCrmPageLoad()
{
if (crmForm.FormType == 1)
{
var signature = new Signature("90886EF8-1A4D-DE11-9CF8-0003FF230264");
signature.Load();
}
}

OnCrmPageLoad();

28 comments:

Michael Cross said...

I'm trying this code but I'm getting an error in the OnLoad event.

Invalid character in line 32

This is an IFD setup. Could that be the issue?

Adi Katz said...

This does not "sound" like an IFD problem.

Enable IE debugging , put the debugger keyword before line 32 and see why it really breaks.

Also, you might have copied the code with unwanted characters. So make sure it’s clean and try again.

Maria K. Greendyk said...

Hi - I've implemented this code and am presented with a window to enter my credentials. After I do so, the text area of the e-mail contains "undefined" and the signature is not populated.

Any thoughts?
Thank you!
~Maria

Adi Katz said...

Did you get this to work? Make sure the (global) signature GUID is correct.

Mike said...

I have created a Global template (ehich contains my email signature)and identified its GUID. I have pasted in your code and replaced the GUID with the one returned from my query and published. WHen I select new email nothing appears in the email description. in the bottom left it does state 'Error on page' but I can get no more information

Peter Moys said...

This would seem to do exactly what i need. However, i'm having the same problem as Maria.
Is the GUID displayed when opening the template the GUID i should be using?

Any advice gratefully received!

Peter Moys

Peter Moys said...

I am certain it is not a problem with the GUID, so it must be something else...
I would love to get this working, so please help if you can!

3biz said...

anyone got this to work ?

Adi Katz said...

The code formatter changed the query xml. Reloaded the code with different formatting. Try it now.

Nestor said...

I get this:

'crmFrom.ownerid.DataValue.0' is null or not an object.

Any suggestions?

Thanks,

Nestor

Adi Katz said...

The email owner field must have a value. The email template dynamic values are taken from this owner.

Nestor said...

Thanks I figured that part out. Turns out that object is null when testing the form it works when it is published.

However, I am now getting this back from the web service 401.2 Unauthorized. You don't happen to know anything about this. We are using an on-premise version.

Thanks,

Nestor Reyes

Adi Katz said...

Copy the signature function to your code again and try it out.

Nestor said...

Tried it. Still being prompted 3 times for user name and password. After the 3 attempts I display the result of the call. I added this to the code.

var resultXml = xmlHttpRequest.responseXML;
if (xmlHttpRequest.status == 200)
{
emailBody = resultXml.selectSingleNode("//q1:description").text;
emailIframeReady();
}
else
{
alert(xmlHttpRequest.statusText + " : " +xmlHttpRequest.status );
}

Nestor said...
This comment has been removed by the author.
Nestor said...

More info since I cannot post XML. The soap header contains an authentication type of 0 and a GUID of 00000000-0000-0000-0000-000000000000

Thanks,

Nestor

Adi Katz said...

The code formatter changes the xml query to upper letters. Try re-writing the query using correct lettering use ObjectId instead of OBJECTID etc…

Nestor said...

I ended up scraping the JavaScript solution and created a CRM 4.0 Plug-in. Works like a charm now, it appends the signature when user clicks send.

Anonymous said...

Hi,
I also have problems using your code above. As Nestor already mentioned I'm prompted 3 times to enter my username and password. After that I get an 401, which indicates a problem with authorization. But I don't know what I'm doing wrong. Any hints?

Anonymous said...

Not sure I can post this into the blog, but if at appears this should work (or at least does for me)

var templateId = 'E4F6EE02-665E-DF11-9F40-001372FB23D1';

function Signature(companyTemplateId)
{
var sig = this;
var emailIframe;
var emailBody;
var header = GenerateAuthenticationHeader();

sig.TemplateId = companyTemplateId;

sig.Load = function()
{
try
{
var xml ='' +
header +
' ' +
' ' +
' ' +
' ' + sig.TemplateId + '' +
' ' + crmForm.ownerid.DataValue[0].typename +'' +
' ' + crmForm.ownerid.DataValue[0].id + '' +
' ' +
' ' +
' ' +
'';

// alert(xml);

var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
xmlHttpRequest.open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xmlHttpRequest.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/Execute");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
xmlHttpRequest.send(xml);

var resultXml = xmlHttpRequest.responseXML;
if (xmlHttpRequest.status == 200)
{
emailBody = resultXml.selectSingleNode("//q1:description").text;
emailIframeReady();
}
}
catch(err)
{
alert(err.description);
}
}

function emailIframeReady()
{
if (emailIframe.readyState != 'complete')
{
return;
}

emailIframe.contentWindow.document.body.innerHTML = emailBody;
}

emailIframe = document.all.descriptionIFrame;
emailIframe.onreadystatechange = emailIframeReady; }

function OnCrmPageLoad()
{
if (crmForm.FormType == 1)
{
var signature = new Signature(templateId);
signature.Load();
}
}

OnCrmPageLoad();

Anonymous said...

Nope, sorry, the XML is removed

Anonymous said...

Adi Katz, hope you don't mind, posted a solution here that works for me: http://groups.google.com/group/microsoft.public.crm.developer/browse_thread/thread/01e5d051323c1add?hl=en#

almost identical to yours but should overcome the issues people having. Such a good idea, and being in javascript something reasonable none technical people should be able to achieve.

Peter Beckwith said...

This solution works when creating an activity of type email.

However

The create quick campaign function uses the same Signature.Load() function and this is opened in a modal dialog so we have no way (that I have found at least ) to retrieve the current users id and typename.

This means we get a javascript error when loading the quick campaign wizard. Any ideas how I can make this work with the quick campaign dialog as well as the email activity?

Nils Georg said...

@Peter: To make it work with the quick campaign wizard just check if the ownerid is null before you call signature.Load()

if (crmForm.ownerid.DataValue != null) {
var signature = new Signature("12447A5F-0318-E011-BE8C-00155D200B03");
signature.Load();
}

Anonymous said...

Hi, What about CRM 2011, can this code work on it ?

Anonymous said...

modified solution from anonymous above at http://groups.google.com/group/microsoft.public.crm.developer/browse_thread/thread/01e5d051323c1add?hl=en# plus extra line from Nils Georg above to cope with quick campaign error was my perfect solution! Thank you all.

Anonymous said...

Can you post a version of this code for CRM 2011?
Thanks!

aparna john said...

Hi,Designers use image editing software applications to create the graphics for a Web site in Web Design Cochin. Adobe Photoshop and Macromedia Fireworks are two of the most popular image editing applications.Thanks....