How to enable Rich Text in Dynamics CRM using CKEditor


I recently found myself in the position where we needed to submit notes from customer conversations into Dynamics CRM.  I’m a fan of ‘fancy’ text (headings, bullet points, highlighting) to help make the notes much more readable, but Dynamics CRM doesn’t support this out of the box on their website.  Instead of making my notes less fancy, I opted to just enable this capability in Dynamics CRM!  We will be using the CKEditor in this example, you can find it here:  http://www.ckeditor.com/ .

There are already a few options online for doing this:

In my case, I was looking for something a little simpler where I didn’t need to do synchronization between the nested iFrame and the control that I was trying to replace.  I’m planning on using CKEditor which does a great job replacing controls inline, let’s make that work!

The first step is to create a web resource to hold some JavaScript.  We are going to do everything dynamically in JavaScript so we won’t need any other resources, files, scripts, etc.  You can do this by navigating to the form that you want to change (the one that needs some fancy text!) and open the form editor.  Click the “Form Properties” button on the toolbar, this is where we can add a web resource.  On the dialog that comes up, click “Add”

image

image

On the next dialog we hit “New” to add a new web resource, this is the place where we’ll put our JavaScript to convert controls on the page!

image

On this next dialog (to create a new web resource), we fill out the fields (example below) and can either upload the javascript file or we can paste in the javascript by clicking on the “”text editor”

image

Now comes the interesting part – what JavaScript should we be using anyway?  Well, the first step is importing the CKEditor library.  We can’t just do a straight import because it’s considered cross site scripting (since the JS file resides in another domain) so we need to be a bit more clever to make this work.  There is a way to load another JavaScript file and that’s to insert another “script” element in the “<head>” section in the html.  Once we do that, the browser will load the CKEditor JavaScript file for us to use.

var doc = parent.parent.document.getElementById("contentIFrame0").contentWindow.document;
var headblock = doc.getElementsByTagName('head')[0];
var newscriptblock = doc.createElement('script');
newscriptblock.type = 'text/javascript';
newscriptblock.src = '//cdn.ckeditor.com/4.5.3/standard/ckeditor.js';
headblock.appendChild(newscriptblock);

Line by line, we first grab the ‘head’ element, but you’ll see the ‘parent.parent’ bit in there.  We need this because CRM puts the web resource inside an IFrame, and that IFrame is inside another IFrame as part of the content in the form.  So we use “parent.parent” to go up two levels, then get the main content IFrame so we can start adding elements there.  Next few lines we construct the script block we need, and last line we append that script block to the original head element.

At this point the browser will start loading the script file!  BUT, we have a catch, we can’t just write more JavaScript to start the replacement because we need to first wait for the CKEditor.js file to be loaded (and it’s dependencies).  So, we register an “onload” event before adding the script block that will do the actual replacements.  Like this:

newscriptblock.onload = function() {
    // We put our code here to get rich text
};

