HTML Editor for Microsoft Dynamics CRM 4.0

Until Microsoft Dynamics 5.0 to comes out the only way to get a HTML or Rich Text editor capability in Microsoft Dynamics CRM 4.0 is to roll your own or use a third party application.  I have had tremendous success using the TinyMCE  editor.  click here to download The editor has a plethora of features available including everything from spell check to word count and is available in a number of  build format and examples including;   jQuery, JavaScript, PHP, .Net and JSP.  I prefer to use the jQuery build for my Microsoft Dynamics CRM 4.0 implementations.

Below I have included a code sample you can use in your custom iFrame.  Accessing the raw HTML source during the CRM onSave event was a little bit tricky, here’s the code for that.

crmForm.all.IFRAME_Accomplishment.contentWindow.tinymce.getInstanceById('elm1').getBody().innerHTML

You can either make a call to the CRM SDK and save this value to the database or use javascript and simply copy to a hidden  nText  field located  directly on the form next to the iFrame.  All we have to do is save and load the raw HTML and the HTML editor does the rest.

I use a FetchXML function to load the raw HTML…

$('#elm1').val(GetContacts());

The GetContacts function simply uses FetchXML to retrieve the value of a nText attribute I have added to the Contacts entity.

Here’s a page that instantiates a HTML editor in a iFrame for use in Microsoft Dynamics CRM 4.0.

<head>
<title>Full featured Microsoft Dynamics CRM 4.0 tinyMCE example using jQuery plugin by ExtremeCRM.net</title>

<!-- Load jQuery -->
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
 google.load("jquery", "1");
</script>

<!-- Load TinyMCE -->
//****** REMEMBER TO CHANGE THIS PATH TO THE PROPER ISV LOCATION  *******//
<script type="text/javascript" src="../jscripts/tiny_mce/jquery.tinymce.js"></script>
<script type="text/javascript">
 $().ready(function() {
 $('textarea.tinymce').tinymce({
 // Location of TinyMCE script
 //****** REMEMBER TO CHANGE THIS PATH TO THE PROPER ISV LOCATION  *******//
 script_url : '../jscripts/tiny_mce/tiny_mce.js',

 // General options
 theme : "advanced",
 plugins : "pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions,iespell,inlinepopups,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template,advlist",

 // Theme options
 theme_advanced_buttons1 : "save,newdocument,|,bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,styleselect,formatselect,fontselect,fontsizeselect",
 theme_advanced_buttons2 : "cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numlist,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code,|,insertdate,inserttime,preview,|,forecolor,backcolor",
 theme_advanced_buttons3 : "tablecontrols,|,hr,removeformat,visualaid,|,sub,sup,|,charmap,emotions,iespell,media,advhr,|,print,|,ltr,rtl,|,fullscreen",
 theme_advanced_buttons4 : "insertlayer,moveforward,movebackward,absolute,|,styleprops,|,cite,abbr,acronym,del,ins,attribs,|,visualchars,nonbreaking,template,pagebreak",
 theme_advanced_toolbar_location : "top",
 theme_advanced_toolbar_align : "left",
 theme_advanced_statusbar_location : "bottom",
 theme_advanced_resizing : true,

 // Example content CSS (should be your site CSS)
 //****** REMEMBER TO CHANGE THIS PATH TO THE PROPER ISV LOCATION  *******//
 content_css : "css/content.css",

 // Drop lists for link/image/media/template dialogs
 template_external_list_url : "lists/template_list.js",
 external_link_list_url : "lists/link_list.js",
 external_image_list_url : "lists/image_list.js",
 media_external_list_url : "lists/media_list.js",

 // Replace values for the template plugin
 template_replace_values : {
 username : "Some User",
 staffid : "991234"
 }

 });

 //Here we load the input textbox named elm1.  This input text box will be overloaded by the tinyMCE editor at runtime.
 //See the GetContacts function below.
 $('#elm1').val(GetContacts());

 });
</script>
<!-- /TinyMCE -->

