Insights

Creating A Dynamic List Of Items

In The Sitecore Rich Text Editor

The Problem

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.


    public void Process(RenderFieldArgs args)
    {
	
	// Ensure the field is of type rich text
      if (args.FieldTypeKey != "rich text")
        return;
		
      // Ensure the field has a value before we continue
      if (!args.FieldValue.IsWhiteSpaceOrNull())
      {
        var newValue = ConvertTokenToLinkList(args.FieldValue);
		
		// If there was no operation then do not replace
        if (newValue != null) args.Result.FirstPart = newValue;
      }
    }

Once we've determined that this field and its value are fair game, let us begin writing our custom 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 "

" + noResultsText+ "

"; var html = "
    "; // 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 += "
"; return html; } private string GetAnchorTagWithTokens() { return "
  • {linkText} {linkDescription}
  • "; }

    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.

    Hey, Developers!

    We're on the look out for talented developers to join our team.

    Think you have what it takes?