In my case, I have several text fields I want to replace, so instead of writing the code for each one separately, I created a string array with all the field names and will loop through.  We also need to do some tweaking on the page when the editor is loaded (hide the ‘value’ version of the field and show the ‘edit’ version of the field), we need to update the underlying field whenever the user types into the Rich Text Editor control, and wrap the whole thing in a function call (so we can call it from the form load method.  All in all the script came together nicely – you can see the full details below!

UPDATE [2/8/2016]:  I updated the code below to work with the latest Dynamics CRM Online, thanks to Anton Kurnitzky for his help!

function convertToRichText() {
    // import the ckeditor script, but do it without web resources..  so we create a script tag in the DOM
    var headblock = parent.parent.document.getElementById("contentIFrame0").contentWindow.document.getElementsByTagName('head')[0];
    var newscriptblock = parent.parent.document.getElementById("contentIFrame0").contentWindow.document.createElement('script');
    newscriptblock.type = 'text/javascript';
    // we have to wait until the script is loaded before we can use it, so registering a callback event
    newscriptblock.onload = function() {
        
        // some configuration for the CKEDITOR rich text control
        var CKE = parent.parent.document.getElementById("contentIFrame0").contentWindow.CKEDITOR;
        CKE.config.allowedContent = true;
        CKE.config.toolbarCanCollapse = true;
        CKE.config.toolbarStartupExpanded = false;
        CKE.config.width = '95%';
         
        var fieldsToReplace = ["description", “detailedstatus”];
        for (var i = 0; i < fieldsToReplace.length; i ++) {
            var fieldname = fieldsToReplace[i];
            // We find the 'edit' control for the engagement overview and replace it with a rich text control
           var richtexteditor = CKE.replace(fieldname + '_i');
            
			richtexteditor.on( 'instanceReady', function()
			{
				parent.parent.document.getElementById("contentIFrame0").contentWindow.document.querySelector('div#' + fieldname + ' > div.ms-crm-Inline-Value').style.display = "none";
				parent.parent.document.getElementById("contentIFrame0").contentWindow.document.querySelector('div#' + fieldname + ' > div.ms-crm-Inline-Edit').style.display = "inline-block";
				parent.parent.document.getElementById("contentIFrame0").contentWindow.document.querySelector('div#' + fieldname + ' > div.ms-crm-Inline-Edit').style.width = "95%";

				(function(field){
				   richtexteditor.on('change', function ( evt ) {
					   // when the value in the rich text control changes, we update the underlying entity field + the 'view' version of the control
					   Xrm.Page.data.entity.attributes.get(field).setValue(richtexteditor.getData());
				   });
				})(fieldname);
			});
        }
    };
    newscriptblock.src = '//cdn.ckeditor.com/4.5.3/standard-all/ckeditor.js';
    headblock.appendChild(newscriptblock);
}

Now that we have the script, we copy/paste this into the “Text Editor” for the web resource we were creating above and click “Save” and close the Web Resource form, then click “Add” on the Look Up Record dialog box (assuming the web resource you just created is selected).  Next, on the “Form Properties” dialog under Event Handlers, we click “Add” to add the function call to the JavaScript we included above.

image

This part is the easy part, we just need a pointer to our function above!  We named our function convertToRichText, so I filled out the dialog like this:

image

Click OK, save the updated form and publish live – you should be good to go!  At this point, when the page loads the text field will be replaced by a rich text field, changes will get automatically applied to the underlying field and you’re all set.  I particularly like this approach (sitting inside the DOM with the rest of the elements instead of inside an IFrame) because you can resize the editor (grab the lower right and drag) to make the editor bigger and all the elements will fall in correctly around it, auto-magically!

image


Comments (24)

  1. Scott Clancy says:

    Nice job Peter this is very cool!

  2. Mike Adler says:

    Great post Peter.  I got it working but I want to add the ability to pick the text color. Is there a way to add the CKEditor plugins with your method?

  3. Sure!  Shouldn't be too hard to add color (in fact, I should do that in mine too. 🙂 ).  It looks like the Color plugin is available in the 'full' distribution of CKEditor – sdk.ckeditor.com/…/colorbutton.html .  So you should swap this line:

               newscriptblock.src = '//cdn.ckeditor.com/4.5.3/standard/ckeditor.js';

    with this one:

               newscriptblock.src = '//cdn.ckeditor.com/4.5.3/full/ckeditor.js';

    An alternative method, if you hosted your own version of the CKEditor package somewhere (with only the add-ins you wanted), you would just change the URL to your hosted location.  That way you would only have the plugins you are planning on using.

  4. Mike Adler says:

    Changing the URL for the editor worked great. I now see the full editor with text color. I have another issue now. When I type in the editor it is not updating the underlying field. So the CRM doesn't think the record is dirty and does not save my changes. Any advice on that one?

  5. Hi Mike – I bet there's something different on your CRM site than mine on how the fields are named.  The code above uses the XRM object model to set the underlying field (which fires the 'change' events that trigger save).

              richtexteditor.on( 'change', function ( field ) {

                  // when the value in the rich text control changes, we update the underlying entity field + the 'view' version of the control

                  Xrm.Page.data.entity.attributes.get(field).setValue(richtexteditor.getData());

               }(fieldname));

    The best way to figure out what's going on is using the browser debugging tools on the site (F12 for Internet Explorer).  You can load the page, search for this line of code in javascript and set a breakpoint.  Type something in the editor and then make sure that Xrm.Page.data.entity.attributes.get(field) returns the correct field (and isn't null)…  My guess is that the field name doesn't match up.  For example, the JQuery selectors will find the field regardless of capitalization, but I think this XRM call is sensitive to correct capitalization of the field.

    Hope that helps, if not let me know!

  6. Patrick Sengeis says:

    Hey Peter,

    thanks for the interresting article. I see the CKEditor in the form, but unfortunately it doesn't save the content. I noticed that everytime I press a key in the editor, I get an "Uncaught TypeError: Cannot read property 'call' of undefined". The error is thrown from ckeditor.js. Currently I'm working with Microsoft Dynamics® CRM 2016. Have you tried the CKEditor with the new CRM already? Or do you have any ideas how to make it working?

    Best regards!

  7. Hi Patrick – good question, I haven't tried this on Dynamics CRM 2016 yet, we are upgrading probably in Jan sometime…  I bet it's because the page is structured slightly different.  I'm happy to help further, but we should probably take the conversation out of blog comments – you can email me directly at peter dot hauge at microsoft dot com if you want to dig further. 🙂  Thanks!

  8. Good news – I just got this working on the latest Dynamics CRM online!  I updated the code above, thanks to Anton Kurnitzky for his help!

  9. Sumeet says:

    Hello Peter,

    Thanks a lot for this!! We were able to get ckeditor working in our crm form using your guidance…

    We have just one issue now…the CRM field shows the HTML markup text…and not the rich text editing when form loads…

    Only when we click on the field…does the Ckeditor showup…any suggestions on how to get around this?

  10. Great – really glad it was helpful!  My best guess is that the script block is loading faster than the other UI elements on the page, but probably can't really diagnose without seeing it.  Feel free to reach out directly (my email is in one of the prior comments) and we can work through it.

    1. Maria says:

      Hi Peter,

      Excellent page! I’d been using the CKEditor via an html webresource previously, but after an upgrade to CRM 2016, this no longer worked. Your tips worked like a charm and was much easier to set up! If only this page had been available when I set it up in the first place 😉

      I have a similar issue as Sumeet – if focus is on any other field than the description field, it reverts to showing the HTML markup text instead of the rich text editor. It’s no big deal, as the editor returns once focus returns to the field, I was just wondering if you might have any ideas as to what is happening?

      Thanks for a great post 🙂

      1. Hi Maria – sorry for the late reply, for some reason your comment wasn’t auto-published but went to the junk spot, I just found it there! This probably has something to do with how the script is setup on the page. I’m happy to help diagnose/debug – feel free to contact me directly at peter dot hauge at microsoft dot com. 🙂

  11. Jake Clauson says:

    This method uses document.getElementById(), rendering it unsupported. Do you have a recommended way of implementing the CKEditor in a supported fashion?

    1. Jake Clauson says:

      Just clarifying that the use of document.getElementById() on its own is not unsupported, but using it to manipulate the form is.

      1. Hi Jake – sorry for the delay responding, your comment ended up in the junk area instead of being auto-published, not sure why! I don’t have a recommendation since the overall approach here is manipulating the DOM which is generally not supported. I found it works well, but it isn’t officially supported.

  12. Idalette de Bruin says:

    Thank you so much for this post it has been extremely useful, and I can see the editor when I load my form. I then tried to change to the full version and when I went into the form again the web resource was not there, but if I go to Web Resources entity it is there. I altered it in the Web Resources entity and published but it has not updated the form. Can you giveme any idea why this would be?

    Many thanks

  13. Idalette de Bruin says:

    I have followed the step above and I have received the following error convertToRichText is undefined at eval code {eval code 1:1)

    Any guidance would be greatly appreciated

    The log file shows this

    ReferenceError: ‘ConvertToRichText’ is undefined
    at eval code (eval code:1:1)
    at RunHandlerInternal (https://sno.crm4.dynamics.com/form/ClientApiWrapper.aspx?ver=1640910717:156:1)
    at RunHandlers (https://sno.crm4.dynamics.com/form/ClientApiWrapper.aspx?ver=1640910717:115:1)
    at Anonymous function (https://sno.crm4.dynamics.com/form/ClientApiWrapper.aspx?ver=1640910717:99:63)
    at Anonymous function (https://sno.crm4.dynamics.com/_static/_common/scripts/MicrosoftAjax.js?ver=1640910717:1:294)

    1. Idalette de Bruin says:

      Using CRM online 2016 fresh installation. I have tried to set the legacy from rendering option, but this has not solved the error
      txs

    2. Idalette de Bruin says:

      Seems to have an error
      SCRIPT1014: Invalid character
      new_ReplaceMyFieldsWithRichText.js (16,47)

    3. Hi Idalette, I saw you sent me email directly, thanks! Just post back here with the solution once we figure it out. 🙂

      1. Neil Benson says:

        I’m getting the same error message as Idalette. Let me know if you find a resolution. (I’m using CRM Online 2016 Update 1 with Project Services Automation).

        1. Hi Neil – Idalette decided to go another route (use another browser for clickable links)… I can help debug, could you drop me an email and we can go from there? peter dot hauge at Microsoft dot com. Thanks!

  14. Nick says:

    This looks promising, but I am getting a “Unable to get property ‘getEditor’ of undefined or null reference” error and the editor controls don’t show up… any ideas? This is for CRM 2013 on-premise.

    1. Hi Nick – I’ve seen that error before when JQuery wasn’t loaded properly, or the CKEditor wasn’t initialized. Feel free to connect with me at peter dot hauge at Microsoft dot com and we can go through it together. Thanks!

Skip to main content