How to enable Rich Text in Dynamics CRM using CKEditor


(note:  this was reposted to fix issues in the code from here:  https://blogs.msdn.microsoft.com/peterhauge/2015/10/01/how-to-enable-rich-text-in-dynamics-crm-using-ckeditor/ , so all the comments related to this post are located at the URL above).

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!

UPDATE [4/17/2017]:  Code below updated again to work with forms & more dynamic UI, thanks to Brian Poff for his help! NOTE: In this version you have a few more places to update, search for “description”, your first field in the list should be updated in all the locations. Thanks!

function convertToRichText() {

    var CKIFrame = findCKEditorFieldIFrame("description");

    // 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(CKIFrame).contentWindow.document.getElementsByTagName('head')[0];
    var newscriptblock = parent.parent.document.getElementById(CKIFrame).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 () {

        var CKIFrame = findCKEditorFieldIFrame("description");

        // some configuration for the CKEDITOR rich text control
        var CKE = parent.parent.document.getElementById(CKIFrame).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  div.ms-crm-Inline-Value').style.display = "none";
                parent.parent.document.getElementById(CKIFrame).contentWindow.document.querySelector('div#' + fieldname + ' > div.ms-crm-Inline-Edit').style.display = "inline-block";
                parent.parent.document.getElementById(CKIFrame).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);
}

function findCKEditorFieldIFrame(fieldName) {

    var frameNum = 0;
    var doc = parent.parent.document;

    while (doc.getElementById("contentIFrame" + frameNum.toString()) != undefined) {
        if (doc.getElementById("contentIFrame" + frameNum.toString()).contentWindow.document.getElementById(fieldName) != undefined)
            return "contentIFrame" + frameNum.toString();
        frameNum++;
    }
}


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 (2)

  1. sabir says:

    Hi ,
    This article to use the Rich text can u plz let me know why ur using the look up field in the code, I why we need to have the look up.\\

    Thanks,
    Sabir

    1. Hi Sabir – are you talking about this?
      var fieldsToReplace = [“company_textmodulebackingfield”, “company_textmodule2backingfield”];

      The reason I used an array here was to enable replacing multiple fields with rich text boxes on the same page without needing to modify the code to do it.. Was that your question? If not, could you let me know which part of the code wasn’t clear? I’m happy to help!

Skip to main content