Views: 8684
Number of votes: 3
Average rating:

Custom root for category selection and EditorDescriptorBehavior

In this blog post I’ll describe how to set a custom root for a category property. The code for this is pretty simple:

[EditorDescriptorRegistration(TargetType = typeof(CategoryList), UIHint = "customcategoryroot", EditorDescriptorBehavior = EditorDescriptorBehavior.ExtendBase)]
public class CategoryListEditorDescriptor : EditorDescriptor
{
    public override void ModifyMetadata(EPiServer.Shell.ObjectEditing.ExtendedMetadata metadata, System.Collections.Generic.IEnumerable attributes)
    {
        metadata.EditorConfiguration["root"] = Category.GetRoot().Categories[0].ID;
    }
}

The actual logic is pretty simple, set the “root” property to the widget that is responsible for editing (this only works in 7.5 due to some changes in the client widget to support this). In this sample the settings will only affect any CategoryLists tagged with the UIHint “customcategoryroot” but removing this will make the descriptor affect all category-properties, including the built in one.

EditorDescriptorBehavior

So how does the editing system know how to edit the property since we are not defining an editor widget? The answer to this is that we have defined the new attribute property EditorDescriptorBehavior. In this case we are setting this property to EditorDescriptorBehavior.ExtendBase which means that we want to run the default editor descriptors for the type (in this case the default descriptor for CategoryList without any UIHint) first and then this editor descriptor. This makes is possible to extend the behavior without taking having to know about the class responsible for the default implementation. There are a few posibilities to set for this property which the SDK explains like this:

/// 
/// Adds this descriptor to the list of descriptors for the given type and ui hint combination.
/// 
Default = 0,
/// 
/// Adds this descriptor to the list of descriptors for the given type and ui hint combination and
/// makes sure that the descriptors registered without a ui hint for the type are called before this descriptor.
/// 
/// This is only valid in combination with a ui hint.
ExtendBase = 1,
/// 
/// Removes any existing descriptors for the type/ui hint combination and then adds this descriptor.
/// 
OverrideDefault = 2,
/// 
/// Adds this descriptor last in the list of descriptors for the given type and ui hint combination.
/// 
/// If several descriptors are defined this way the order of execution is undefined.PlaceLast = 3

There is one known limitation that you might to be aware of: You cannot extend an editor descriptor that already has a UIHint defined. So if you want to extend ContentReferences hinted with for example a “Page”, you cannot override this behavior direcly (though this can be simply archieved which will be content for another blog post).

Dec 04, 2013

Chris Magee
(By Chris Magee, 7/31/2014 2:13:57 AM)

This doesn't seem to work in 7.5.

Visually, this works great. When you select categories and save, on refresh the categories are no longer selected.

Do you know why this might happen?

linus.ekstrom
(By linus.ekstrom, 8/1/2014 7:43:59 PM)

@Christopher: I just tried on a site using the latest packages and it seems to work fine with me. In my testing I added a few categories under a specific node that I set as the root node for a specific property and I tried selecting categories both on the first level but also further down the hierarchy and they are still selected after I save and switch content and then go back to the item. Could you try getting the latest packages to see if that does any differences. If this does not solve your issues, could you write your repro steps more in detail?

Neil F
(By Neil F, 11/14/2014 2:55:07 PM)

This is really useful, thanks. I'm interested to understand how you knew the name of EditorConfiguration Dictionary key to set, do you know of other existing keys? Specifically we have a requirement to sort the categories and was hoping it would be as straightforward as using a similar setting.

linus.ekstrom
(By linus.ekstrom, 11/14/2014 4:24:45 PM)

Hi!

Actually, the reason for this specific property and my knowledge of this is that we heard the request to be able to changes this a few times. So we moved out the root for the responsible editor to a setting that can be overridden on the server side. From what I can see, this setting is the only settings that can currently be altered.

Neil F
(By Neil F, 11/17/2014 2:23:07 PM)

Hi Linus - thanks for that, will try and dissuade the customer for now!

radoslaw.szymanski
(By radoslaw.szymanski, 1/14/2015 11:17:12 PM)

Hi Linus,



i tried this method to write over the PageCategory (episerver default property) without any success.

Do you have any suggestions how i can set new root on the episerver default categorylist property Category (PageCategory)

linus.ekstrom
(By linus.ekstrom, 1/15/2015 2:54:02 PM)

Hi Radek!

