Adding a button to Rich Text Editor in Sitecore

February 05, 2011

Sitecore is an incredibly good platform that provides a lot of functionality out of the box, but one of the great things about Sitecore is that if there is something custom that you'd like to add in, there's always a way to do it. I'm going to walk through the process of adding a custom piece of functionality to Sitecore's Rich Text Editor. There are a lot of reasons you would want to do this but for the sake of this example we'll build a simple text insertion demo that you can expand upon.

To start you'll need to create a button in the wysiwyg toolbar. You will need to switch over to the core database and browse to /sitecore/system/Settings/Html Editor Profiles. It's up to you where you end up creating the button but for now I'm just going to go into /sitecore/system/Settings/Html Editor Profiles/Rich Text Full/Toolbar 1 and create an "__Html Editor Button" and name it InsertText. Once you've got your button you're going to want to go to the "configure" tab at the top and change the icon to something recognizeable. Make sure that when you've selected the image it's 16x16 otherwise it won't look right in the wysiwyg. Next you'll want to add in a value for the "Click" field. This value will be used in javascript to call your function we're going to stay consistent and give it the value "InsertText". Now go back into the master database and open a Rich Text Editor field with the correct profile to view the button and ensure it's displaying.

Next thing to do is create the javascript method that will launch your modal window. In Visual Studio or whatever editor you're using open /sitecore/shell/controls/Rich Text Editor/RichText Commands.js. This file is included when the wysiwyg is launched and will need to contain your code or a reference to it to support your button. Add in the following code:

 

RadEditorCommandList["InsertText"] = function(commandName, editor, tool) {	var args = editor.GetDialogParameters(commandName);	var html = editor.GetSelectionHtml(); 	scEditor = editor;	editor.ShowDialog(		"/sitecore/shell/default.aspx?xmlcontrol=RichText.InsertText&la=" + scLanguage + "&selectedText=" + escape(html),		null, //argument		600, //width		500, //height		scInsertText,		null,		"Insert Text");};function scInsertText(returnValue) {	if (returnValue) {		scEditor.PasteHtml(returnValue.text);	}}

 

The first thing you'll notice is the "RadEditorCommandList["InsertText"] = function". This is where you're defining the function that is referenced in the "Click" field of your button in Sitecore. You'll also want to know that you have an editor object that will be giving you access to the selected text from the wysiwyg field. The most important thing you'll want to do in this function aside from gathering any data from the selected text (such as querystring params from a url) is to call your dialog window. In this case our dialog window will be leveraging Sitecore's sheer UI. If you haven't heard of this already, Sitecore has a tutorial here. It will help you understand the basics. The "editor.ShowDialog" function calls the /sitecore/shell/default.aspx file with the xmlcontrol RichText.InsertText and selected text from the wysiwyg as params. You also have the ability to set the height, width and callback method. Before we get to the xml control let's go over the callback method first. When you're leaving you're xml control you'll be calling another javascript method that will define your returnValue object. The returnValue object is passed to your callback method and you can then process the values. In this case I'm just interested in pasting the returned text back into the wysiwyg editor. In some cases you'll find that you're returning a value that is intended for the html view and needs to be converted to design view. You can use this callback function to handle converting your text before pasting it. A function to handle the conversion would look like this:

 

function scConvertText(html) {	try {		var win = window.dialogArguments[0];	}catch (e) {win = window;}	var form = win.scForm;	if (form != null) {		var request = new win.scRequest();		request.form = "html=" + encodeURIComponent(html);		request.build("", "", "", 'Convert("' + scMode + '")', true);		var url = "/sitecore/shell/Applications/Content Manager/Execute.aspx?cmd=Convert&mode=" + scMode;		request.url = url;		request.send();		var r = "";		if (request.httpRequest != null && request.httpRequest.status == "200") {			r = request.httpRequest.responseText;		}		return r;	} }

 

This is making a call to another utility that will do the conversion for you and return the text.

Now we need to create your dialog window. In the /sitecore/shell/controls/Rich Text Editor/ folder copy the existing InsertLink folder and name it InsertText. Inside there you'll see two files: InsertLink.js and InsertLink.xml. You'll want to rename both of them to InsertText.js and InsertText.xml respectively. Open the InsertText.js file and modify the "scClose" function to look like this:

 

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

 

This is the function that you're xml control will call that defines you're return value which is passed to your callback function. Here we're just setting the text property on the returnValue. Now save and close that and open the InsertText.xml file. This is where we're using Sitecore's sheer UI. This xml file will be the designed layout of your dialog window. It will also reference a class file from a binary library that will be used to control the events. You can overwrite the existing content with this:

 

<control xmlns:def="Definition" xmlns="http://schemas.sitecore.net/Visual-Studio-Intellisense">	<richtext.inserttext>		<formdialog icon="Network/32x32/link.png" header="Insert Text" text="Insert the text you want." okbutton="Insert">			<script type="text/javascript" language="javascript" src="/sitecore/shell/RadControls/Editor/Scripts/7_2_0/RadWindow.js">.</script>			<script type="text/javascript" language="javascript" src="InsertCode/InsertText.js">.</script>			<codebeside type="Library.ClassName,BinaryFileName"></codebeside>			<gridpanel width="100%" height="100%" style="table-layout:fixed">      				<memo id="memoText" style="height:100%;width:100%;border-top:1px solid #919b9c"></memo>			</gridpanel>		</formdialog>	</richtext.inserttext></control>

 

You'll notice the "RichText.InsertText" tag wrapping most of the content. This will line up with the name of the xmlControl called in the javascript dialog function. Here we're just defining the class that handles the events, including some functional javascript, a submit button and a text (Memo) field to capture the text you want to enter.

Now that the UI of the dialog is setup you're going to have to create the class that is referenced in the UI. The class will look like this:

 

public class InsertTextForm : DialogForm { 	// Front End Object Reference  	protected Sitecore.Web.UI.HtmlControls.Memo memoText;  	protected override void OnLoad(EventArgs e) {		Assert.ArgumentNotNull(e, "e");		base.OnLoad(e);		if (!Context.ClientPage.IsEvent) {			this.Mode = WebUtil.GetQueryString("mo"); 			string text = WebUtil.GetQueryString("selectedText");			//set textbox text to selected text from the wysiwyg			memoText.Value = text;		}	}	protected override void OnOK(object sender, EventArgs args) {		Assert.ArgumentNotNull(sender, "sender");		Assert.ArgumentNotNull(args, "args");		//send it back		if (this.Mode == "webedit") {			SheerResponse.SetDialogValue(StringUtil.EscapeJavascriptString(memoText));			base.OnOK(sender, args);		} else {			//call javascript method in InsertText.js    			SheerResponse.Eval("scClose(" + StringUtil.EscapeJavascriptString(memoText) + ")");		}	}	// Local property accessor	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;		}	} }

 

All right, now you're going to want to compile your class and restart your browser so that any javascript used isn't cached. Then open up you're wysiwyg editor and click the InsertText button. You'll see your dialog window popup and you can enter some text which, when you click the "Insert" button will insert the text back into the wysiwyg editor.

Now this example is pretty generic and by now you're asking yourself, "why would I ever do that?" In this particular instance I was demonstrating how I insert code blocks into my text. I use the Memo field to format the code and on the "OK" click I wrap it in a <pre> tag with a "Code" class. I realize that inserting text by means of another text field is entirely useless but as a demo it's as simple as you can get for something that requires so many moving parts. It should however show you how all the pieces are tied together and allow you customize the code as needed to insert information compiled by accessing the content tree and process selected text from the wysiwyg.

There will be times when your client/manager will want you to be able to insert some functional content inline on the body of the main text area. This will provide that capability. You could simply replace the Memo field in this instance with a treelist have a user select an item and then output a server control tag. If you're unfamiliary with a server control you can read on MSDN about it or go for a tutorial here first, but the basic premise is that you design a UI control like a Literal and display something yourself. The reason I would suggest that path is that the wysiwyg editor will display a server control like an object that you can select and change the attributes on. A very little known but powerful feature in Sitecore.