Improvements to the PageTypeBuilder project - Version 0.5

by: Joel Abrahamsson

About a week ago I wrote about, and offered download of, an experimental project called PageTypeBuilder which enabled page type inheritance and strongly typed property access. Since then I’ve done some refactoring and I have also added quite a lot of features to the project. The new version, 0.5 (the old one was 0.1 to signal that it was just an experiment), is downloadable here. While quite a lot of work still remains it’s starting to mature from just an experiment to something that might some day become a beta version ;) Here’s what I’ve done:

Renaming of page types

The PageTypeDefinitionAttribute class has been renamed to PageTypeAttribute and to it I’ve added a Guid property. If we specify a GUID, either by checking what Guid an existing page type has or by adding a new one we can now rename page types by simply changing the name property of the PageType attribute or by renaming the class if we haven’t specified a name.

[PageType("bb6737e6-ccb4-4584-b8ce-3c62ba4ea599")]
public class StartPage : PageData
{
}

The above code will add a page type called Startpage:

image

We then change the name to “My Start Page”, like this:

[PageType("bb6737e6-ccb4-4584-b8ce-3c62ba4ea599",

Name="My Start Page")]

public class StartPage : PageData
{
}

If we hadn’t specified a GUID a new page type would have been created but now the existing page type is renamed:

image

I haven’t found a native way to create GUIDs in Visual Studio but the below macro does the trick.

Public Module GUIDGeneratorModule
    Sub Create_GUID()
        DTE.ActiveDocument.Selection.Text = 
            System.Guid.NewGuid().ToString()
    End Sub
End Module

Specifying Page Type settings

With the previous version a few page type settings such as name and filename could be set. In this new version every setting that can be set in EPiServers admin mode can be set in code using the PageType attribute. I’m especially found of the AvailablePageTypes property which corresponds to the Settings –> Available Page Types tab in admin mode as it accepts an array of types as parameter value.

Here are a few usage examples:

[PageType("bb6737e6-ccb4-4584-b8ce-3c62ba4ea599", 
    AvailablePageTypes = new [] { typeof(StartPage) },
    AvailableInEditMode = false, Filename = "/Default.aspx",
    DefaultPageName = "Welcome to the site",
    DefaultChildSortOrder = FilterSortOrder.PublishedAscending)]
public class StartPage : PageData
{
}

Specifying property settings

Just as with page types the previous version allowed us to set some settings for properties but far from all. With this version we can set all settings for page type properties that can be set in edit mode except those that are specific for long strings and XHTML strings, from code. This is done by setting values for properties of the PageTypeProperty attribute (renamed from PropertyDefinition in the previous version).  A few examples:

[PageTypeProperty(SortOrder = 1, UniqueValuePerLanguage = true,
    DisplayInEditMode = true, DefaultValue = "Hello world!")]
public string MainBody
{
    //Implementation
}

Undefined properties are removed

In the previous version undefined properties, that is page type properties that where not defined in the corresponding class, where not removed. In this version they are. This means that if you add a property to a page type through code and then remove it from the code, or if you add it from admin mode, it will be removed the next time page types are synchronized, which happens when EPiServer CMS starts up.

Attribute inheritance

Another nice addition in this version is that the PageTypeBuilder now checks for PageType attributes and PageTypeProperty attributes and attributes that inherit from them. This means that it’s possible to create custom attributes with some default values set. Let’s for instance say that we want to have the Unique value per language setting set to true for all of our string properties. We could handle this to some extent with page type inheritance but if some pages have different string properties than those in our base class we’d have to specify this setting for them. Using attribute inheritance we can now simply create our own subclass of the PageTypePropertyAttribute class and use that for all of our string properties, like this:

public class PageTypeStringPropertyAttribute 
    : PageTypePropertyAttribute
{
    public PageTypeStringPropertyAttribute()
    {
        UniqueValuePerLanguage = true;
    }
}

What’s next

While I think it’s starting to look pretty usable by now there are still some major issues to figure out before the PageTypeBuilder project can be considered somewhat complete. It still needs to be able to handle dynamic properties and long string/XHML string settings for example.

Any feedback on how it can be improved is greatly appreciated!

Request for feedback

I initially thought I’d make the GUID mandatory for both  page types and properties, but I then found out that properties (PageDefinitions) doesn’t have GUIDs (pleeeaaase EPiServer, add that ;-)) which made me a bit disheartened and therefore I didn’t make it mandatory for page types either. I’m still contemplating making it mandatory though and any feedback on that, and in general, would be greatly appreciated!

Download

The source code for the PageTypeBuilder version 0.5, and a compiled assembly, can be downloaded here.

07 June 2009

Tags:


    Comments

    1. 1) Does inheritance work if you want a base type with a bunch of properties that you want for all sub-page types ? (but without registering a page type for the base class, I guess it could work if you just exclude the PageTypeAttribute on the base class)

      2) Do you really want GUID's on properties? Feels kind of COM-ish even though I understand your thinking.

      Finally, I am impressed by your work.

    2. Thanks! And thanks for your feedback! 1) It checks that the class isn't abstract so you could have an abstract class with the PageType attribute as base class. On the other hand there wouldn't be much of a point to setting the PageType attribute on an abstract class, so either way works :) 2) I see your point. But especially if it would be possible to use the "GUID method" for properties as well I think it would be quite powerfull as we could almost fully control page types from code. If that would be possible I personally would gladly pay the price of having ugly, COM-ish GUID-strings in all of the attributes :) However, I think the effect of the GUID-method is greatest for properties as they are automatically removed which makes it almost impossible to rename them. As for page types the effect is much smaller so I think a good compromise might be to keep the GUID property on the PageType attribute but not make it mandatory. Still not sure though :)
    3. Another thing that might be usefull is to be able to access the page types themself (or at least some key properties) strongly typed. Say for instance we have a (extension) method GetAllChildren that returns all children as page data objects that we want to filter like such: children.Where(pd => pd.PageTypeID == StartPage.(something). I guess you could reflect the StartPage class, get it's guid, load the pagetype and return the ID but there must be a smoother way...?
    4. Agreed. What I've been thinking about is looking into creating a wrapper around the DataFactory class that returns all PageData objects strongly typed. That is we should be able to do something like this: List

      children = PageBuilder.DataFactory.GetChildren( somePage.PageLink); children.Where(pd => pd is MyPageType); I have yet to try this but it feels like it should be possible if we build a dictionary with page type IDs as key and (page type) types as value at start up. I'll look into it :)

    Post a comment    
    User verification Image for user verification  
    EPiTrace logger