</head>
<body>

 <form id="form1" runat="server">
 <div>
 <h3>Full featured example using jQuery plugin</h3>

 <p>
 This example shows how TinyMCE can be used from Microsoft Dynamics CRM 4.0  using jQuery. The jQuery plugin will also attach it's self to various jQuery methods to make it more easy to get/set editor contents etc.
 </p>

 <!-- Gets replaced with TinyMCE, remember HTML in a textarea should be encoded -->
 <div>
 <textarea id="elm1" name="elm1" rows="15" cols="80" style="width: 80%">
 &lt;p&gt;
 This is some example text that you can edit inside the &lt;strong&gt;TinyMCE editor&lt;/strong&gt;.
 &lt;/p&gt;
 &lt;p&gt;
 ExtremeCRM provides developers coding examples, insights, concepts and options for developing customizations for Microsoft CRM 4.0.  The primary contributor, Brenden Smith is a Microsoft MCP and Microsoft CRM 4.0 Lead developer for a federal agency in Washing D.C.  If you would like to be a contributor for extremeCRM.net
 &lt;/p&gt;
 </textarea>
 </div>

 <!-- Some integration calls -->
 <a href="javascript:;" onmousedown="$('#elm1').tinymce().show();">[Show]</a>
 <a href="javascript:;" onmousedown="$('#elm1').tinymce().hide();">[Hide]</a>
 <a href="javascript:;" onmousedown="$('#elm1').tinymce().execCommand('Bold');">[Bold]</a>
 <a href="javascript:;" onmousedown="alert($('#elm1').html());">[Get contents]</a>
 <a href="javascript:;" onmousedown="alert($('#elm1').tinymce().selection.getContent());">[Get selected HTML]</a>
 <a href="javascript:;" onmousedown="alert($('#elm1').tinymce().selection.getContent({format : 'text'}));">[Get selected text]</a>
 <a href="javascript:;" onmousedown="alert($('#elm1').tinymce().selection.getNode().nodeName);">[Get selected element]</a>
 <a href="javascript:;" onmousedown="$('#elm1').tinymce().execCommand('mceInsertContent',false,'<b>Hello world!!</b>');">[Insert HTML]</a>
 <a href="javascript:;" onmousedown="$('#elm1').tinymce().execCommand('mceReplaceContent',false,'<b>{$selection}</b>');">[Replace selection]</a>

 <br />
 <input type="submit" name="save" value="Submit" />
 <input type="reset" name="reset" value="Reset" />
 </div>
</form>
<script type="text/javascript">
if (document.location.protocol == 'file:') {
 alert("The examples might not work properly on the local file system due to security settings in your browser. Please use a real webserver.");
}
</script>
</body>
</html>

Attaching Javascript code to a CRM Toolbar Button

Attaching Javascript code to a tool bar button can be very ugly.  Generally the code needs to be included within the CRM, ISV Config  XML file.  It’s pretty easy to add a button via the ISV Config.  Click Here for more information on adding custom buttons to a Microsoft Dynamics CRM Entity form, toolbar.

The red highlighted Javascript tag below shows you where you would typically place your JavaScript or JavaScript function call but this isn’t the best place for the JavaScript.  I have seen huge JavaScript routines embedded in to the tag whci makes debugging and readability a painful exercise.  Even if you call a JavaScript function and leave the tag nice and cleas as shown here that Javascript function has to be maintained outside of the CRM Model.

<Entities>
        <Entity name=”new_customentity”>
          <ToolBar ValidForCreate=”0″ ValidForUpdate=”1″>
            <ToolBarSpacer></ToolBarSpacer>
            <Button Icon=”/_imgs/ico_18_debug.gif” JavaScript=”
DuplicateRecord (10016);”>
              <Titles>
                <Title LCID=”1033″ Text=”Duplicate Record” />
              </Titles>
              <ToolTips>
                <ToolTip LCID=”1033″ Text=”Copies a record based on this open record.” />
              </ToolTips>
            </Button>
            <ToolBarSpacer />
          </ToolBar>
        </Entity>
      </Entities>
 

The solution is pretty simple.

If we change the structure of our JavaScript function a bit we can simply place it in a Entity’s form OnLoad Event.  The key syntax is simply  MyFunction = function() .

Here is a working example of a function I wrote which calls a web service to copy the  current open record and create a new one based on it,

The CRM Form OnLoad Event JavaScript

DuplicateRecord = function() {

    try {
        var oXmlHTTP = new ActiveXObject(“Msxml2.XMLHTTP”);
       oXmlHTTP.Open(“POST”, “/somepath/ DuplicateRecord Service.asmx/ DuplicateRecord?mso-spacerun: yes”>      + crmForm.ObjectId, false);
        oXmlHTTP.setRequestHeader(“Content-Type”, “application/x-www-form-urlencoded”)
        oXmlHTTP.Send(‘jobId=’ + crmForm.ObjectId);
        var newId = oXmlHTTP.responseXML.selectSingleNode(“string”).text;
   }
    catch (e) {
        debugger;
        return null;
    }
}

 

The ISV Config  XML Tag

<Button Icon=”” Url=”” PassParams=”1″ WinParams=”” WinMode=”0″ JavaScript=” DuplicateRecord ();”>
              <Titles>
                <Title LCID=”1033″ Text=” Duplicate This Record ” />
              </Titles>
              <ToolTips>
                <ToolTip LCID=”1033″ Text=”Copy Record” />
              </ToolTips>
            </Button>
 

Conclusion

I like this approach because now all our JavaScript is still maintained within the CRM Entity’s and for a number of good reasons that’s where a Microsoft Dynamics CRM  application should store the JavaScript.  Because its with the entity it will get exported with your model and other customizations.

Ever need to debug a web service from the production server rather than Visual Studio?

Ever need to debug a web service from the production server rather than Visual Studio?  Well it’s a pain to do so because for security reasons Microsoft disables the parameter inputs and replaces it with this message.

Test

The test form is only available for requests from the local machine.

The solution for this is simple, add the following tags to the   <system.web> section of the web.config.

        <webServices>

          <protocols>

            <add name=”HttpGet”/>

            <add name=”HttpPost”/>

          </protocols>

        </webServices>

Now when you load the asmx page it will allow you to enter the required parameters to run your web service.