Different approaches for enabling strongly typed EPiServer page types
EPiServer’s native approach
The default approach for managing EPiServer page types looks like this:
As you can see, the EPiServer GUI is used to manage page type definitions stored in the EPiServer database. For the sake of clarity, EPiServer does not natively offer strongly typed page types.
Our (previous) approach
In 2007 we had a little project called EPiPageTypes. It was based on the idea that page types are created through EPiServer’s admin interface. But, as a pre-build event in our projects we ran an executable which retrieved all page type definitions from the EPiServer database and generated a plain C# code file within the project with one class per page type which in turn contained one property per page type property.
In other words, if I created a page type called MyPageType which had an XHTML page property called MainBody I could access that property for the current page using this syntax:
string propertyValue = ((MyPageType)CurrentPage).MainBody;
Whenever the EPiServer website was compiled the source code for the page type classes were updated, so you’d get a compile-time error if a page type had been changed in EPiServer, for example if a page property had been renamed or removed. So, the model looked something like this:
However, there was one major thing we didn't accomplish with this, and that was page type inheritance. But, thanks to some truly excellent work by Joel Abrahamsson, we've gotten exactly that. Which brings me to the third approach…
Joel Abrahamsson’s Page Type Builder project
Joel, a systems architect at Valtech, is the guy behind the open-source project Page Type Builder. Not only does it result in strongly typed page types like our EPiPageTypes project, but it also allows for page type inheritance – something the EPiServer community has been wanting for quite some time.
So, in order to be consistent, here’s the idea behind the PageTypeBuilder project (notice how the arrows now point the other way):
As you see, Joel’s approach starts from the other end: page type definitions are declared in code and through reflection the EPiServer database is updated when the application starts.
Erik Nordin has posted some code snippets for the project, but I thought I’d complement those with a brief demonstration of how to declare EPiServer page types through code and how to realize page type inheritance in EPiServer.
Using the Page Type Builder project with your EPiServer website
First of all you need to download the Page Type Builder project from CodePlex. You can download either a compiled version or the source code.
I opted for the source code and included the project in my EPiServer solution:
Note: to avoid having Visual Studio tell you the PageTypeBuilder project location isn’t trusted, or an exception saying That assembly doesn’t allow partially trusted callers, ensure you unblock the ZIP file before extracting the PageTypeBuilder files:
Next I added a reference to the PageTypeBuilder project in my website project called TedNyberg:
Allright, now we’re able to access a whole box of goodies inside the PageTypeBuilder namespace!
Defining our first page type
I created an empty ASPX file called MyPageTemplate and made it inherit from the TemplatePage class, just like the default page templates in the EPiServer sample website:
Next I created a class called MyPageType like so:
After logging in to the EPiServer admin interface I could now see this:
Opening the settings for the My Page Type page type I could see that the settings applied through the PageTypeBuilder attributes have worked as expected:
Notice how the name, description and file name reflect the PageType attribute values specified earlier. Once again, hats off to Joel! This part right here got me excited! Nice clean code that just works - it sure gives you a sense of accomplishment!
Adding page properties using Page Type Builder
Our page type won’t do much good without any page properties. First off, let’s create a well-known MainBody property!
I added an XHTML property using Erik Nordin’s nifty little snippet:
Notice how the LongStringSettings parameter can be used to specify which toolbar options should be available:
using EPiServer.Core;
using PageTypeBuilder;
using EPiServer.SpecializedProperties;
using EPiServer.DataAbstraction;
using EPiServer.Editor;
namespace TedNyberg
{
[PageType(
Name="My Page Type",
Description="Page type created using PageTypeBuilder attributes",
Filename="/Templates/PageTypeBuilder/MyPageTemplate.aspx")]
public class MyPageType : TypedPageData
{
[PageTypeProperty(
EditCaption="Main body",
HelpText="The main rich text content of the page.",
DefaultValue="<p>Nothing to see here</p>",
DefaultValueType=DefaultValueType.Value,
DisplayInEditMode=true,
Required=true,
Searchable=true,
SortOrder=100,
UniqueValuePerLanguage=true,
Type=typeof(PropertyXhtmlString),
LongStringSettings=(
EditorToolOption.DynamicContent ^
EditorToolOption.ToggleHtml ^
EditorToolOption.InsertImage ^
EditorToolOption.InsertUrl))]
public virtual string MainBody
{
get;
set;
}
}
}
After having compiled the application I could now see the following in EPiServer admin mode:
Note: you can use the EditorToolOption.All enum to specify toolbar options for the XHTML editor. The following would enable all options except for the Font option (this is currently the default when using Erik Nordin’s code snippet):
LongStringSettings= ( EditorToolOption.All ^ EditorToolOption.Font)
Accessing the property value
First I created a new page based on My Page Type:
My new page got page ID 26:
Under normal circumstances I would get the MainBody property value using syntax similar to this:
string content = CurrentPage["MainBody"];
Easy enough, for sure, but since it isn’t strongly typed you won’t be able to trap errors until run-time. For example if the MainBody property doesn’t exist for the current page.
Using PageTypeBuilder we can use the following syntax instead:
string content = CurrentPage.MainBody
Here’s another example, in this case to retrieve the MainBody property value of the newly created page with page ID 26:
string content = DataFactory.Instance.GetPage<MyPageType>(new PageReference(26)).MainBody;
Creating an inheriting page type
I created another page type called My Other Page Type. This one also has a MainBody property since I inherited from My Page Type:
[PageType(
Name="My Other Page Type",
Description="Another page type created using PageTypeBuilder attributes",
Filename="/Templates/PageTypeBuilder/MyPageTemplate.aspx")]
public class MyOtherPageType : MyPageType
{
}
Now, after re-compiling I could see two page types in EPiServer:
Looking at the settings for My Other Page Type I could see that this page indeed also has a MainBody property:
Modifying a property value programmatically
I changed my page template logic to update the MainBody property each time the page loads:
public partial class MyPageTemplate : TemplatePage<MyPageType>
{
protected override void OnLoad(System.EventArgs e){base.OnLoad(e);
{
// Create a writable copy
MyPageType copy = CurrentPage.CreateWritableClone();
// Set the MainBody property value
copy.MainBody = string.Format("<p>Page last loaded: {0}</p>", DateTime.Now);
// Publish the modified page
DataFactory.Instance.Save(copy, SaveAction.ForceCurrentVersion);
}
}
Now, after having refreshed the page, I see the following:
Lastly, I would once again like to thank Joel for his work on the Page Type Builder project – truly awesome!
Links
- PageTypeBuilder project at Codeplex
- EPiServer CMS at EPiServer World
- PageTypeBuilder snippets by Erik Nordin