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.