November Happy Hour will be moved to Thursday December 5th.

CMS12 - How to use an EditorDescriptor for IList<ContentReference>?

Vote:
 

We have a page in our application with various properties, for which we use AllowedTypes to restrict which content can be selected.
This is an example of such a property:

        [AllowedTypes(typeof(UnderlyingContent)), UIHint(nameof(UnderlyingContentEditorDescriptor))]
        [Display(
            Name = "Default selected underlying",
            GroupName = GroupNames.Products,
            Order = 1030)]
        public virtual ContentReference? DefaultSelectedUnderlying { get; set; }

The accompanying EditorDescriptor looks as follows:

[EditorDescriptorRegistration(TargetType = typeof(ContentReference), UIHint = nameof(UnderlyingContentEditorDescriptor))]
public class UnderlyingContentEditorDescriptor : ContentReferenceEditorDescriptor<UnderlyingContent>
{
    private readonly ISiteSettingsFactory siteSettingsFactory;

    public UnderlyingContentEditorDescriptor(ISiteSettingsFactory siteSettingsFactory)
    {
        this.siteSettingsFactory = siteSettingsFactory;
    }
    public override IEnumerable<ContentReference> Roots
    {
        get
        {
            var siteSettings = this.siteSettingsFactory.Find();

            return new[] { new ContentReference(siteSettings.Value.UnderlyingDetailPage.ID ) };
        }
    }
}

So far, this all works perfectly; when selecting an item, the content selector pop-up neatly opens the correct node to select content from.

We are now trying to accomplish the same for a collection, but this does not work with the same EditorDescriptor:

        [Display(
            Name = "Product calculator underlyings to exclude",
            GroupName = GroupNames.Products,
            Order = 1040,
            Description = "When a underlying is present in the list, the option 'product calculator' on the product detail is hidden")]
        [AllowedTypes(typeof(UnderlyingContent)), UIHint(nameof(UnderlyingContentEditorDescriptor))]
        public virtual IList<ContentReference> ProductCalculatorExcludedUnderlyings { get; set; } = new List<ContentReference>();

Can anyone tell me how to implement an EditorDescriptor for an IList<ContentReference>?
I know of the existence of ContentReferenceListEditorDescriptor, for instance, but I cannot find any examples of implementations that use this.
With the documentation available I have not been able to figure out how it works either.

#306635
Aug 11, 2023 10:39
Vote:
 

Hi Edwin,

You will more than likely need to introduce a custom model for the IList e.g.

public class CustomListObjectItem
{
    public virtual ContentReference Content {get; set;}
}

....

public virtual IList<CustomListObjectItem> ListContent

You can then add your EditorDescriptor etc to the property of the custom model.

Let me know if you need more help.

Thanks

Paul

#306639
Aug 11, 2023 11:43
Vote:
 

Hi Paul,

Thank you for your swift response.
That does sound like a solution that would work in and of itself, but in our context it would not be the preferred solution.
Allow me to elaborate.

This topic came up as part of a migration from CMS11 to CMS12.
In CMS11, the IList<ContentReference> property looked like the following:

        [Display(
            Name = "Product calculator underlyings to exclude",
            GroupName = GroupNames.Products,
            Order = 1040,
            Description = "When a underlying is present in the list, the option 'product calculator' on the product detail is hidden")]
        [UIHint("UnderlyingContent")]
        [AllowedTypes(typeof(UnderlyingContent))]
        [UnderlyingContentRoot]
        public virtual IList<ContentReference> ProductCalculatorExcludedUnderlyings { get; set; }

The related UnderlyingContentRoot, that worked for both ContentReference and IList<ContentReference>, looked as follows:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class UnderlyingContentRootAttribute : Attribute, IMetadataAware
{
    public void OnMetadataCreated(ModelMetadata metadata)
    {
        var extendedMetadata = metadata as ExtendedMetadata;

        var siteSettings = ServiceLocator.Current.GetInstance<ISiteSettingsFactory>().Find();

        if (!siteSettings.HasValue)
        {
            return;
        }

        if (extendedMetadata == null)
        {
            return;
        }

        if (siteSettings.Value.UnderlyingDetailPage != null)
        {
            extendedMetadata.EditorConfiguration["roots"] = new[] { siteSettings.Value.UnderlyingDetailPage.ID };
        }
    }
}

Unfortunately, IMetadataAware is no longer available in .NET 6.0, but it is hard to imagine that there would not be an equivalent implementation possible in CMS12/.NET 6.0. It actually surprises us that the EditorDescriptors apparently do not work in the same way.

So to get back to your suggestion: the introduction of an intermediate model would introduce the challenge of migrating the current data to said new model structure as well. This was also not the only property that was implemented along these lines.

Ideally, we would just keep using IList<ContentReference> as before.

#306642
Edited, Aug 11, 2023 12:26
Vote:
 

As a collegue of Edwin, we solved it by adding an extra EditorDescriptorRegistration for the IList<ContentReferences>, the behaviour is key to support both

For future references, these are the attributes to add

[EditorDescriptorRegistration(TargetType = typeof(ContentReference), UIHint = nameof(VirtualUnderlyingContentDescriptor))]
[EditorDescriptorRegistration(TargetType = typeof(IList<ContentReference>), UIHint = nameof(VirtualUnderlyingContentDescriptor), EditorDescriptorBehavior = EditorDescriptorBehavior.ExtendBase)]
#306644
Aug 11, 2023 13:20
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.