Creating a dynamic image in Rich Text fields

Have you ever inserted an image, then changed its alt or title tags in the Media Library and noticed that the change does not propagate into the rich text field? It’s because Sitecore only inserts static values into the field HTML when you insert an image. If you’ve ever wanted or needed those images to act dynamically – so that changing alt or title text affects the field HTML -then this article might help you.

The Secret? Tokens!

We can modify the rich text editor dialogs so that when a user inserts Sitecore media, the ALT and TITLE text being sent is actually just a token – i.e. {{alt}}. There is no specific syntax required, just use something that you like. That means that we’re actually inserting “{{alt}}” and “{{title}}” into the field HTML, not the actual (static) values. The goal then is to create a pipeline processor that will read the field and look for those tokens, and replace them with the current values of the Alt and Title fields of the media item.

Customizing the InsertImage dialog

The first thing we will want to do is modify the InsertImage dialog. The InsertImage dialog is a Sitecore dialog, and the source can be found with any Sitecore installation. You would need two files:

  1. The InsertImage.xml file for the dialog, which can be found in the webroot of your site under sitecore\shell\Controls\Rich Text Editor/InsertImage.
  2. The InsertImageForm.cs file (code behind file) which can be extracted from the Sitecore.Client.dll. You can use .NET Reflector, DotPeek, or JustDecompile (I personally use the last one). Look for a Sitecore.Shell.Controls.RichTextEditor.InsertImage entry.

You will want to take these two files and preferably put them in your solution. Place these in a new folder, e.g. Controls/RichTextEditor/InsertSitecoreMediaExtended and make the following changes:

  • Keep the .xml file name the same. In the .xml file, there is a line which points to the code behind file – update that with the location of the file in the solution and the assembly name.
  • Update the name of the .cs file to match the parent folder (InsertSitecoreMediaExtended.cs). In that file, remove everything except the OnOK and SetDimensions methods. You will need to modify the OnOK method so that alt and title are set using tokens:
image.Add("Alt", StringUtil.EscapeQuote("{{alt}}"));
image.Add("Title", StringUtil.EscapeQuote("{{title}}"));

Optional: You can edit the Telerik.Web.UI.Editor.DialogControls.SetImageProperties dialog by adding a title field and checkboxes next to the alt and title fields to make it easier for content editors to specify this option.

Let’s make a new pipeline!

You will want to extend the RenderField pipeline in Sitecore.Kernel, specifically the GetFieldValue processor. So when Sitecore gets the value of the RTE field, we’re going to modify the HTML by swapping tokens for media item values. Create a new folder in your pipelines folder or project named GetFieldValueExtended, and a new class file under it with the same name.

Now you’ll need to tell Sitecore to run your custom processor. There are a few options – patch:before, patch:after or overwrite completely. I personally used the patch:after option. If you choose to overwrite, make sure your processor implements all of the same code as the original. Create a new patch file in the App_Config/Include folder similar to the below. (In my case, the solution has a Processor project.)

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <pipelines>
            <renderField>
                <processor
                    type="MyProject.Processor.RenderField.GetFieldValueExtended, MyProject.Processor"
                    patch:after="processor[@type='Sitecore.Pipelines.RenderField.GetFieldValue, Sitecore.Kernel']" />
            </renderField>
        </pipelines>
    </sitecore>
</configuration>

In your custom processor class file, set up the Process event with some Asserts and other logic needed before doing the swap. Create the tokens as constants for flexibility. Here is the full code used, which is currently running in a production environment with no issues. I highly recommend HtmlAgilityPack for spelunking in the HTML. (NOTE: The code assumes your production environment Sitecore database is web, which may not be true in your case.)

    public class GetFieldValueExtended
    {
        private const string AltToken = "{{alt}}";
        private const string TitleToken = "{{title}}";

        public void Process(RenderFieldArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            Assert.ArgumentNotNull(args.Item, "args.Item");
            Assert.ArgumentNotNull(args.Item.Database, "args.Item.Database");
            Assert.ArgumentNotNull(args.GetField(), "args.GetField()");

            if (args.Item.Database.Name.Equals("web"))
            {
                if (args.GetField().Value.Contains(AltToken) || args.GetField().Value.Contains(TitleToken))
                {
                    args.Result.FirstPart =  ReplaceTokens(args.GetField().Value);
                }
            }
        }

        private string ReplaceTokens(string fieldValue)
        {
            if (fieldValue.Contains("img") == false) return fieldValue;

            var document = new HtmlAgilityPack.HtmlDocument();
            document.LoadHtml(fieldValue);

            foreach (var img in document.DocumentNode.Descendants("img"))
            {
                var imgSrc = img.Attributes["src"];
                if (imgSrc == null) continue;

                // Remove query string from src (if it exists)
                var mediaUrl = CleanImageSource(imgSrc.Value);

                // Get media item ID based on media url
                DynamicLink dynamicLink;
                if (!DynamicLink.TryParse(mediaUrl, out dynamicLink)) continue;
                MediaItem mediaItem = Sitecore.Context.Database.GetItem(dynamicLink.ItemId,
                    dynamicLink.Language ?? Sitecore.Context.Language);

                // Replace the tokens
                if (img.Attributes["alt"] != null && img.Attributes["alt"].Value.Trim() == AltToken)
                {
                    img.Attributes["alt"].Value = mediaItem.Alt;
                }

                if (img.Attributes["title"] != null && img.Attributes["title"].Value.Trim() == TitleToken)
                {
                    img.Attributes["title"].Value = mediaItem.Title;
                }
            }

            return document.DocumentNode.OuterHtml;
        }

        private string CleanImageSource(string imgSrcValue)
        {
            // First match the SHA1
            var shaMatcher = new Regex("\b[0-9a-f]{5,40}\b");
            var match = shaMatcher.Match(imgSrcValue);

            if (match.Success)
            {
                // Setup media url
                return string.Format("/~/media/{0}.ashx", match.Value);
            }

            return imgSrcValue;
        }
    }

 

Completion

Overall, this was a great feature that the client needed as they tried to make their website ADA compliant, by including the appropriate alt and title text values. I hope this helps you in your Sitecore development adventures.

Advertisements