Improved CategoryList editor descriptor for EPiServer

Mattias Olsson 2015-05-06 01:08:31

Of course, you could easily accomplish a filtered category list with the [SelectOne] or [SelectMany] attribute on a string property together with a ISelectionFactory, but I really don't like having to parse that string into a list of integers. I want a CategoryList! Period. :) My idea was to override the editor descriptor for CategoryList that's built into EPiServer. To be able to configure root category id or name, I created a custom attribute.

L'attribut

I added three root category configuration properties to my attribute, RootCategoryId, RootCategoryName and RootCategoryAppSettingKey. One of them is selected in the following priority:

  1. RootCategoryId
  2. RootCategoryName
  3. RootCategoryAppSettingKey (Name of appSetting key with root category id value)
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class CategorySelectionAttribute : Attribute
{
    /// 
    ///  ID of the root category.
    /// 
    public int RootCategoryId { get; set; }

    /// 
    /// Name of the root category.
    /// 
    public string RootCategoryName { get; set; }

    /// 
    /// The appSetting key containing the root category id to use.
    /// 
    public string RootCategoryAppSettingKey { get; set; }

    public int GetRootCategoryId()
    {
        if (RootCategoryId > 0)
        {
            return RootCategoryId;
        }

        if (!string.IsNullOrWhiteSpace(RootCategoryName))
        {
            var category = Category.Find(RootCategoryName);

            if (category != null)
            {
                return category.ID;
            }
        }

        if (!string.IsNullOrWhiteSpace(RootCategoryAppSettingKey))
        {
            string appSettingValue = ConfigurationManager.AppSettings[RootCategoryAppSettingKey];
            int rootCategoryId;

            if (!string.IsNullOrWhiteSpace(appSettingValue) && int.TryParse(appSettingValue, out rootCategoryId))
            {
                return rootCategoryId;
            }
        }

        return Category.GetRoot().ID;
    }
}


Editor descriptor

To make things easy I inherited the CategoryListEditorDescriptor in EPiServer and set the EditorDescriptorBehavior to OverrideDefault. What I'm doing in the code is to check if one of the attributes is a CategorySelectionAttribute, and if true, I replace the settings that is passed to the dojo widget (epi-cms.widget.CategorySelector). Lucky for me, the category widget in EPiServer already has support for specifying a root category. :)

[EditorDescriptorRegistration(TargetType = typeof(CategoryList), EditorDescriptorBehavior = EditorDescriptorBehavior.OverrideDefault)]
public class CustomCategoryListEditorDescriptor : CategoryListEditorDescriptor
{
    public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable attributes)
    {
        base.ModifyMetadata(metadata, attributes);
        var categorySelectionAttribute = 
            attributes.OfType<CategorySelectionAttribute>().FirstOrDefault();

        if (categorySelectionAttribute != null)
        {
            metadata.EditorConfiguration["root"] =
                categorySelectionAttribute.GetRootCategoryId();
            return;
        }

        var contentTypeCategorySelectionAttribute =
            metadata.ContainerType.GetCustomAttribute<CategorySelectionAttribute>(true);

        if (contentTypeCategorySelectionAttribute != null)
        {
            metadata.EditorConfiguration["root"] =
                contentTypeCategorySelectionAttribute.GetRootCategoryId();
        }
    }
}


Usage examples

[CategorySelection(RootCategoryId = 123)]
public virtual CategoryList MyCategories { get; set; }

[CategorySelection(RootCategoryName = "MyRootCategory")]
public virtual CategoryList MyCategories { get; set; }

[CategorySelection(RootCategoryAppSettingKey = "NewsRootCategoryId")]
public virtual CategoryList MyCategories { get; set; }

The last example uses an appSetting to set root category id

<appSettings>
    <add key="NewsRootCategoryId" value="123" />
</appSettings>


Using the attribute on a content type class

You can also use the CategorySelectionAttribute on a content type class to set root category for all CategoryList properties, including the built-in PageCategory property. Property attribute targets have higher priority than class so you can still override class settings for specific properties.

[CategorySelection(RootCategoryId = 123)]
[ContentType]
public class StartPage : PageData
{
    // Overrides settings on the class for this property
    [CategorySelection(RootCategoryId = 321)] 
    public virtual CategoryList MyCategory { get; set; }
}

Kontakta oss

Christian Öman

Christian Öman

Sales Executive

Charlotte Tyldhed

Charlotte Tyldhed

CEO Geta Digital Sverige

Kontakta oss

Christian Öman

Christian Öman

Sales Executive

Charlotte Tyldhed

Charlotte Tyldhed

CEO Geta Digital Sverige