EPiServer  /  CMS May 23, 2011

Page Type Builder 2 Preview 1 released!

It’s been a looooong time coming but tonight I finally released a first preview of version 2 of Page Type Builder. This release is far from feature complete and it’s highly untested. Let me stress that again: this release is highly untested. With that said, it contains a couple of cool new features: Property Groups and support for Property Settings.

Property Groups

Property groups is a way to reuse definitions of properties on several page types without using inheritance. Currently this has only been possible by using interfaces, a solution that had some issues and which will be removed in favor of property groups in the future. Apart from giving us a nice way of reusing properties on different page types property groups also offers a more object oriented feel to working with page type classes as classes now can have complex values as properties while still being represented as individual properties for the editor.

What property groups are is basically a grouping of page type properties that Page Type Builder will regard as properties on page types that have a property of the property group type. For instance, imagine that we create a property group named MetaData with two properties that we annotate with the PageTypeProperty attribute, like this:

public class MetaData : PageTypePropertyGroup
{
    [PageTypeProperty(Type = typeof(PropertyLongString))]
    public virtual string Description { get; set; }

    [PageTypeProperty(Type = typeof(PropertyString))]
    public virtual string Keywords { get; set; }
}

And we also create a page type named Article which has a property of type MetaData, which we also annotate with the PageTypePropertyGroup attribute:

[PageType(Filename = "~/ArticleTemplate.aspx")]
public class Article : TypedPageData
{
    [PageTypePropertyGroup]
    public virtual MetaData MetaData { get; set; }
}

Once we compile and run the site Page Type Builder will create the page type with two properties named MetaData-Description and MetaData-Keywords.

property-groups

These can be accessed like this:

<%= CurrentPage.MetaData.Description %>
<%= CurrentPage.MetaData.Keywords %>

I’m sure myself or Lee Crowe who committed this feature (along with Mark Everard) will blog about more details in the future. In the meantime you can find some history and more details in the original discussion thread. Another example is also available on Stefan Forsberg’s blog.

Support for Property Settings

The other big thing in this release is support for property settings. After some interesting debate on how to implement this support I decided to try a simplistic route where Page Type Builder doesn’t try to guess how we want to use it but rather offer us quite a lot of power to do whatever we want. Unfortunately this route was far from simplistic in terms of implementation (it involves a hefty dose of black reflection magic) but after a few nights of tearing my hair out it seem to work. The functionality offers three things.

Managing settings for individual properties

To control what settings are used, and what they contain, for individual properties we create a class that inherits from Attribute and implements Page Type Builder’s new IUpdatePropertySettings<T> interface where the type parameter is used to specify the type of settings. For instance, to define a slightly larger Tiny MCE editor we could create a class like this:

public class ArticleEditorSettingsAttribute 
    : Attribute, IUpdatePropertySettings<TinyMCESettings>
{
    public void UpdateSettings(TinyMCESettings settings)
    {
        settings.Width = 600;
        settings.Height = 600;
    }

    public int GetSettingsHashCode(TinyMCESettings settings)
    {
        return settings.Height + settings.Width;
    }

    public bool OverWriteExistingSettings
    {
        get { return true; }
    }
}

As you can see it consists of members, all mandated by the interface. The UpdateSettings method offers us the possibility to do just what it says.

The GetSettingsHashCode method is used by Page Type Builder to determine if the UpdateSettings method has modified the settings in such a way that it should be saved if the settings already exists. In other words, we should implement it in such a way that it returns a unique value for all combinations of the properties on the settings object that we care about. In theory this could be handled automatically by Page Type Builder by using EPiServer’s ToPropertyBag method but with the current implementation we are more in control. Feedback regarding this would be interesting.

The third member is quite self explanatory. If there already exists settings of the same type for the property they will only be overwritten if the OverWriteExistingSettings property returns true.

To use our new attribute we simply annotate one or several properties with it:

[PageTypeProperty]
[ArticleEditorSettings]
public virtual string MainBody { get; set; }

In admin mode we will now find that the MainBody property has custom settings with the specified width and height.

custom-property-settings

Managing global settings

