Last active
August 6, 2017 14:48
-
-
Save stefanolsen/85393dbe740383e9312640d0c1212b62 to your computer and use it in GitHub Desktop.
Code listings for blog post about rendering EPiServer CMS properties with dynamic HTML attributes. Read about it here: https://stefanolsen.com/posts/adding-dynamic-html-to-property-templates-in-episerver/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@model EPiServer.Core.ContentReference | |
@Html.ContentLink(Model, null, Html.LinkAttributes(Model)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class HtmlHelperExtensions | |
{ | |
public static RouteValueDictionary LinkAttributes(this HtmlHelper helper, ContentReference contentLink) | |
{ | |
var resolver = ServiceLocator.Current.GetInstance<LinkAttributesResolver>(); | |
var attributes = resolver.ToLinkAttributes(contentLink); | |
return attributes; | |
} | |
public static RouteValueDictionary LinkAttributes(this HtmlHelper helper, Url url, string action = null) | |
{ | |
var resolver = ServiceLocator.Current.GetInstance<LinkAttributesResolver>(); | |
var attributes = resolver.ToLinkAttributes(url, action); | |
return attributes; | |
} | |
public static MvcHtmlString InsertLinkAttributes(this HtmlHelper helper, ContentReference contentLink) | |
{ | |
var resolver = ServiceLocator.Current.GetInstance<LinkAttributesResolver>(); | |
var attributes = resolver.ToLinkAttributes(contentLink); | |
return MvcHtmlString.Create(string.Join(" ", attributes.Select(a => $"{a.Key}=\"{a.Value}\""))); | |
} | |
public static MvcHtmlString InsertLinkAttributes(this HtmlHelper helper, Url url, string action = null) | |
{ | |
var resolver = ServiceLocator.Current.GetInstance<LinkAttributesResolver>(); | |
var attributes = resolver.ToLinkAttributes(url, action); | |
return MvcHtmlString.Create(string.Join(" ", attributes.Select(a => $"{a.Key}=\"{a.Value}\""))); | |
} | |
public static MvcHtmlString InsertLinkAttributes(this HtmlHelper helper, MvcHtmlString xhtmlString) | |
{ | |
var resolver = ServiceLocator.Current.GetInstance<LinkAttributesResolver>(); | |
return MvcHtmlString.Create(resolver.InsertLinkAttributes(xhtmlString)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class LinkAttributesResolver | |
{ | |
private const string DataCategory = "data-category"; | |
private const string DataLabel = "data-label"; | |
private const string DataAction = "data-action"; | |
private const string Download = "download"; | |
private const string Target = "target"; | |
private const string AttributesCategoryExternal = "External"; | |
private const string AttributesCategoryDownload = "Downloads"; | |
private const string AttributeDownload = "download"; | |
private const string AttributesTargetBlank = "_blank"; | |
private static readonly Regex ExternalLinkRegex = | |
new Regex(@"<a(?:.*?)href=""(?<linkUrl>(?:(https?|mailto):)(?:.*?))""(?:.*?)>", RegexOptions.Compiled); | |
private static readonly Regex MediaLinkRegex = | |
new Regex(@"<a(?:.*?)href=""(?<linkUrl>(?:(\/globalassets\/|\/siteassets\/|\/contentassets\/))(?:.*?))""(?:.*?)(?:target=""(?<target>.*)"")?>", RegexOptions.Compiled); | |
private readonly IContentLoader _contentLoader; | |
private readonly ISiteDefinitionResolver _siteDefinitionResolver; | |
private readonly UrlResolver _urlResolver; | |
public LinkAttributesResolver( | |
IContentLoader contentLoader, | |
ISiteDefinitionResolver siteDefinitionResolver, | |
UrlResolver urlResolver) | |
{ | |
_contentLoader = contentLoader; | |
_siteDefinitionResolver = siteDefinitionResolver; | |
_urlResolver = urlResolver; | |
} | |
public RouteValueDictionary ToLinkAttributes(ContentReference contentLink) | |
{ | |
var result = new RouteValueDictionary(); | |
if (ContentReference.IsNullOrEmpty(contentLink)) | |
{ | |
return result; | |
} | |
IContent content; | |
if (!_contentLoader.TryGet(contentLink, out content)) | |
{ | |
return result; | |
} | |
IContent shortcutContent; | |
if (TryGetShortcut(content, out shortcutContent)) | |
{ | |
content = shortcutContent; | |
} | |
string action = string.Empty; | |
string category = string.Empty; | |
string label = string.Empty; | |
string download = string.Empty; | |
var mediaData = content as MediaData; | |
if (mediaData != null) | |
{ | |
category = AttributesCategoryDownload; | |
label = mediaData.Name; | |
action = GetFileExtension(mediaData); | |
if (mediaData is ImageFile) | |
{ | |
download = mediaData.Name; | |
} | |
} | |
else | |
{ | |
var siteDefinition = _siteDefinitionResolver.GetByContent(content.ContentLink, false, false); | |
if (siteDefinition == SiteDefinition.Empty || siteDefinition != SiteDefinition.Current) | |
{ | |
category = AttributesCategoryExternal; | |
label = GetFriendlyUrl(contentLink); | |
action = AttributesCategoryExternal; | |
} | |
} | |
result.Add(DataCategory, category); | |
result.Add(DataLabel, label); | |
result.Add(DataAction, action); | |
if (!string.IsNullOrEmpty(download)) | |
{ | |
result.Add(Download, download); | |
} | |
return result; | |
} | |
public RouteValueDictionary ToLinkAttributes(Url url, string action) | |
{ | |
var result = new RouteValueDictionary(); | |
if (url == null || url.IsEmpty()) | |
{ | |
return result; | |
} | |
if (!url.IsAbsoluteUri) | |
{ | |
IContent content = GetContent(url); | |
return content == null ? result : ToLinkAttributes(content.ContentLink); | |
} | |
string category = string.Empty; | |
string label = string.Empty; | |
string download = string.Empty; | |
if (url.ToString().StartsWith("mailto:")) | |
{ | |
category = "Contact"; | |
label = url.ToString().Substring(7); | |
action = "mailto"; | |
} | |
else | |
{ | |
category = AttributesCategoryExternal; | |
label = GetFriendlyUrl(url); | |
action = !string.IsNullOrEmpty(action) ? action : AttributesCategoryExternal; | |
} | |
result.Add(DataCategory, category); | |
result.Add(DataLabel, label); | |
result.Add(DataAction, action); | |
if (!string.IsNullOrEmpty(download)) | |
{ | |
result.Add(Download, download); | |
} | |
return result; | |
} | |
/// <summary> | |
/// Inserts computed attributes for all links in an HTML string. | |
/// </summary> | |
public string InsertLinkAttributes(MvcHtmlString xhtmlString) | |
{ | |
string htmlString = xhtmlString.ToHtmlString(); | |
var externalLinkMatchEvaluator = new MatchEvaluator(ExternalLinkMatchEvaluator); | |
string replaced = ExternalLinkRegex.Replace(htmlString, externalLinkMatchEvaluator); | |
var mediaLinkMatchEvaluator = new MatchEvaluator(MediaLinkMatchEvaluator); | |
replaced = MediaLinkRegex.Replace(replaced, mediaLinkMatchEvaluator); | |
return replaced; | |
} | |
/// <summary> | |
/// Tries to lookup an external looking URL, and add the computed attributes. | |
/// </summary> | |
private string ExternalLinkMatchEvaluator(Match match) | |
{ | |
string linkTag = match.Value; | |
string linkUrl = match.Groups["linkUrl"].Value; | |
bool hasTarget = match.Groups["target"].Success; | |
RouteValueDictionary attributes = ToLinkAttributes(new Url(linkUrl), AttributesCategoryExternal); | |
if (hasTarget) | |
{ | |
attributes.Remove(Target); | |
} | |
var attributeString = MvcHtmlString.Create(string.Join(" ", attributes.Select(a => $"{a.Key}=\"{a.Value}\""))); | |
linkTag = linkTag.Replace(">", $" {attributeString}>"); | |
return linkTag; | |
} | |
/// <summary> | |
/// Tries to lookup a media looking URL, and add the computed attributes. | |
/// </summary> | |
private string MediaLinkMatchEvaluator(Match match) | |
{ | |
string linkTag = match.Value; | |
string linkUrl = match.Groups["linkUrl"].Value; | |
bool hasTarget = match.Groups["target"].Success; | |
RouteValueDictionary attributes = | |
ToLinkAttributes(new Url(linkUrl), AttributesCategoryDownload); | |
if (hasTarget) | |
{ | |
attributes.Remove(Target); | |
} | |
var attributeString = | |
MvcHtmlString.Create(string.Join(" ", attributes.Select(a => $"{a.Key}=\"{a.Value}\""))); | |
linkTag = linkTag.Replace(">", $" {attributeString}>"); | |
return linkTag; | |
} | |
private bool TryGetShortcut(IContent content, out IContent shortcut) | |
{ | |
shortcut = null; | |
var page = content as PageData; | |
if (page == null || page.LinkType != PageShortcutType.Shortcut) | |
{ | |
return false; | |
} | |
var propertyContentReference = page.Property["PageShortcutLink"] as PropertyContentReference; | |
if (propertyContentReference != null && | |
!ContentReference.IsNullOrEmpty(propertyContentReference.ContentLink)) | |
{ | |
_contentLoader.TryGet(propertyContentReference.ContentLink, out shortcut); | |
return true; | |
} | |
return false; | |
} | |
private string GetFriendlyUrl(Url internalUrl) | |
{ | |
if (internalUrl == null || internalUrl.IsEmpty()) | |
{ | |
return string.Empty; | |
} | |
var url = new UrlBuilder(internalUrl); | |
return _urlResolver.GetUrl(url, new VirtualPathArguments { ContextMode = ContextMode.Default }) ?? string.Empty; | |
} | |
private string GetFriendlyUrl(ContentReference contentReference) | |
{ | |
if (ContentReference.IsNullOrEmpty(contentReference)) | |
{ | |
return string.Empty; | |
} | |
return _urlResolver.GetUrl(contentReference); | |
} | |
private IContent GetContent(Url internalUrl) | |
{ | |
if (internalUrl == null) | |
{ | |
return default(IContent); | |
} | |
var url = new UrlBuilder(internalUrl); | |
return _urlResolver.Route(url); | |
} | |
private string GetFileExtension(MediaData mediaData) | |
{ | |
if (mediaData == null) | |
{ | |
return string.Empty; | |
} | |
string url = GetFriendlyUrl(mediaData.ContentLink); | |
return url.Split('.').LastOrDefault(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@model EPiServer.Url | |
@Html.ContentLink(Model, Html.LinkAttributes(Model)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@model EPiServer.Core.XhtmlString | |
@{ | |
MvcHtmlString xhtmlString = Html.XhtmlString(Model); | |
xhtmlString = Html.InsertLinkAttributes(xhtmlString); | |
} | |
@xhtmlString |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment