Creating Page Type Classes from Page Types in EPiServer CMS 7

After my upgrade of Stockholm Pride to EPiServer 7 CMS (blog post coming soon) I wanted the project to be a bit more shiny with some Code Managed Page Types.

I forked Erik Nordin’s utility to create classes for PageTypeBuilder in an existing project. Did some research on what attributes I needed to create a similar thing with EPiServer 7 CMS.

As a bonus I also gave it a little more EPiServer look-and-feel:

create pt

Since I don’t know if I should commit this back to Erik Nordin’s GitHub or not, you can download it from GitHub.

Current status

I would see this as a first draft of the creation tool that worked quite good for my upgrade of Stockholm Pride.

It creates Properties for the most common properties in EPiServer but gives a template where you can decide if you want to go with a BackingTypeAttribute or using EditorDescriptors/UIHints instead (see below).

Attributes and value types

I’ve tried to cover all correct DataTypes Attributes such as replacing PropertyImageUrl with Url and setting UIHints but I still thinks it needs some more cleaning.

Note that since I only check for what’s in the database, avoid running this tool while already having Page Types Classes in your project.

Custom Properties

For now, the support for Custom Properties are not that great. I try to read out what kind of Data Value the Properties are meant to return but leave a code comment to those Properties I don’t know what to do with.

This way you can make a search for //DefinitionType= and decide for yourself if you want to use the UIHintAttribute or the BackingFieldAttribute to solve this problem.

Some of the older EPiServer Properties are obsolete which mean that they should not be used as a BackingField.

UILanguageProperty is one of them which luckily is only a property that returns a string, but the Editor may select among all activated Languages.

By reflection I can find out exactly how that list is populated.

Example of EditorDescriptor

This means that UILanguageProperty can easily be replaced with an EditorDescriptor that gives the same functionality:

//[BackingType(typeof(PropertyUILanguage))] -- PropertyUILanguage has been obsoleted and will be removed in a future release.
[Display(Name = "Content Language")]
public virtual string ContentLanguage { get; set; }

[EditorDescriptorRegistration(TargetType = typeof(string), UIHint = "UILanguage")]
public class UILanguageEditorDescriptor : EditorDescriptor
   public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable<Attribute> attributes)
      SelectionFactoryType = typeof(UILanguageSelectionFactory);
      ClientEditingClass = "epi.cms.contentediting.editors.SelectionEditor";

      base.ModifyMetadata(metadata, attributes);

public class UILanguageSelectionFactory : ISelectionFactory
   public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)
      var languages = new List<SelectItem>();
      foreach (System.Globalization.CultureInfo info in LocalizationService.Current.AvailableLocalizations)
         string nativeName = info.NativeName;
         if (info.TextInfo.ANSICodePage != 0x4e4)
            nativeName = nativeName + string.Format(" ({0})", info.EnglishName);
         languages.Add(new SelectItem { Value = info.Name, Text = nativeName });
      return languages;

