Improved CategoryList editor descriptor for EPiServer
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:
- RootCategoryId
- RootCategoryName
- 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
Kontakta oss