Extensions for LinkItemCollection – Convert to pages and prepare Link Items for repeater
December 3rd, 2010LinkItemCollection is a very useful property type in EPiServer. However, quite often you need to transform, convert or in some ways prepare a LinkItemCollection before you can use it the way you want to.
There have been several posts in the EPiServer community regarding this (Joel Abrahamsson and Frederik Vig have blogged nice solutions) but sometimes you need to filter out external links and documents, other times all links should be included – even documents and e-mails. It would be great if it’s possible to filter the resulting pages by a certain pagetype, and all access rights issues should be dealt with before we get the final result.
With those requirements in mind, me and my colleague from Ottoboni Stefan Wärting wrote a few extension methods for the LinkItemCollection, which return objects prepared for lazy use in repeaters without further concern for deleted pages, access rights, publish status, or even broken document links.
Tested in EPiServer CMS 6, but should work fine in CMS 5 as well. Enjoy!
EDIT – Correction made around row 40 to ensure that specified target and link text will be kept for internal links! Thanks to Stefan Wärting for discovering.
EDIT #2 – Correction in IsFileAccessible() method: The checking if a file exists ( if File.Exists ) is removed due to potential problems with File.Exists when the VPP folder is placed in a network share. The file exists check was actually redundant anyhow so it works just as fine without it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | using System; using System.Collections.Generic; using System.Linq; using System.Web.Hosting; using System.IO; using EPiServer; using EPiServer.Web; using EPiServer.Core; using EPiServer.Security; using EPiServer.SpecializedProperties; namespace Extensions { public static class LinkItemCollectionExtension { /// <summary> /// Prepares all links in a LinkItemCollection for output /// by filtering out inaccessible links and ensures all links are correct. /// </summary> /// <param name="linkItemCollection">The collection of links to prepare.</param> /// <param name="targetExternalLinksToNewWindow">True will set target to _blank if target is not specified for the LinkItem.</param> /// <returns>A prepared and filtered list of LinkItems</returns> public static IEnumerable<LinkItem> ToPreparedLinkItems(this LinkItemCollection linkItemCollection, bool targetExternalLinksToNewWindow) { if (linkItemCollection != null) { foreach (var linkItem in linkItemCollection) { var url = new UrlBuilder(linkItem.Href); if (PermanentLinkMapStore.ToMapped(url)) { var pr = PermanentLinkUtility.GetPageReference(url); if (!PageReference.IsNullOrEmpty(pr)) { // page var page = DataFactory.Instance.GetPage(pr); if (IsPageAccessible(page)) { linkItem.Href = page.LinkURL; yield return LinkItem; } } else { // document if (IsFileAccessible(linkItem.Href)) { Global.UrlRewriteProvider.ConvertToExternal(url, null, System.Text.Encoding.UTF8); linkItem.Href = url.Path; yield return linkItem; } } } else if (!linkItem.Href.StartsWith("~")) { // external if (targetExternalLinksToNewWindow && string.IsNullOrEmpty(linkItem.Target)) { linkItem.Target = "_blank"; } if (linkItem.Href.StartsWith("mailto:") || linkItem.Target == "null") { linkItem.Target = string.Empty; } yield return linkItem; } } } } /// <summary> /// Prepares all links in a LinkItemCollection for output /// by filtering out inaccessible links and ensures all links are correct. /// </summary> /// <param name="linkItemCollection">The collection of links to prepare.</param> /// <returns>A prepared and filtered list of LinkItems</returns> public static IEnumerable<LinkItem> ToPreparedLinkItems(this LinkItemCollection linkItemCollection) { return linkItemCollection.ToPreparedLinkItems(false); } /// <summary> /// Converts a LinkItemCollection to typed pages. Any non-pages will be filtered out. (Not compatible with PageList - Use ToPageDataList) /// </summary> /// <typeparam name="T">PageType</typeparam> /// <param name="linkItemCollection">The collection of links to convert</param> /// <returns>An enumerable of typed PageData</returns> public static IEnumerable<T> ToPages<T>(this LinkItemCollection linkItemCollection) where T : PageData { if (linkItemCollection != null) { foreach (var linkItem in linkItemCollection) { var url = new UrlBuilder(linkItem.Href); if (PermanentLinkMapStore.ToMapped(url)) { var pr = PermanentLinkUtility.GetPageReference(url); if (!PageReference.IsNullOrEmpty(pr)) { var page = DataFactory.Instance.GetPage(pr); if (page is T && IsPageAccessible(page)) { yield return (T)page; } } } } } } /// <summary> /// Converts a LinkItemCollection to typed pages. Any non-pages will be filtered out. (PageList compatible) /// </summary> /// <typeparam name="T">PageType</typeparam> /// <param name="linkItemCollection"></param> /// <returns>A list of typed PageData</returns> public static List<T> ToPageDataList<T>(this LinkItemCollection linkItemCollection) where T : PageData { return linkItemCollection.ToPages<T>().ToList(); } /// <summary> /// Converts a LinkItemCollection to a list of pages. Any non-pages will be filtered out. (PageList compatible) /// </summary> /// <param name="linkItemCollection"></param> /// <returns>A list of typed PageData</returns> public static List<PageData> ToPageDataList(this LinkItemCollection linkItemCollection) { return linkItemCollection.ToPages<PageData>().ToList(); } private static bool IsPageAccessible(PageData page) { return (page != null && !page.IsDeleted && page.Status == VersionStatus.Published && page.PendingPublish == false && page.StartPublish < DateTime.Now && page.StopPublish > DateTime.Now && page.ACL.QueryDistinctAccess(AccessLevel.Read)); } // TODO: Possibly a better solution w/o try catch? private static bool IsFileAccessible(string filePath) { try { HostingEnvironment.VirtualPathProvider.GetFile(filePath); return true; } catch { } return false; } } } |
To print out link items you could use a simple repeater populated using OnItemDataBound or just use the quick and dirty way:
1 2 3 4 5 | <asp:Repeater runat="server"> <ItemTemplate> <li><a href="<%# Eval("Href") %>" title="<%# Eval("Title") %>" target="<%# Eval("Target") %>"><%# Eval("Text") %></a></li> </ItemTemplate> </asp:Repeater> |
Neat and useful solution, will definitely use this one next time I’ll be working with a LinkItemCollection.
Great post, Shahin!
I’ll grab your class right away. Sure it’ll come in handy
Thanks! Glad to hear it’s useful.
Yay! 🙂
Works great… again! 🙂
Note that some corrections have been made in the code, please update your versions!