October 25, 2012

Add a custom button in the rich text editor

For one of my tasks I had to add a custom button in the sitecore rich text editor (RADEditor) to open a dialog box. But we don’t find a lot of documentation about that on the web. The only post I have found about this is this one: http://www.awareweb.com/AwareBlog/9-21-11-HyperlinkMgr.aspx which is a good start but with this method we need to decompile the code of the RADEditor and override the Rich Text Editor javascript.

Here is another method to create a button without overriding the code for sitecore and RADEditor.

First of all, we will add the new button in the interface. As you may know they are different profiles for the rich text. A profile define which features and button are available for a specific field. By default, the selected profile in the “Rich Text Default” but you may specify which profile you want need to be apply by setting the datasource of the field. (example: /sitecore/system/Settings/Html Editor Profiles/Rich Text Full). In this example, I will add a button in the Toolbar 1 of the Full profile.

So, in the core database, add an item in of type /sitecore/templates/System/Html Editor Profiles/Html Editor Button below /sitecore/system/Settings/Html Editor Profiles/Rich Text Full/Toolbar 1. Assign an icon to you item and in the field “Click” add an id for your action. In this example the field “Click” is set to “TestBtn”.

Now, we need to handle this action in the javascript. By default, those actions are handled in \sitecore\shell\Controls\Rich Text Editor\RichText Commands.js but I don’t want to modify the default file to stay as updatable as possible with the future versions of sitecore. By using my friend Reflector, I have found a work around. You may add some javascript in the Rich Text Editor IFrame by adding it in the config below the node /configuration/sitecore/Clientscript/htmleditor. So for my example, add this config file in \App_Config\Include:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <clientscripts>
      <htmleditor>
        <script src="/sitecore/shell/Controls/Rich Text Editor/TestBtn/TestBtn Commands.js" language="JavaScript"/>
      </htmleditor>
    </clientscripts>
  </sitecore>
</configuration>

And the content of this new javascript file located into /sitecore/shell/Controls/Rich Text Editor/TestBtn/TestBtn Commands.js is:

var scEditor = null;
var scTool = null;

//Set the Id of your button into the RadEditorCommandList[]
RadEditorCommandList["TestBtn"] = function (commandName, editor, args) {
    var d = Telerik.Web.UI.Editor.CommandList._getLinkArgument(editor);
    Telerik.Web.UI.Editor.CommandList._getDialogArguments(d, "A", editor, "DocumentManager");

 //Retrieve the html selected in the editor
    var html = editor.getSelectionHtml();

    scEditor = editor;

 //Call your custom dialog box
    editor.showExternalDialog(
  "/sitecore/shell/default.aspx?xmlcontrol=RichText.TestBtn&la=" + scLanguage,
  null, //argument
  500, //Height
  180, //Width
  scTestBtnCallback, //callback
  null, // callback args
  "TestBtn",
  true, //modal
  Telerik.Web.UI.WindowBehaviors.Close, // behaviors
  false, //showStatusBar
  false //showTitleBar
   );
};

//The function called when the user close the dialog
function scTestBtnCallback(sender, returnValue) {
    if (!returnValue) {
        return;
    }
 
 //You may retreive some code from your returnValue
 
 //For the example I add Hello and my return value in the Rich Text
    scEditor.pasteHtml("Hello " + returnValue.text, "DocumentManager");
}

And the dialog as a XAML form located into /sitecore/shell/Controls/Rich Text Editor/TestBtn/TestBtn.xml is:

<?xml version="1.0" encoding="utf-8" ?>
<control xmlns:def="Definition" xmlns="http://schemas.sitecore.net/Visual-Studio-Intellisense">
  <RichText.TestBtn>
    <FormDialog Icon="Network/32x32/link.png" Header="Insert a html code"
      Text="Insert a html code." OKButton="Link">

      <script Type="text/javascript" Language="javascript" Src="/sitecore/shell/Controls/Rich Text Editor/TestBtn/TestBtn.js">.</script>

      <CodeBeside Type="MyNameSpace.TestBtn, MyDll"/>

      <Border Padding="4px 0px 4px 0px">
        <GridPanel Width="100%" Columns="2">
          <Border Padding="0px 4px 0px 0px">
            <Literal Text="Enter you text:"/>
          </Border>

          <Edit ID="TextEdit" Width="100%" GridPanel.Width="100%"/>
        </GridPanel>
      </Border>
    </FormDialog>
  </RichText.TestBtn>
</control>