Page Type Builder also support creating and updating global property settings. To utilize this functionality we can create a class that implements the IUpdateGlobalPropertySettings<T> interface. This has the same members as IUpdatePropertySettings but also adds four more. As an example, we could create a class that will create settings with only a limited set of buttons for the Tiny MCE editor:

public class LimitedEditorGlobalSettings 
    : IUpdateGlobalPropertySettings<TinyMCESettings>
{
    public string DisplayName
    {
        get { return "Limited editor"; }
    }

    public string Description
    {
        get { return "Only the most essential settings"; }
    }

    public bool? IsDefault
    {
        get { return false; }
    }

    public bool Match(PropertySettingsWrapper propertySettingsWrapper)
    {
        return propertySettingsWrapper.DisplayName.Equals(DisplayName);
    }

    public void UpdateSettings(TinyMCESettings settings)
    {
        settings.ToolbarRows.Clear();
        var buttons = new List<string> {"bold", "italic", "underline"};
        var toolbarRow = new ToolbarRow(buttons);
        settings.ToolbarRows.Add(toolbarRow);
    }

    public int GetSettingsHashCode(TinyMCESettings settings)
    {
        var flattened = settings.ToolbarRows.SelectMany(row => row.Buttons);
        return string.Concat(flattened).GetHashCode();
    }

    public bool OverWriteExistingSettings
    {
        get { return false; }
    }
}

The first three members of this class, and of the IUpdateGlobalPropertySettings interface map directly to the PropertySettingsWrapper for the settings. In other words they are used to specify values that aren’t specific to the type of settings we’re updating but to the meta data shown in EPiServer’s admin and edit interface. It’s worth noting that if either of these returns null Page Type Builder will ignore them, in other words making it possible for editors to specify them themselves.

global-property-settings

The fourth member, the Match method, is a bit interesting. As Page Type Builder is mapping from code to the database it needs some sort of key to match classes with rows in the database (or rather objects in returned by EPiServer’s API. For page types is used the name or the GUID if specified. For properties it uses the name. However, for property settings I decided to try out a more open solution with which we can control the mapping ourselves.

In other words, the Match method should be implemented to determine if a specific PropertySettingsWrapper (sort of an instance of the settings) should be updated by this class or not. I would guess that in most cases we will want to implement this by matching the DisplayName like in the example above.

Mapping global settings to properties

We’ve now seen how we can use Page Type Builder to create and update settings for individual properties as well as how we can create and update global settings. One final piece of the puzzle remains, being able to map global settings to individual properties. This can be done using the UseGlobalSettings attribute with which we simply specify a type that implements the IUpdateGlobalPropertySettings interface. So, for instance we could create a Preamble property in our Article class and make it use our previously created global settings for the Tiny MCE editor:

[PageTypeProperty]
[UseGlobalSettings(typeof(LimitedEditorGlobalSettings))]
public virtual string Preamble { get; set; }

Using-global-property-settingsOnce we compile and go to our site we will see that Page Type Builder will have updated the Preamble property to use the settings named “Limited editor”.

Other changes

I’ve done some change to the code base to prepare for some interesting changes in the future but most of the public API should be fairly backwards compatible. However, one clear breaking change is that I’ve removed the LongStringSettings property from the PageTypeProperty attribute making it impossible to update settings for the old editor using Page Type Builder. It was either that or only supporting the latest version of EPiServer (R2). More information about why I had to make this change can be found here.

What’s next

Like I wrote in the beginning of this post this is very untested release and the only reason for it is to get feedback to myself and those who contribute to the project. There are some other interesting features planned (some I’ve already started on myself and some have already been contributed) but the two described above are the most significant one so I wanted to get this release out as soon as it was possible to hopefully get help finding serious flaws in these concepts early.

Next on the agenda is adding a lot of start up validation for the new features to make the development experience with Page Type Builder pleasant. After that I’m planning to work on making Page Type Builder more extendable as well as some more minor features.

In the meantime please try this release out. Any and all feedback is very welcome!

Thanks to everyone who has contributed with ideas and spare time for development, especially Lee Crowe for taking the time to implement property groups and jumping through all my hoops for getting it into the code base!

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