Haven't done this myself, but isn't it so that you might need to save modified property definition?
A ContentScannerExtensions is a part of episervers modeltype discovery and syncronization process, so i should not have to save anything. I think :)
From API docs for AssignValuesToProperties(): "Override to modify the properties model done by default assigner."
The default assigner will for example set Order from the DisplayAttribute...
Aaaaaaaaaaaaah, is it that you cant change sortorder from code? Maybe i need to make an initialization module that runs after ModelSyncInitialization!
"So as of EPiServer 7 we have changed this behavior, sort index is never enforced when saving properties but always enforced (1..n) if you re-order using drag-n-drop or use the move up and down arrows in admin mode. To make sure the sort order is consistent we order first by index and for properties that do not have a sort index we “put them at the end” and order by the ID, so order of creation basically."
And then in the sync code:
if (!propertyDefinition.NullableFieldOrder.HasValue && model.Order.HasValue) { propertyDefinition.FieldOrder = model.Order.Value; }
which looks like it will only update fieldorder if it's not having a value already!
I solved it like this:
public class ContentTypeSyncronizationInitialization : IInitializableModule { private ILogger _logger = LogManager.GetLogger(typeof(ContentTypeSyncronizationInitialization)); private IContentTypeRepository _contentTypeDefinitionRepository; private IPropertyDefinitionRepository _propertyTypeDefinitionRepository; public void Initialize(InitializationEngine context) { _contentTypeDefinitionRepository = context.Locate.Advanced.GetInstance<IContentTypeRepository>(); _propertyTypeDefinitionRepository = context.Locate.Advanced.GetInstance<IPropertyDefinitionRepository>(); var contentDefinitionTypes = _contentTypeDefinitionRepository.List(); var contentDataType = typeof(EPiServer.Core.IContentData); var contentTypes = typeof(ContentTypeSyncronizationInitialization).Assembly .GetTypes().Where(x => contentDataType.IsAssignableFrom(x)); foreach (var contentType in contentTypes) { var contentTypeDefinition = contentDefinitionTypes.SingleOrDefault(x => x.ModelType == contentType); if (contentTypeDefinition == null) continue; _logger.Information("Updating content type {0} ({1})", contentTypeDefinition.DisplayName, contentTypeDefinition.Name); UpdatePropertyDefinitionsForContentTypeDefinition(contentType, contentTypeDefinition); } } private void UpdatePropertyDefinitionsForContentTypeDefinition(Type contentType, ContentType contentTypeDefinition) { var propertyDefinitions = _propertyTypeDefinitionRepository.List(contentTypeDefinition.ID); var propertySortOrderType = contentType.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(x => typeof(IPropertySortOrder).IsAssignableFrom(x)); if (propertySortOrderType == null) return; var sortOrders = (IPropertySortOrder)Activator.CreateInstance(propertySortOrderType); foreach (var propDef in propertyDefinitions) { var sortOrder = sortOrders.GetSortOrder(propDef.Name); if (sortOrder > -1 && sortOrder != propDef.FieldOrder) { _logger.Information("- Updating property {0}'s sort order to {1}", propDef.Name, sortOrder); var propDefWritable = propDef.CreateWritableClone(); propDefWritable.FieldOrder = sortOrder; _propertyTypeDefinitionRepository.Save(propDefWritable); } } } public void Uninitialize(InitializationEngine context) { } }
To sort my properties i just add a nested class to my pagetype like this:
public class MyPage : PageBase { protected class PropertySortOrder : PropertySortOrder<MyPage> { public PropertySortOrder() { Add(x => x.Heading); Add(x => x.MainBody); } } public virtual XhtmlString MainBody {get;set;} public virtual string Heading {get;set;} }
Properties will be sorted in the order they are added.
IPropertySortOrder and PropertySortOrder<>:
public interface IPropertySortOrder { int GetSortOrder(string propertyName); } public abstract class PropertySortOrder<TContentType> : IPropertySortOrder { private Dictionary<string, int> _sortOrders = new Dictionary<string, int>(); private int _order = 1; protected void Add<TReturnType>(Expression<Func<TContentType, TReturnType>> expr) { var memberExpr = expr.Body as MemberExpression; if (memberExpr == null) return; var propName = memberExpr.Member.Name; _sortOrders[propName] = _order++; } public int GetSortOrder(string propertyName) { int sortOrder = -1; return _sortOrders.TryGetValue(propertyName, out sortOrder) ? sortOrder : -1; } }
I've made a custom ContentScannerExtension that sets Order on the PropertyDefinitionModel in AssignValuesToProperties() method. I can see that i runs but the properties aren't sorted.
What could be wrong?
The scanner extensions looks like this and the log shows that properties gets the order i expect.