Extensions for LinkItemCollection – Convert to pages and prepare Link Items for repeater

December 3rd, 2010

LinkItemCollection 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:

?View Code CSHARP
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>

6 Responses to “Extensions for LinkItemCollection – Convert to pages and prepare Link Items for repeater”

  1. Martin S. says:

    Neat and useful solution, will definitely use this one next time I’ll be working with a LinkItemCollection.

  2. Martin Helgesen says:

    Great post, Shahin!

    I’ll grab your class right away. Sure it’ll come in handy

  3. Thanks! Glad to hear it’s useful.

  4. Works great… again! 🙂

  5. Note that some corrections have been made in the code, please update your versions!

Leave a Reply to Stefan Wärting