14 thoughts on “Creating Page Type Classes from Page Types in EPiServer CMS 7

  1. I’ve been waiting for someone to do this. Nice one :)

  2. Daniel says:

    Same here! Nice.

  3. Alf says:

    Please give me feedback if you find improvements or if something’s wrong.

  4. Martin S. says:

    Sweet work Alf.

  5. Øyvind WH says:

    Great tool.

    In my case I got an asynchrony error on some pages which stopped the Forms View (all property view) in Edit View. The 500 error came because the model and the base class had the same name on a property f.eks. MetaTitle. When removing the one in the model, Edit View worked as normal.

    500 Internal Server Error

    Errormessage in response: Sequence contains more than one matching element.

    [InvalidOperationException: Sequence contains more than one matching element]
    System.Linq.Enumerable.SingleOrDefault(IEnumerable`1 source, Func`2 predicate) +4140586
    EPiServer.Cms.Shell.UI.ContentDataMetadataProvider.GetAttributes(Type modelType, PropertyData prop) +165
    EPiServer.Cms.Shell.UI.<GetContentDataMetaData>d__4.MoveNext() +325
    System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) +536
    System.Linq.Enumerable.ToList(IEnumerable`1 source) +80
    EPiServer.Cms.Shell.UI.ContentDataMetadataProvider.GetMetadataForProperties(Object container, Type containerType) +254
    EPiServer.Shell.ObjectEditing.ExtensibleMetadataProvider.GetMetadataForProperties(ExtendedMetadata parent, Object container, Type containerType, IMetadataProvider customProvider) +70
    EPiServer.Shell.ObjectEditing.ExtendedMetadata.get_Properties() +236
    EPiServer.Cms.Shell.UI.ObjectEditing.SettingsPanelMetadataExtender.ModifyMetadata(ExtendedMetadata metadata, IEnumerable`1 attributes) +44
    EPiServer.Shell.ObjectEditing.ExtensibleMetadataProvider.ApplyExtendersToMetadata(ExtendedMetadata metadata, IEnumerable`1 attributes, IEnumerable`1 metadataHandlers) +122
    EPiServer.Shell.ObjectEditing.ExtensibleMetadataProvider.CreateMetadata(IEnumerable`1 attributes, Type containerType, Func`1 modelAccessor, Type modelType, String propertyName) +462
    System.Web.Mvc.AssociatedMetadataProvider.GetMetadataForType(Func`1 modelAccessor, Type modelType) +130
    EPiServer.Shell.UI.Rest.MetadataStore.Get(String type, String modelAccessor) +74
    lambda_method(Closure , ControllerBase , Object[] ) +179
    System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +261
    System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +39
    System.Web.Mvc.<>c__DisplayClass13.<InvokeActionMethodWithFilters>b__10() +124
    System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +309
    System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +854224
    EPiServer.Shell.Services.Rest.RestControllerBase.ExecuteCore() +241
    System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +334
    EPiServer.Shell.Services.Rest.RestHttpHandler.ProcessRequest(HttpContextBase httpContext) +274
    System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +913
    System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +165

    • Alf says:

      Hi Øyvind,
      When you say “Model” I assume you mean “PageType”.

      Do you mean that you had for example
      public virtual string MetaTitle { get; set; } on your base class as well as you Page Type class?

  6. Øyvind WH says:

    When I create a pagetype with the same named property in both the the pagetype and the base class I got the error. I’ve tested the same behavior in the Alloy sample site, but the same behavior is not there. My site is an old site (2003->) with loads of custom properties etc. Just wanted to post the error message here since probably be more people who uses this tool on older site upgrades.

  7. Jöran says:

    Great code! I found one issue though. I got null exceptions on rows 212 and 213:

    /*1*/ definition.EditCaption.Replace(“\””, “‘”),
    /*2*/ definition.HelpText.Replace(“\””, “‘”),

    My page types didn’t have any edit captions or help texts. My solution was:

    /*1*/ (definition.EditCaption ?? definition.Name).Replace(“\””, “‘”),
    /*2*/ (definition.HelpText ?? “”).Replace(“\””, “‘”),

  8. Worked like a charm! Nice work

  9. Øyvind says:

    This tool works great, but there is one thing that is important to remember if you are using the tool on pagetypes and properties made by PageTypeBuilder (PTB) and have used PropertyGropus (PTB 2.0). The naming of properties in propertygroups will be group-propertyname. Like this; ImagePropertyGroup-Image. The “problem” is when you are trying to create properties the tools removes the – and the propertyname will be different from the original one (and the same with the value). The tool is logging this, but it’s easy to forget when you do migrations.

    Old name vs. new name in strongly taped EPiServer manner:
    ImagePropertyGroup-Image to ImagePropertyGroupImage

    • Alf says:

      I see what you mean. In my project I had not used PTB but when I have worked with PTB projects there has always been a deeper discussion about Converting PTB classes vs. Throw PTB classes out and make new based on the pagetypes in the database

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: