Sitecore for Developers
Add Custom List Button in Sitecore RTE
A client requested a new button in the rich text editor that would work exactly like an unordered list, but would look different based on a class that would be added to the unordered list. Since the RTE is a telerik control it took a lot of digging through javascript in the "\Website\sitecore\shell\Controls\Rich Text Editor" folder and Chrome web dev tools to find what we needed. The solution ended up being a custom button to replace the unordered list, a custom button for the new list, and javascript to handle button states. We need to replace the unordered list button to deal with button states - any time an unordered list is clicked in the RTE the Insert Unordered List button will show as the selected state. We need to take control of this so we know whether we’re in a normal unordered list or our new custom list button.
The first step is to create our buttons. In core db, /sitecore/system/Settings/Html Editor Profiles find your RTE and Toolbar with the ordered lists. You’ll need to create two Html Editor Buttons and set the Click field values, and get rid of the “Insert Unordered List”. I set the Click field to "InsertNewUnorderedList" for the replacement unordered list button and "InsertCustomUnorderedList" for the custom list. You will want to add a new icon for the custom list, and do nothing with the new unordered list icon. We don’t want to change this icon because we want it to look the same as the regular button. In order for that to happen you need to add css to the Default.css, targeting your new click value (Sitecore will set the class up for you) so we can recreate what the unordered button is already doing:
When you bring up your RTE you should now see the custom unordered button looks the same and the new button should be there as well
Now for the fun part. We need to first get the list functionality working for the buttons. We’ll use the “RichText Commands.js” in \Website\sitecore\shell\Controls\Rich Text Editor. We can see from the other functions in here what our function will look like, we’re using the command list to grab our Click value. Next we insert this function into "RichText Commands.js":
[code language="javascript"]RadEditorCommandList["InsertCustomUnorderedList"] = function(commandName, editor, tool) { }[/code]After some digging in the telerik js we can see what the unordered button is doing, so we will finish out the previous function like this:
[code language="javascript"]RadEditorCommandList["InsertCustomUnorderedList"] = function(commandName, editor, tool) { editor.setFocus(); var listType = "InsertUnorderedList"; editor.executeCommand(new Telerik.Web.UI.Editor.InsertListCommand(editor.getLocalizedString(listType), editor.get_contentWindow(), editor.get_newLineMode() == Telerik.Web.UI.EditorNewLineModes.Br, listType, null, editor));}[/code]
Now we need to do our custom work and worry about the button states. We can hook into some functions in “RTEfixes.js” which is in the same folder as above. We're going to make our OnClientCommandExecuted function look like this:
[code language="javascript"]function OnClientCommandExecuted(sender, args) { var li = "LI"; var ul = "UL"; var cTool = sender.getToolByName("InsertNewUnorderedList"); if (args.get_commandName() === "SetImageProperties") { replaceClearImgeDimensionsFunction(); } else if (args.get_commandName() === "InserCustomUnorderedList") { var selElem = sender.getSelectedElement(); if (selElem.nodeName !== li && selElem.nodeName !== ul) return; if (selElem.nodeName === li) { selElem = selElem.parentNode; } selElem.className = "myCustomClassName"; var kTool = sender.getToolByName("InsertCustomUnorderedList"); kTool.setState(1); cTool.setState(0); } else if (args.get_commandName() === "InserNewUnorderedList") { var selElem2 = sender.getSelectedElement(); if (selElem2.nodeName !== li && selElem2.nodeName !== ul) return; cTool.setState(1); }}[/code]
For the custom button we need to add a class to the ul and then set our button states. We should be in the list, but I have a check in there to make sure. For the new unordered list button we just need to set the button state:
For button states we need to add some code to the OnClientSelectionChange function in "RTEfixes.js". We access the states of the buttons by getting their “tool name” by click value. Then we’ll set the correct button state based off of the presence of the class on the ul. The resulting function looks like this:
[code language="javascript"]function OnClientSelectionChange(editor, args) { var tool = editor.getToolByName("FormatBlock"); if (tool) { setTimeout(function() { var defaultBlockSets = [ ["p", "Normal"], ["h1", "Heading 1"], ["h2", "Heading 2"], ["h3", "Heading 3"], ["h4", "Heading 4"], ["h5", "Heading 5"], ["menu", "Menu list"], ["pre", "Formatted"], ["address", "Address"] ];
var value = tool.get_value(); var block = Prototype.Browser.IE ? defaultBlockSets.find(function(element) { return element[1] == value; }) : [value];
if (block) { var tag = block[0]; var correctBlock = editor._paragraphs.find(function(element) { return element[0].indexOf(tag) > -1; }); if (correctBlock) { tool.set_value(correctBlock[1]); } } }, 0); }
var selElem = editor.getSelectedElement(); var kTool = editor.getToolByName("InsertCustomUnorderedList"); var cTool = editor.getToolByName("InsertNewUnorderedList"); kTool.setState(0); cTool.setState(0); if (selElem.nodeName === ul || selElem.nodeName === li) { if (selElem.nodeName === li) { selElem = selElem.parentNode; }
if (selElem.className === "myCustomClassName") { kTool.setState(1); } else { cTool.setState(1); } }}[/code]
Now both buttons should insert a list and we will know which list we’re in because the button states are correct. You may notice that you can still use the buttons interchangeably but this is as far as this blog covers.