If you haven’t, be sure to check out EPiServer filter – part 1.
In one of my project I had a bunch of categories and needed to filter a PageDataCollection to find pages that had one or more of the categories. Out of the box EPiServer has no filter class that does this, so I needed to create my own. This was actually pretty easy. I created a new class and implemented the EPiServer.Filters.IPageFilter interface which has three method signatures.
void Filter(EPiServer.Core.PageDataCollection pages);
void Filter(object sender, EPiServer.Filters.FilterEventArgs e);
bool ShouldFilter(EPiServer.Core.PageData page);
I then added a public property of type CategoryList for my categories. Then in my filter method I traversed the PageDataCollection and looked for matches, if there was no match the page got removed.
The complete code
public class FilterAtLeastOneCategory : IPageFilter
{
public CategoryList Categories
{
get;
set;
}
public FilterAtLeastOneCategory()
{
}
public FilterAtLeastOneCategory(CategoryList categoryList)
{
this.Categories = categoryList;
}
public void Filter(PageDataCollection pages)
{
for (int i = pages.Count - 1; i >= 0; i--)
{
bool isMatch = false;
var categoryList = pages[i].Category;
if (categoryList != null)
{
foreach (var category in categoryList)
{
foreach (var currentCategory in this.Categories)
{
if (category == currentCategory)
{
isMatch = true;
}
}
}
}
if (!isMatch)
{
pages.RemoveAt(i);
}
}
}
public void Filter(object sender, FilterEventArgs e)
{
this.Filter(e.Pages);
}
public bool ShouldFilter(PageData page)
{
throw new NotImplementedException();
}
}
I could then use my new filter class.
var currentCategories = CurrentPage.Category;
new FilterAtLeastOneCategory(currentCategories).Filter(myPageDataCollection);
Filter by Role
Here is another custom filter that removes all pages that the specified role doesn’t have access to.
using System;
using EPiServer.Core;
using EPiServer.Filters;
namespace FV.Templates.FV.Filters
{
public class FilterRole : IPageFilter
{
public string RoleName
{
get;
set;
}
public FilterRole()
{
}
public FilterRole(string roleName)
{
this.RoleName = roleName;
}
public void Filter(PageDataCollection pages)
{
for (int i = pages.Count - 1; i >= 0; i--)
{
bool isMatch = false;
var acl = pages[i].ACL;
if (acl != null)
{
foreach (var role in acl)
{
if (role.Key == this.RoleName)
{
isMatch = true;
}
}
}
if (!isMatch)
{
pages.RemoveAt(i);
}
}
}
public void Filter(object sender, FilterEventArgs e)
{
this.Filter(e.Pages);
}
public bool ShouldFilter(PageData page)
{
throw new NotImplementedException();
}
}
}
new FilterRole("Everyone").Filter(pages);
I came across this simple technique while watching Building great standards based websites for the big wide world with ASP.NET 4.0.
The cool thing about being able to have multiple forms on the same page is that you don’t have to use JavaScript to fix the default button problem, or use unnecessary ASP.NET web controls or logic on the server (like the Response.Redirect method). The drawback, for some, is that you cannot have the forms inside the form (web form) with the runat=”server” attribute, which means that you cannot use all of the ASP.NET web controls.
Example

Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<fieldset>
<legend>ASP.NET web form (POST)</legend>
<asp:Label runat="server" AssociatedControlID="txtSearch">Name: </asp:Label><asp:TextBox runat="server" ID="txtSearch" /><asp:Button runat="server" ID="btnSend" Text="Search" OnClick="btnSend_Click" />
</fieldset>
</form>
<form method="get" action="Search.aspx">
<fieldset>
<legend>Regular HTML form using GET</legend>
<label for="name-text">Name: </label><input type="text" id="name-text" name="q" /><input type="submit" value="Search" />
</fieldset>
</form>
<form method="post" action="Search.aspx">
<fieldset>
<legend>Regular HTML form using POST</legend>
<label for="name-text2">Name: </label><input type="text" id="name-text2" name="q" /><input type="submit" value="Search" />
</fieldset>
</form>
</body>
</html>
using System;
public partial class _Default : System.Web.UI.Page
{
protected void btnSend_Click(object sender, EventArgs e)
{
Response.Redirect(string.Format("Search.aspx?q={0}", txtSearch.Text));
}
}
Search.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Search.aspx.cs" Inherits="Search" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label runat="server" ID="lblResult" />
</div>
</form>
</body>
</html>
using System;
using System.Web;
public partial class Search : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(Request.QueryString["q"]))
{
lblResult.Text = HttpUtility.HtmlEncode(Request.QueryString["q"]);
return;
}
// For the regular html form POST method
lblResult.Text = HttpUtility.HtmlEncode(Request.Form["q"]);
}
}
EPiServer Extension Methods part 1
From LinkItemCollection to PageDataCollection
public static PageDataCollection ToPageDataCollection(this LinkItemCollection linkItemCollection)
{
var pageDataCollection = new PageDataCollection();
foreach (var linkItem in linkItemCollection)
{
var url = new UrlBuilder(linkItem.Href);
bool isEPiServerPage = PermanentLinkMapStore.ToMapped(url);
if (isEPiServerPage)
{
var page = DataFactory.Instance.GetPage(PermanentLinkUtility.GetPageReference(url));
if (page != null)
{
pageDataCollection.Add(page);
}
}
}
return pageDataCollection;
}
Example
if (IsValue("RelatedPages"))
{
var relatedLinkItems = (LinkItemCollection) CurrentPage["RelatedPages"];
if (relatedLinkItems != null)
{
var relatedPages = relatedLinkItems.ToPageDataCollection();
if (relatedPages != null)
{
rptRelatedContent = relatedPages;
rptRelatedContent.DataBind();
}
}
}
This is great since you now have the ability to filter out the pages that the user does not have access to or that are not published.
FilterForVisitor.Filter(relatedPages);