EPiServer overwrites globalization of DisplayAttribute - Is there an extension point to modify generated metadata?

Vote:
 

Hi,

I'm attempting to translate property names using the DisplayAttribute without success. We always get the neutral resource text.

[Display(Name = "Header", ResourceType = typeof(PageResources))]
public virtual String Header { get; set; }

It works if we build a custom attribute that implements IMetadataAware. A breakpoint in our implementation of IMetadataAware.OnMetadataCreated shows that CurrentUICulture == "sv-SE". Creating a new DisplayAttribute with the above properties does produce the translated value from a call to DisplayAttribute.GetName().

I've tracked down the actual call to DisplayAttribute.GetName() to ExtendedMetadata.ReadSettingsFromDisplayAttributes which reads the correct translated values. However, ContentDataMetadataProvider.GetContentDataMetaData overwrites the correct value with the result of PropertyData.TranslateDisplayName (which either calls LocalizationService or defaults to, in this case, a bad value).

I would really want to continue to use the existing DisplayAttribute instead of going with a custom solution, both due to being a "standard attribute", and third party tools identify it correctly (like intellisense for resource keys in Resharper).

I've identified one workaround which makes my skin crawl; building a custom LocalizationService which would take the xpaths EPiServer has generated internally and use these to calculate the resource files to query. This means that the DisplayAttribute is still present on the property, but not in use. It would just happen to work as long as the resources files are named correctly and there are no gnomes messing around server-side.

I've tried reproducing this in a unit-test, without mocking items since my code shouldn't change the outcome. It was all fun until there were a few dozens IInitializableModule required...

1. Is this a bug that should be reported? If so, what's the requirements for a bug report?

2. Is there an extensibility point to modify the metadata without using IMetadataAware? I do not want to stop using the DisplayAttribute. Such extensibility would need to provide access to the PropertyInfo or the attributes present so that I can call metadata.Name = displayAttribute.GetName(). Or the ExtendedMetaData already generated since it seems to have references to all attributes.

3. Can I replace the builtin ContentDataMetadataProvider that's used, to do whatever I want to provide the glorious metadata? (I would probably just wrap it to process the metadata generated before returning them to EPiServer.)

// Simon

#88588
Jul 17, 2014 22:58
Vote:
 

I can answer my second and third point; there's an IMetadataExtender interface that can do whatever it wants with the metadata. Some reflection is needed to access the attributes, but I see no issues with this.

I still consider this as a bug, but now I have a workaround that I'm satisfied with.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Mvc;
using EPiServer.Core;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using EPiServer.Shell.ObjectEditing;

[InitializableModule]
internal class MetadataExtenderModule : IInitializableModule {
    public void Initialize(InitializationEngine context) {
        var registry = context.Locate.Advanced.GetInstance();
        registry.RegisterMetadataHandler(typeof (ContentData), new ContentDataMetadataExtender());
    }

    public void Uninitialize(InitializationEngine context) {
    }

    public void Preload(String[] parameters) {
    }
}

/// 
///     

/// An IMetadataExtender which reapplies values that EPiServer has overwritten in /// ContentDataMetadataProvider.GetContentDataMetaData. This affects all values /// where EPiServer expect the LocalizationService to handle translations. ///

///

/// In case of DisplayName it will fallback to the property name, which comes /// from the neutral resource file. We reset it to a call to DisplayAttribute.GetName /// so we get the text for CurrentUICulture. ///

///
public class ContentDataMetadataExtender : IMetadataExtender { public void ModifyMetadata(ExtendedMetadata metadata, IEnumerable attributes) { foreach (var propertyMetadata in metadata.Properties) { var property = propertyMetadata.ContainerType.GetProperty(propertyMetadata.PropertyName); if (property != null) { var propertyAttributes = property.GetCustomAttributes(true) .OfType() .ToArray(); ModifyMetadataFromAttributes(propertyMetadata, propertyAttributes); } } } private void ModifyMetadataFromAttributes(ModelMetadata metadata, Attribute[] attributes) { ModifyFromDisplayAttribute(metadata, attributes.OfType().SingleOrDefault()); } public void ModifyFromDisplayAttribute(ModelMetadata metadata, DisplayAttribute attribute) { if (attribute != null && attribute.ResourceType != null) { metadata.DisplayName = attribute.GetName(); metadata.Description = attribute.GetDescription(); } } }

// Simon

#88613
Jul 18, 2014 22:12
* 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.