A couple of days ago, Sitecore MVP Brian Pedersen wrote an article discussing how newlines and carriage returns in Multi-Line Text fields can intrusively launch Sitecore’s “Do you want to save the changes to the item?” dialog box when clicking away from an item — even when you’ve made no changes to the item. Brian then offered an extension method on the String class as a way to remedy this annoyance.
However, Brian’s extension method cannot serve a solution on its own. It has to be invoked from somewhere to stamp out the substring malefactors — newlines (“\n”), carriage returns (“\r”), tabs (“\t”), and non-breaking spaces (“\xA0”).
This article gives one possible solution for uprooting these from Multi-Line Text fields within the Sitecore client by removing them upon item save.
First, I created a utility class that removes specified substrings from a string passed to it.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Sitecore.Sandbox.Utilities.StringUtilities.Base { public interface ISubstringAnnihilator { string AnnihilateSubstrings(string input); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Sitecore.Sandbox.Utilities.StringUtilities.Base; namespace Sitecore.Sandbox.Utilities.StringUtilities { public class SubstringAnnihilator : ISubstringAnnihilator { private const string ReplacementString = " "; private static readonly IEnumerable<string> SubstringsToAnnihilate = new string[] { "\r\n", "\n", "\r", "\t", "\xA0"}; private SubstringAnnihilator() { } public string AnnihilateSubstrings(string input) { foreach (string substringToAnnihilate in SubstringsToAnnihilate) { input = input.Replace(substringToAnnihilate, ReplacementString); } return input; } public static ISubstringAnnihilator CreateNewSubstringAnnihilator() { return new SubstringAnnihilator(); } } }
It would probably be ideal to move the target substrings defined within the SubstringsToAnnihilate string array into a configuration file or even into Sitecore itself. I decided not introduce that complexity here for the sake of brevity.
Next, I created a Save pipeline. I used .NET Reflector to see how other Save pipelines in /configuration/sitecore/processors/saveUI/ in the Web.config were built — I used these as a model for creating my own — and used my SubstringAnnihilator utility class to seek and destroy the target substrings (well, just replace them with a space :)).
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Sitecore.Data; using Sitecore.Data.Fields; using Sitecore.Data.Items; using Sitecore.Diagnostics; using Sitecore.Pipelines.Save; using Sitecore.Sandbox.Utilities.StringUtilities; using Sitecore.Sandbox.Utilities.StringUtilities.Base; namespace Sitecore.Sandbox.Pipelines.SaveUI { public class FixMultiLineTextFields { private static readonly ISubstringAnnihilator Annihilator = SubstringAnnihilator.CreateNewSubstringAnnihilator(); public void Process(SaveArgs saveArgs) { FixAllItemFieldsWhereApplicable(saveArgs); } private static void FixAllItemFieldsWhereApplicable(SaveArgs saveArgs) { AssertSaveArgs(saveArgs); foreach (SaveArgs.SaveItem saveItem in saveArgs.Items) { FixSaveItemFieldsWhereApplicable(saveItem); } } private static void AssertSaveArgs(SaveArgs saveArgs) { Assert.ArgumentNotNull(saveArgs, "saveArgs"); Assert.IsNotNull(saveArgs.Items, "saveArgs.Items"); } private static void FixSaveItemFieldsWhereApplicable(SaveArgs.SaveItem saveItem) { Item item = GetItem(saveItem); foreach (SaveArgs.SaveField saveField in saveItem.Fields) { FixSaveItemFieldIfApplicable(item, saveField); } } private static Item GetItem(SaveArgs.SaveItem saveItem) { if (saveItem != null) { return Client.ContentDatabase.Items[saveItem.ID, saveItem.Language, saveItem.Version]; } return null; } private static void FixSaveItemFieldIfApplicable(Item item, SaveArgs.SaveField saveField) { if (ShouldEnsureFieldValue(item, saveField)) { saveField.Value = Annihilator.AnnihilateSubstrings(saveField.Value); } } private static bool ShouldEnsureFieldValue(Item item, SaveArgs.SaveField saveField) { Field field = item.Fields[saveField.ID]; return ShouldEnsureFieldValue(field); } private static bool ShouldEnsureFieldValue(Field field) { return field.TypeKey == "memo" || field.TypeKey == "multi-line text"; } } }
Now, it’s time to insert my new Save pipeline within the SaveUI pipeline stack. I’ve done this with my /App_Config/Include/FixMultiLineTextFields.config file:
<?xml version="1.0" encoding="utf-8"?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <processors> <saveUI> <processor mode="on" type="Sitecore.Sandbox.Pipelines.SaveUI.FixMultiLineTextFields, Sitecore.Sandbox" patch:after="processor[@type='Sitecore.Pipelines.Save.TightenRelativeImageLinks, Sitecore.Kernel']" /> </saveUI> </processors> </sitecore> </configuration>
Let’s take the above for a spin. I’ve inserted a sentence with newlines after every word within it into my Blurb Multi-Line Text field:
Now, I’ve clicked save, and all newlines within my Blurb Multi-Line Text field have been annihilated:
I would like to thank to Brian Pedersen for writing his article the other day — it served as the bedrock for this one, and kindled something in me to write the code above.
Keep smiling when coding!
Very good article. I’m experiencing a few of these issues as well..