The associate cs file. This file is located into /sitecore/shell/Controls/Rich Text Editor/TestBtn/TestBtn.cs is:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Sitecore;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.IO;
using Sitecore.Shell.Framework;
using Sitecore.Web;
using Sitecore.Web.UI.HtmlControls;
using Sitecore.Web.UI.Pages;
using Sitecore.Web.UI.Sheer;

namespace MyNamespace
{
    public class TestBtn : DialogForm
    {
        // Fields
        protected Edit TextEdit;

        protected override void OnCancel(object sender, EventArgs args)
        {
            Assert.ArgumentNotNull(sender, "sender");
            Assert.ArgumentNotNull(args, "args");
            if (this.Mode == "webedit")
            {
                base.OnCancel(sender, args);
            }
            else
            {
                SheerResponse.Eval("scCancel()");
            }
        }
        
        protected override void OnOK(object sender, EventArgs args)
        {
            Assert.ArgumentNotNull(sender, "sender");
            Assert.ArgumentNotNull(args, "args");
            if (string.IsNullOrWhiteSpace(TextEdit.Value))
            {
                SheerResponse.Alert("Please enter a html code.", new string[0]);
            }

            if (this.Mode == "webedit")
            {
                //SheerResponse.SetDialogValue(StringUtil.EscapeJavascriptString(mediaUrl));
                base.OnOK(sender, args);
            }
            else
            {
                SheerResponse.Eval("scClose('" + TextEdit.Value + "')");
            }
        }

        // Properties
        protected string Mode
        {
            get
            {
                string str = StringUtil.GetString(base.ServerProperties["Mode"]);
                if (!string.IsNullOrEmpty(str))
                {
                    return str;
                }
                return "shell";
            }
            set
            {
                Assert.ArgumentNotNull(value, "value");
                base.ServerProperties["Mode"] = value;
            }
        }
    }
}

Associate to this XAML form, I also have this javascript to handle the close and cancel (to only interesting method is the scClose and scCloseWebEdit). This file is located into /sitecore/shell/Controls/Rich Text Editor/TestBtn/TestBtn.js

function GetDialogArguments() {
    return getRadWindow().ClientParameters;
}

function getRadWindow() {
  if (window.radWindow) {
        return window.radWindow;
  }
    
    if (window.frameElement && window.frameElement.radWindow) {
        return window.frameElement.radWindow;
    }
    
    return null;
}

var isRadWindow = true;

var radWindow = getRadWindow();

if (radWindow) { 
  if (window.dialogArguments) { 
    radWindow.Window = window;
  } 
}


function scClose(text) {
    var returnValue = {
        text: text
    };

    getRadWindow().close(returnValue);

}

function scCancel() {
  getRadWindow().close();
}

function scCloseWebEdit(text) {
    window.returnValue = text;
    window.close();
}

if (window.focus && Prototype.Browser.Gecko) {
  window.focus();
}

UPDATE:
We have found that if the command contains some special characters like ':' the button icon may be not loaded correctly. So just avoid it :-)

8 comments:

  1. nice work with the client script includes in the config.

    ReplyDelete
  2. HI Benjamin, Hope you are doing great! I also having similar requirement. I am facing some using. First my javascript file TestBtn Commands.js not showing in the Network source and not called yet and displayed error message "TestBtn Action was not implemented yet". I am using 8.1 update 2 and VS 2013 with framework 4.5. Please let me know which version you are using and do you have any idea why this error is coming? This will be really helpful.

    ReplyDelete
  3. Hi,

    This post has been writen in 2012 so I would say 6.5. Sitecore has evelove a lot since this version especially with speak.
    My post: http://sitecoreblog.blogspot.be/2016/01/deep-drive-into-sitecore-client.html could help you to understand how Sitecore implement the javascripts now.
    Your javasctipt is probably not included. Did you already check with chrome if the ressource is loaded?

    REgards,

    ReplyDelete
    Replies
    1. Hi Benjamin!
      Now that Sitecore 9 is in full swing have you implemented custom buttons there yet?

      Thanks!!
      Larry

      Delete
    2. I should also mention we're upgrading our site and making use of Helix.

      Delete
    3. No sorry I didn't have implemented such kind of button in Sitecore 9...

      Delete
  4. Hi Benjamin,
    I am getting error message "Testbtn was not impleted yet". the issue is RadEditorCommandList is not defined. Where to configure this RadEditorCommandList?

    Thanks,
    Renuga K.

    ReplyDelete
    Replies
    1. Instead of using RadEditorCommandList["TestBtn"] try Telerik.Web.UI.Editor.CommandList['TestBtn']

      Delete