EPiServer  /  CMS April 11, 2011

Automatically organize EPiServer pages – Part 2

In a previous post I described a prototypical module that I’ve created which offers generic functionality for organizing EPiServer pages in various structures. While the previous post presented the background and implementation of the core classes of the module we’ll focus on how to use it in this part. You’ll find the source code for this module on GitHub where you can download or fork it.

Creating a structure based on creation date

As I mentioned in the previous post I’ve created a number of abstract base classes that implement the IOrganizeChildren interfaces and which will, when inherited, organize pages that are put as children of them in different ways. One of these are the DateStructureBase class which will organize pages first by year, secondly by month and finally by day.

When inheriting from DateStructureBase we have to specify three type parameters. These types, which must inherit from PageData, will be used as the types for the container pages for year, month and day. To keep it simple we can begin by creating a single page type that we will use for all types of containers.

[PageType]
public class Container : TypedPageData
{
}

We can then create a concrete implementation of DateStructureBase, like this:

[PageType]
public class DateStructure : 
    DateStructureBase<Container, Container, Container>
{
}

With these two classes added to our web project we can compile and begin using the new DateStructure page type. For instance we can create a new page of type DateStructure that we name “My archive”. If we then right click on our newly created “My archive” page and choose to create a page of some content type, such as the standard page page type from the public templates, and publish it we’ll see three new pages under “My archive” and that our content page has been put as a child of the last leaf in the tree.

CreatingDateStructure

Implementation

The implementation of the DateStructureBase class is quite straight forward except for the unfortunate high number of type parameters of course.

public abstract class DateStructureBase<TYear, TMonth, TDay> 
    : TypedPageData, IOrganizeChildren
    where TYear : PageData 
    where TMonth : PageData 
    where TDay : PageData
{
    public PageReference GetParentForPage(PageData page)
    {
        if (page is TYear)
        {
            return PageLink;
        }

        var pageDate = GetStructureDate(page);
        var structureHelper = new StructureHelper();
        var yearPage = structureHelper.GetOrCreateChildPage<TYear>(
            PageLink, pageDate.Year.ToString());
        var monthPage = structureHelper.GetOrCreateChildPage<TMonth>(
            yearPage.PageLink, pageDate.Month.ToString());
        var dayPage = structureHelper.GetOrCreateChildPage<TDay>(
            monthPage.PageLink, pageDate.Day.ToString());
        return dayPage.PageLink;
    }

    protected virtual DateTime GetStructureDate(
        PageData pageToGetParentFor)
    {
        return pageToGetParentFor.Created;
    }
}

The class utilizes an instance of the StructureHelper class (which I described in the previous post) three times to ensure that there is a structure that matches the creation date of the page. Finally it returns a reference to the page that matches the day. Worth noticing is that it uses a virtual method named GetStructureDate to retrieve the date that it uses for the page which is to be placed into the structure. In other words it’s possible to modify what date is used by overriding this method.

Creating an alphabetical structure based on page name

AlphabeticalStructure2Another quite common scenario in which we want to group pages is by the first letter of their names. This is easily accomplished by creating a page type that inherits from AlphabeticalStructureBase. All we have to do is specify the type of the container pages. Luckily, this time it’s only a single type parameter and there’s nothing stopping us from reusing the Container page type that we previously created.

[PageType(DefaultChildSortOrder = FilterSortOrder.Alphabetical)]
public class AlphabeticalStructure 
    : AlphabeticalStructureBase<Container>
{
}

Implementation

The implementation of the AlphabeticalStructureBase class is very simple in it self as it utilizes functionality from it’s base class, SingleLevelStructureBase. We’ll soon take a look at this useful base class but first let’s quickly review the AlphabeticalStructureBase class.

public abstract class AlphabeticalStructureBase<TCharacter> 
    : SingleLevelStructureBase<TCharacter>
    where TCharacter : PageData, new()
{
    protected override string GetContainerPageName(PageData childPage)
    {
        return childPage.PageName[0].ToString().ToUpperInvariant();
    }
}

As you can see it requires a type parameter for container pages which it passes on to its base class. It also overrides the GetContainerPageName method and returns the first letter of the page’s name in upper casing.

Easily create new types of structures with SingleLevelStructureBase

When I first created the AlphabeticalStructureBase class I realized that almost the exact same functionality could be reused by other types of structures. Normally I would have extracted this to a separate class as to favor composition over inheritance, but in this case it actually seemed that a base class would be a good fit so that’s what I created.

By inheriting from SingleLevelStructureBase we can easily create new types of structures that are, you guessed it, single level. All we have to do is implement the GetContainerPageName method to determine the page name for a container that should be the parent of the page supplied as an argument to the method.

As SingleLevelStructureBase uses the StructureHelper class to find and create container pages it’s implementation is quite straight forward.

public abstract class SingleLevelStructureBase<TContainer> 
    : TypedPageData, IOrganizeChildren
    where TContainer : PageData
{
    public virtual PageReference GetParentForPage(PageData page)
    {
        if (page is TContainer)
        {
            return PageLink;
        }

        if (string.IsNullOrEmpty(page.PageName))
        {
            return PageLink;
        }

        var structureHelper = new StructureHelper();

        var container = structureHelper
            .GetOrCreateChildPage<TContainer>(
            PageLink, GetContainerPageName(page));
        return container.PageLink;
    }

    protected abstract string GetContainerPageName(
        PageData childPage);
}

Creating a structure based on page types

Perhaps not the most commonly used scenario but still interesting is grouping pages by their page types. We can accomplish this by creating a page type that inherits from PageTypeStructureBase. Just as before we have to specify the type of the container page that it will use.

[PageType]
public class PageTypeStructure : PageTypeStructureBase<Container>
{
}

Implementation

As AlphabeticalStructureBase the PageTypeStructureBase class inherits from SingleLevelStructureBase meaning that its implementation is short in terms of code. It simply overrides the GetContainerPageName method and returns the name of the page type that the child page is of.

public abstract class PageTypeStructureBase<TContainer> : 
    SingleLevelStructureBase<TContainer>
    where TContainer : PageData
{
    protected override string GetContainerPageName(
        PageData childPage)
    {
        var pageType = PageType.Load(childPage.PageTypeID);
        return pageType.Name;
    }
}

Creating composite structures

We’ve now seen how the module works to automatically organize children of page types whose classes implement the IOrganizeChildren interface in the first part. We’ve also, in this part seen how some of the most common types of structures can be accomplished. This pretty much covers everything. Except for one detail, the ability to create composite structures, which we’ll take a look at in the third and final part.

PS. For updates about new posts, sites I find useful and the occasional rant you can follow me on Twitter. You are also most welcome to subscribe to the RSS-feed.

Joel Abrahamsson

Joel Abrahamsson

I'm a passionate web developer and systems architect living in Stockholm, Sweden. I work as CTO for a large media site and enjoy developing with all technologies, especially .NET, Node.js, and ElasticSearch. Read more

Comments

comments powered by Disqus

My book

Want a structured way to learn EPiServer 7 development? Check out my book on Leanpub!

More about EPiServer CMS