Creating A Dynamic List Of Items

In The Sitecore Rich Text Editor

October 20, 2021

By Mike Payne

I was recently tasked with creating a dynamic list of documents in a website that was mostly rich text authored. Reason being, this site was migrated from a static html page to a Sitecore environment with no redesign planned.

The lists of Sitecore items were not in consistent places within blocks of content so it didn't make sense to me to create a component to try and capture all scenarios.

The Idea

I had the idea to create a snippet of code an author can add to a rich text field with parameters. This snippet would be 'picked up' by the backend code and replaced with dynamically generated Sitecore content.

The Execution

Let us first decide on a syntax to use for the snippet of code we are inserting into the Rich Text fields HTML editor. This code is written for the below format:

$[linklist={17468C01-0D05-4D91-837B-2FAE3F529193}|There are no alerts or advisories at this time]

  • linklist - indicates the name of the method we have defined
  • {17468C01-0D05-4D91-837B-2FAE3F529193} - is the first parameter, it equals the guid of the parent folder containing the link items/documents
  • There are no alerts or advisories at this time - the second parameter (separated by a pipe '|') is the message returned when there are no items in the folder

We need to create a processor that only executes on the rich text editor fields. This is done by checking args.FieldTypeKey != "rich text". We'll then check to see that field actually has a value then proceed to execute our code.


    private string ConvertTokenToLinkList(string resultText)
    {
      var outputText = resultText;
      if (resultText.IsWhiteSpaceOrNull()) return null;
	 
      // Check to see if we have instances of the snippet
      var matches = GetMethodSubstring(resultText);
      if (matches == null) return null;
	 
      // Loop through the multiple matches returned and replace the snippets with the generated content
      foreach (Match m in matches)
      {
        var linkList = ReplaceMatchWithLinkList(m.Value);
        outputText = outputText.Replace(m.Value, linkList);
      }
      return outputText;
    }

    private string ReplaceMatchWithLinkList(string match)
    {
	
      // Get the parameters from our string. An incorrect length would indicate incorrect formatting
      var param = GetParametersFromSubstring(match);
      if (param.Length < 2) return null;
      var parentId = new ID(param[0]);
      if (parentId.IsNull) return null;
      var noresults = param[1].Replace("]", "");
      // This is where we use the parameters to get the child items from Sitecore
      var linklist = GetLinkListText(parentId, noresults);
      return linklist;
    }

    private MatchCollection GetMethodSubstring(string text) {
      var pattern = @"(\$\[linklist).*?(\])";
      var matches = Regex.Matches(text, pattern, RegexOptions.IgnoreCase);
      if (matches.Count > 0) return matches;
      return null;
    }

    private string[] GetParametersFromSubstring(string substring)
    {
      var array = substring.Split('=');
      // Our two parameters are split by a '|' and appear after the instance of the '='
      if (array != null && array.Length > 1) {
        var param = array[1].Split('|');
        return param;
      }
      return null;
    }

    private string GetLinkListText(ID parentId, string noResultsText) {
      var parentItem = Sitecore.Context.Database.Items.GetItem(parentId);
	 
      // If the parent item was null or has no children, return our no results text
      if (parentItem == null || !parentItem.HasChildren) return "<p>" + noResultsText+ "</p>";
      var html = "<ul>";
      // Here we get our html with tokens to be replaced by item values. This could also come from a config file.
      var tokenizedString = GetAnchorTagWithTokens();
      foreach (Item i in parentItem.Children)
      {
        var media = new MediaItem(i);
        var filePath = MediaManager.GetMediaUrl(media);
        html += (tokenizedString.Replace("{linkUrl}", filePath).Replace("{linkTitle}", media.Title).Replace("{linkText}", media.Title).Replace("{linkDescription}", media.Description));
      }
      html += "</ul>";
      return html;
    }

    private string GetAnchorTagWithTokens() {
      return "<li><a href=\"{linkUrl}\" target=\"blank\" title=\"{linkTitle}\">{linkText}</a> <b>{linkDescription}</b></li>";
    }

Configuration

Now let us connect our processor to the correct pipeline.

It is crucial that we include the patch:after piece as this must execute after Sitecore.Pipelines.RenderField.GetTextFieldValue or else RenderFieldArgs parameter in our processor will be missing values.


  <sitecore>
	<pipelines>
		<renderField>
			<processor type="Company.Foundation.Extensions.Pipelines.RichTextLinksListProcessor, Company.Foundation.Extensions"
			patch:after="processor[@type='Sitecore.Pipelines.RenderField.GetTextFieldValue, Sitecore.Kernel']"/>
		</renderField>
	</pipelines>
  </sitecore>

Conclusion

This code will return a list of items in your specified format

Further considerations

Perhaps further null checking is in order. The output html of the link items could potentially be stored in a config file for easy editing as well.

Mike Headshot

Mike Payne

Development Team Lead

Mike is a Development Team Lead who is also Sitecore 9.0 Platform Associate Developer Certified. He's a BCIS graduate from Mount Royal University and has worked with Sitecore for over seven years. He's a passionate full-stack developer that helps drive solution decisions and assist his team. Mike is big into road cycling, playing guitar, working out, and snowboarding in the winter.