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.
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.
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]
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} ";
}
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>
This code will return a list of items in your specified format
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.
We're on the look out for talented developers to join our team.
Think you have what it takes?