Adding RTE RichText Custom DropDown List in #Sitecore 9

Recently I had to do a custom implementation for an RTE dropdown list in Sitecore 9. I used @jammykam’s example to get started. This was done in a previous version of Sitecore. You can find it here. Granted the implementation did not change too much between versions, but if you are searching for a way to do it in Sitecore 9 this should help since there is at least one minor difference I found.

So, to not disturb the out of box RTE profiles I duplicated an existing one.

I created a custom dropdown button in the part of the RTE toolbar I would like to have the button available.

To tie the Rich Text Profile with its own EditorConfiguration pipeline a Configuration Type was added to the profile.

In the Visual Studio solution, I created the following which will define the button, add CSS and add JavaScript.

The InsertToken.xml contains the following:

  1. <? xml version = “1.0”  
  2. encoding = “utf-8” ?> < control xmlns: def = “Definition”  
  3. xmlns = http://schemas.sitecore.net/Visual-Studio-Intellisense&#8221; > < RichText.InsertToken > span.InsertToken {  
  4.     background – image: url(‘/sitecore/shell/Themes/Standard/Images/Editor/WebResource.png’) !important;  
  5. } < /RichText.InsertToken> < /control>  

    The InsertToken.js contains the following. Telerik controls were used in later versions of Sitecore including 9 instead of the RadEditor controls.

  6. Telerik.Web.UI.Editor.CommandList[“InsertToken”] = function(commandName, editor, args) {  
  7.     var val = args.get_value();  
  8.     editor.pasteHtml(val);  
  9.     args.set_cancel(true);  
  10. };  
  11. jQuery(document).ready(function($) {  
  12.     loadCSS = function(href) {  
  13.         var cssLink = $(“<link rel=’stylesheet’ type=’text/css’ href='” + href + “‘>”);  
  14.         $(“head”).append(cssLink);  
  15.     };  
  16.     loadCSS(“/sitecore/shell/Controls/Rich Text Editor/InsertToken/InsertToken.css”);  
  17. });  

    The InsertToken.css contains the following. The reason for this CSS code is so that the button in the RTE appears correctly. @Ja

    span.InsertToken {

  18. background – image: url(‘/temp/iconcache/office/16×16/document_json.png’) !important;  
  19. }  

    Under Pipelines\RichTextEditor an override to the EditorConfiguration was created.

    You can use DotPeek or another decompiler to get the code to override, but the main thing you want to change is the following in this case. Note the ItemsPerRow kept the column values in one single line.

  20. case “Html Editor Custom Drop Down Button”:  
  21.     var editorSplitButton1 = new EditorSplitButton {  
  22.         Name = str1, ShowIcon = true, ItemsPerRow = “1”, ImageUrl = “/temp/iconcache/office/24×24/document_json.png”  
  23.     };  
  24.     EditorSplitButton editorSplitButton2 = editorSplitButton1;  
  25.     SetProperties((EditorTool) editorSplitButton2, obj);  
  26.     EditorConfiguration.SetChildren(editorSplitButton2.Items, obj);  
  27.     toolbar.Tools.Add((EditorToolBase) editorSplitButton2);  
  28.     continue;  

    The list of values is fed from Sitecore. The token item contains a Description and TokenValue field.

    SetChildren is called which in turns calls GetTokenList to retrieve the values and set the dropdown.

  29. private static void SetChildren(EditorDropDownItemCollection items, Sitecore.Data.Items.Item parent) {  
  30.     Assert.ArgumentNotNull((object) items, “items”);  
  31.     Assert.ArgumentNotNull((object) parent, “parent”);  
  32.     foreach(Sitecore.Data.Items.Item obj in parent.Children) {  
  33.         if (parent.Name == “Insert Token” && obj.TemplateName == “Html Editor List Item”) {  
  34.             items = GetTokenList(items);  
  35.         } else {  
  36.             if (obj.TemplateName == “Html Editor List Item”) {  
  37.                 items.Add(obj[“Header”], obj[“Value”]);  
  38.             }  
  39.         }  
  40.     }  
  41. }  

  42. private static EditorDropDownItemCollection GetTokenList(EditorDropDownItemCollection items) {  
  43.     Sitecore.Data.Database master = Sitecore.Configuration.Factory.GetDatabase(“master”);  
  44.     Item parentTokenFolder = master.GetItem(“/sitecore/content/Tokens”);  
  45.     if ((parentTokenFolder == null) || (!parentTokenFolder.HasChildren)) {  
  46.         return items;  
  47.     }  
  48.     foreach(Item token in parentTokenFolder.Children) {  
  49.         items.Add(token.Fields[“TokenValue”].Value + ” – “ + token.Fields[“Description”].Value, token.Fields[“TokenValue”].Value);  
  50.     }  
  51.     return items;  
  52. }  

     

    After the changes you should see the RTE button in the editor and selecting it should insert the text that was predefined.

#Sitecore ‘s SaveUI Pipeline One Way it Can Save You from the Cache

Recently I had a bug fix that required fixing a custom tab page in the content editor. The custom page had a save button, but users were also clicking on the content editor save button. Well unfortunately that undid the previous change. The reason why was even though the changes were saved to the database the old values were still being cached.

So after doing some research on maybe refreshing, clearing cache etc… the best way to fix this issue turned out to be was adding a custom pipeline step on the SaveUI pipeline. It was a little a trial and error at first, but I was able to accomplish what I needed to.

First thing is you want to do is come up with a name for your new class and create a config file that will hook into the SaveUI pipeline. Now you have some options here. If you look below the SaveUI has a series of steps that happen in order for an item to save. You just need to find the one you want your step to go before or after.

<saveUI>
<processor mode=”on” type=”Sitecore.Pipelines.Save.BeforeSaveEvent, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.ParseXml, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.CheckItemLock, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.CheckRevision, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.Validators, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.ValidateFields, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.HasWritePermission, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.NewVersion, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.TightenRelativeImageLinks, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.ConvertToXHtml, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.CheckLock, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.Lock, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.CheckBaseTemplateFieldChange, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.CheckTemplateFieldChange, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.ConvertLayoutField, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.CheckLinks, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.Save, Sitecore.Kernel” />
<processor mode=”off” type=”Sitecore.Pipelines.Save.RenderingHack, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.Unlock, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.WorkflowSaveCommand, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.PostAction, Sitecore.Kernel” />
</saveUI>

I created a config file and placed it in the app_config\include folder of the website.

<configuration xmlns:patch=”http://www.sitecore.net/xmlconfig/”&gt;
<sitecore>
<processors>
<saveUI>
<processor patch:before=”processor[@type=’Sitecore.Pipelines.Save.CheckRevision, Sitecore.Kernel’]” type=”MySite.Feature.Website.Pipelines.ConfirmChanges,MySite.Feature.Website” />
</saveUI>
</processors>
</sitecore>
</configuration>

The code for the pipeline was simple. Basically I would check for what fields changed and made sure what was saved in the database fields would update the fields in the cache. That way when the save happens the SaveUI pipeline will process the changes and not overwrite anything.

public class ConfirmChanges
{
public void Process(SC.Pipelines.Save.SaveArgs args)
{
Assert.ArgumentNotNull(args, “args”);

if (!args.HasSheerUI)
{
return;
}

Assert.IsNotNull(SC.Context.ContentDatabase, “Sitecore.Context.ContentDatbabase”);
string message = string.Empty;

// for each of the items the user attempts to save
foreach (SC.Pipelines.Save.SaveArgs.SaveItem uiItem in args.Items)
{
// retrieve from the database the same item containing the old values
SC.Data.Items.Item dbItem = SC.Context.ContentDatabase.GetItem(uiItem.ID, uiItem.Language, uiItem.Version);
Assert.IsNotNull(dbItem, “dbItem”);

// for each of the fields in that item (not just updated fields)
if (UsesCustomTab(dbItem))
{
foreach (SC.Pipelines.Save.SaveArgs.SaveField uiField in uiItem.Fields)
{
if (uiField.Value == dbItem[uiField.ID])
{
continue;
}
string title = string.IsNullOrEmpty(dbItem.Fields[uiField.ID].Title)
? dbItem.Fields[uiField.ID].DisplayName
: dbItem.Fields[uiField.ID].Title;
if (title == “FieldName1” || title == “FieldName2”)
{
//if values are the same don’t do anything exit pipeline.
if (!string.IsNullOrEmpty(dbItem[uiField.ID]) && dbItem[uiField.ID] == uiField.Value)
{
continue;
}
else
{
if (title == “FieldName1”)
{
uiField.Value = dbItem.Fields[“FieldName1”].Value;
}
if (title == “FieldName2”)
{
uiField.Value = dbItem.Fields[“FieldName2”].Value;
}
}

}
}
}
}

}
}

That’s it. Let me know if you have any questions.

cache