Microsoft Dynamics CRM 4.0 - Thought Repository

Wednesday, April 15, 2009

Create an Outlook Email with Message Body using a CRM Activity

View Comments



Out of the box, Dynamics CRM 4.0 lets you send a link to CRM activity records by clicking Actions->Send Shortcut from the top toolbar. This forces users to open up the link in order to view the contents of an Activity. When an email chain regarding the linked Activity starts to form, it can become a hassle to open up the Activity each time to see the message body. The following javscript code and addition to the isv.config file will simplify matters by giving users an 'Email' button to email the text of the activity.


Start off by exporting your isv.config file. Open up the customizations.xml file and locate the "<Entity name="task">" section. Paste the following code between the "Entity" tags for Task. If the Entity Task tag doesn't exist, copy and paste the whole section of xml below.
<Entity name="task">
  <ToolBar ValidForCreate="0" ValidForUpdate="1">
    <Button Client="Web, Outlook" Icon="../../../ISV/styles/outlookSmall.gif" JavaScript="createOutlookEmail(); " PassParams="1" WinMode="0">
      <Titles>
 <Title LCID="1033" Text=" Email " />
      </Titles>
      <ToolTips>
 <ToolTip LCID="1033" Text="Email Activity using Outlook" />
      </ToolTips>
    </Button>
  </ToolBar>
</Entity>

Download the Outlook icon we use for the button from the file here: Download Zip. You will want to place this Outlook icon inside the "C:\Program Files\Microsoft Dynamics CRM\CRMWeb\ISV\styles" of your CRM 4.0 server. A "styles" directory may not yet exist so please create one and then drop the smallOutlook.gif file in.
Next, we'll customize the Task entity by adding the javascript below to its Form Properties->OnLoad event. The following code uses the Outlook.Application ActiveX control, which provides a mechanism for Internet Explorer to access Outlook APIs. You will notice a FetchXml query in the code, this is used to find a Contact's Account name when the Regarding field of an activity has been set to a Contact. Feel free to change or modify the body of the Email message. I chose to include the Account name, Actual end date, Owner, Activity type, link to activity, Activity subject and Activity description fields but you may find other fields more useful.
//
// export activity to Outlook Email, activated by button in toolbar
//
createOutlookEmail = function() {
 var linkLocation = location.href;   //link to activity
 var emailBody = '';     //contents of body of email      
 
 //ActiveX calls to open new Outlook email template
 var outlookApp = new ActiveXObject('Outlook.Application'); 
 var nameSpace = outlookApp.getNameSpace('MAPI');
 var mailFolder = nameSpace.getDefaultFolder(6); 
 var mailItem = mailFolder.Items.add('IPM.Note.FormA');
 
 if (crmForm.all.regardingobjectid.DataValue) {
  //regarding is set to an account
  if (crmForm.all.regardingobjectid.DataValue[0].type == 1) {
   emailBody = 'Account name: '
     + crmForm.all.regardingobjectid.DataValue[0].name;
  }
  //regarding is set to a contact
  else if (crmForm.all.regardingobjectid.DataValue[0].type == 2) {
   //write fetchxml statement to query for contact's parent account name
   var fetchQuery = '<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="true" page="1" count="1"><entity name="contact"><attribute name="contactid"/><filter type="and"><condition attribute="contactid" operator="eq" uitype="contact" value="'
     + crmForm.all.regardingobjectid.DataValue[0].id
     + '"/></filter><link-entity name="account" from="accountid" to="parentcustomerid" visible="false" link-type="outer" alias="contactparentcustomeridaccountaccountid"><attribute name="name"/></link-entity></entity></fetch>';
   //package fetchxml in xml statement
   var xmlQuery = '<?xml version="1.0" encoding="utf-8"?>'
     + '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">'
     + GenerateAuthenticationHeader()
     + '  <soap:Body>'
     + '<Fetch xmlns="http://schemas.microsoft.com/crm/2007/WebServices">'
     + '<fetchXml>' + _HtmlEncode(fetchQuery) + '</fetchXml>'
     + '</Fetch></soap:Body></soap:Envelope>';

   //package xml into ajax soap request 
   var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
   xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
   xmlHttpRequest.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Fetch");
   xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
   xmlHttpRequest.setRequestHeader("Content-Length", xmlQuery.length);
   xmlHttpRequest.send(xmlQuery);
   
   //format xml response to replace esacaped characters 
   var resultXml = xmlHttpRequest.responseXML.xmlQuery.toString();
   resultXml = resultXml.replace(/&lt;/g, "<");
   resultXml = resultXml.replace(/&gt;/g, ">");
   resultXml = resultXml.replace(/&amp;/g, "&");

   //load xml response into xml parser 
   var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
   xmlDoc.async = "false";
   xmlDoc.loadXML(resultXml);

   //parse xml response to find account name node and add to emailBody
   var entityNodes = xmlDoc.selectNodes("//FetchResult/resultset/result");
   var descriptionNode = '';
   for (var i = 0; i < entityNodes.length; i++) {
    var entityNode = entityNodes[i];
    descriptionNode = entityNode
      .selectSingleNode("contactparentcustomeridaccountaccountid.name");
    emailBody = 'Account name: ';
   }
   //add contact name to emailBody
   emailBody += descriptionNode.text + '\nContact name: '
     + crmForm.all.regardingobjectid.DataValue[0].name;
  }
  emailBody = emailBody + '\n';
 }

 //escape '{' and '}' symbols in link to activity
 linkLocation = linkLocation.replace(/{/g, '%7b');
 linkLocation = linkLocation.replace(/}/g, '%7d');

 //set subject of Outlook email message
 mailItem.Subject = crmForm.all.subject.value;
 
 //set body of Outlook email message
 mailItem.Body = emailBody
   + 'Actual End Date: '
   + crmForm.all.actualend.DataValue
   + '\nOwner: '
   + crmForm.all.ownerid.DataValue[0].name
   + '\nActivity Type: Task\nLink: '
   + linkLocation
   + '\n\nSubject: '
   + crmForm.all.subject.value
   + '\n\nDescription: '
   + (crmForm.all.description.DataValue == null
    ? ''
    : crmForm.all.description.DataValue);
     
 //show final Outlook email on user's screen     
 mailItem.display(0);
}

The final step is to enable ActiveX controls to run within your intranet. By default Internet Explorer is configured to block ActiveX controls as they can allow malicious virus writers to potentially take over your desktop. However, since our instance of Dynamics CRM 4.0 is on premise, we can safely choose to enable ActiveX controls on our local intranet. Follow the steps below to enable the setting for "Initialize and script ActiveX controls not marked safe for scripting." Make sure to select the Local Intranet icon in step 1, otherwise you may make your computer vulnerable to ActiveX script attacks. If you have a large organization, it may be helpful to script out the change in one blast.
In the above example I used the Task entity. If you want the Email button to appear for other Activity entities, you will need to add the javascript above to the Form Properties->OnLoad event of the other entities as well. In addition, you must also copy the xml code above and paste it into the other "<Entity></Entity>" sections for the other Activity types.

blog comments powered by Disqus

About Me

Henry Bow
I'm a programmer living in sunny Orange Country, California. Since the beginning of 2008, I've been developing on the MS Dynamics CRM 4.0 platform. This blog will help me jot down some of the tips and neat features I've developed along the way while giving me a chance to dabble into the curious world of analytics and SEO.

Please let me know if I can help with your CRM needs.
hbow27@gmail.com

Feed Rss

Subscribe to new posts via e-mail

Recent Posts