I noticed you had to do a few changes to make it work for the default categories. First of all, I moved the logic for setting the editor setting from the constructor to a method. This is mostly for performance reasons since the base class would otherwise do some things that we don't need. The trick to make it work (except the obvious thing to remove the UIHint), is to change EditorDescriptorBehavior to EditorDescriptorBehavior.PlaceLast. The reason for this is that we have removed the UI hint and therefore want to have two editor descriptors for the same type/uihint combination. I hope this solves your issue.

linus.ekstrom
(By linus.ekstrom, 1/29/2015 5:05:42 PM)

This code sample works for me:

using EPiServer.Core;
using EPiServer.DataAbstraction;
using EPiServer.Shell.ObjectEditing.EditorDescriptors;

namespace UIExtensionSamples.EditorDescriptors
{
[EditorDescriptorRegistration(TargetType = typeof(CategoryList), EditorDescriptorBehavior = EditorDescriptorBehavior.PlaceLast)]
public class CustomCategoryRootEditorDescriptor : EditorDescriptor
{
public CustomCategoryRootEditorDescriptor()
{
//We set the root property to the editor to alter the root category for the category picker dialog.
//EditorConfiguration["root"] = Category.GetRoot().Categories[0].ID;
}

public override void ModifyMetadata(EPiServer.Shell.ObjectEditing.ExtendedMetadata metadata, System.Collections.Generic.IEnumerable attributes)
{
metadata.EditorConfiguration["root"] = Category.GetRoot().Categories[0].ID;
}

public const string CustomCategoryRoot = "customcategoryroot";
}
}

radoslaw.szymanski
(By radoslaw.szymanski, 1/30/2015 9:07:58 AM)

Hi Linus,

sorry for my prev post. I noticed it works great.
I have another question that you might help me out with?
If i would like to change the name of episervers property with name "Category" to "Related products"
JUST when a particular block is loaded.
So, all over the place it should the name Category be used but when user loads a special block it should state "Related Products"

[EditorDescriptorRegistration(TargetType = typeof(CategoryList), EditorDescriptorBehavior = EditorDescriptorBehavior.PlaceLast)]
public class CategoryListEditorDescriptor : EPiServer.Shell.ObjectEditing.EditorDescriptors.EditorDescriptor
{
public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable attributes)
{

dynamic contentMetadata = metadata;
var ownerContent = contentMetadata.OwnerContent as IContent;
var ownerType = ownerContent.GetType().BaseType;

if (ownerType != null && ownerType.Name == "SpecialBlock")
{
var categoryService = ServiceLocator.Current.GetInstance();
var category = categoryService.GetCategoryStartNode(Categories.Industry);
if (category != null)
EditorConfiguration["root"] = category.ID;

base.ModifyMetadata(metadata, attributes);
if (metadata.PropertyName == "icategorizable_category")
{
metadata.DisplayName = "Related products";
metadata.ShortDisplayName = "Related products";
metadata.SimpleDisplayText = "Related products";
}
}

}

}

I have tried altering the EditorDescriptorBehavior.PlaceLast to different options without any success.

Maybe doing something wrong?

radoslaw.szymanski
(By radoslaw.szymanski, 2/2/2015 10:47:39 PM)

Also tried setting a new name on init (IInitializableModule)

Manage to do it on all properties that are not episerver default.
This means that episerver changes the name somewhere down the road back to "Category".

linus.ekstrom
(By linus.ekstrom, 2/3/2015 4:56:32 PM)

Display names for properties are handled by localization in EPiServer. It seems that this is done after the metadata is created and that's the reason why changing the metadata has no effect. I would simply add a new Catagory property for your custom type and hide the built in category property for your property( by setting metadata.ShowForEdit = false;).

Yagnik Jadav
(By Yagnik Jadav, 3/26/2019 1:27:17 PM)

Hey,

I am trying to add custom root node in Select Image dialog decorated with UIHint.Image.

I am using custom media content peovider and EntryPoint is set under root and not inside asset folder.

So  I would like to extend existing ImageReference Editor descriptor for type IContentImage and UIHint.Image to include custom root in left side of dialog.

Your help will be appreciated.

Regards Yagnik

linuse
(By linuse, 3/26/2019 1:59:27 PM)

Hi Yagnik,

This technique can be used whenever you have an editor that uses different properties. Basically, properties set will override default property values and I've recently used it for another scenario that I plan to blog about.

In your case, you need to have a look at the editor widget that you want to change and see if it has a property to override, and in that case you can apply it either to all properties of a given type, or properties with a given UIHint for editors.

Regards
Linus

Please login to comment.