diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs index ce9e2b7586..c76f4b3a9d 100644 --- a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs @@ -27,5 +27,14 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "locked")] [ReadOnly(true)] public bool Locked { get; set; } + + /// + /// This is required for the UI editor to know if this particular property belongs to + /// an inherited item or the current item. + /// + [DataMember(Name = "contentTypeId")] + [ReadOnly(true)] + public int ContentTypeId { get; set; } + } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs index 2c9f9939d1..1aa6825e19 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs @@ -255,12 +255,14 @@ namespace Umbraco.Web.Models.Mapping .ForMember(g => g.Editor, expression => expression.Ignore()) .ForMember(g => g.View, expression => expression.Ignore()) .ForMember(g => g.Config, expression => expression.Ignore()) + .ForMember(g => g.ContentTypeId, expression => expression.Ignore()) .ForMember(g => g.Locked, exp => exp.Ignore()); config.CreateMap() .ForMember(g => g.Editor, expression => expression.Ignore()) .ForMember(g => g.View, expression => expression.Ignore()) .ForMember(g => g.Config, expression => expression.Ignore()) + .ForMember(g => g.ContentTypeId, expression => expression.Ignore()) .ForMember(g => g.Locked, exp => exp.Ignore()); #endregion diff --git a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs index f8d0af4bdf..6409fe8009 100644 --- a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs @@ -41,6 +41,26 @@ namespace Umbraco.Web.Models.Mapping .FirstOrDefault(x => x != null); } + /// + /// Gets the content type that defines a property group, within a composition. + /// + /// The composition. + /// The identifier of the property type. + /// The composition content type that defines the specified property group. + private static IContentTypeComposition GetContentTypeForPropertyType(IContentTypeComposition contentType, int propertyTypeId) + { + // test local property types + if (contentType.PropertyTypes.Any(x => x.Id == propertyTypeId)) + return contentType; + + // test composition property types + // .ContentTypeComposition is just the local ones, not recursive, + // so we have to recurse here + return contentType.ContentTypeComposition + .Select(x => GetContentTypeForPropertyType(x, propertyTypeId)) + .FirstOrDefault(x => x != null); + } + protected override IEnumerable> ResolveCore(IContentTypeComposition source) { // deal with groups @@ -58,7 +78,7 @@ namespace Umbraco.Web.Models.Mapping ContentTypeId = source.Id }; - group.Properties = MapProperties(tab.PropertyTypes, tab.Id, false); + group.Properties = MapProperties(tab.PropertyTypes, source, tab.Id, false); groups.Add(group); } @@ -85,7 +105,7 @@ namespace Umbraco.Web.Models.Mapping ParentTabContentTypeNames = new[] { definingContentType.Name } }; - group.Properties = MapProperties(tab.PropertyTypes, tab.Id, true); + group.Properties = MapProperties(tab.PropertyTypes, definingContentType, tab.Id, true); groups.Add(group); } @@ -94,14 +114,20 @@ namespace Umbraco.Web.Models.Mapping // add generic properties local to this content type var entityGenericProperties = source.PropertyTypes.Where(x => x.PropertyGroupId == null); - genericProperties.AddRange(MapProperties(entityGenericProperties, PropertyGroupBasic.GenericPropertiesGroupId, false)); + genericProperties.AddRange(MapProperties(entityGenericProperties, source, PropertyGroupBasic.GenericPropertiesGroupId, false)); // add generic properties inherited through compositions var localGenericPropertyIds = genericProperties.Select(x => x.Id).ToArray(); var compositionGenericProperties = source.CompositionPropertyTypes .Where(x => x.PropertyGroupId == null // generic && localGenericPropertyIds.Contains(x.Id) == false); // skip those that are local - genericProperties.AddRange(MapProperties(compositionGenericProperties, PropertyGroupBasic.GenericPropertiesGroupId, true)); + foreach (var compositionGenericProperty in compositionGenericProperties) + { + var definingContentType = GetContentTypeForPropertyType(source, compositionGenericProperty.Id); + if (definingContentType == null) + throw new Exception("PropertyType with id=" + compositionGenericProperty.Id + " was not found on any of the content type's compositions."); + genericProperties.AddRange(MapProperties(new [] { compositionGenericProperty }, definingContentType, PropertyGroupBasic.GenericPropertiesGroupId, true)); + } // if there are any generic properties, add the corresponding tab if (genericProperties.Any()) @@ -165,7 +191,7 @@ namespace Umbraco.Web.Models.Mapping return groups.OrderBy(x => x.SortOrder); } - private IEnumerable MapProperties(IEnumerable properties, int groupId, bool inherited) + private IEnumerable MapProperties(IEnumerable properties, IContentTypeBase contentType, int groupId, bool inherited) { var mappedProperties = new List(); @@ -179,20 +205,21 @@ namespace Umbraco.Web.Models.Mapping mappedProperties.Add(new TPropertyType { - Id = p.Id, - Alias = p.Alias, - Description = p.Description, - Editor = p.PropertyEditorAlias, - Validation = new PropertyTypeValidation { Mandatory = p.Mandatory, Pattern = p.ValidationRegExp }, - Label = p.Name, - View = propertyEditor.ValueEditor.View, - Config = propertyEditor.PreValueEditor.ConvertDbToEditor(propertyEditor.DefaultPreValues, preValues) , - //Value = "", - GroupId = groupId, - Inherited = inherited, - DataTypeId = p.DataTypeDefinitionId, - SortOrder = p.SortOrder - }); + Id = p.Id, + Alias = p.Alias, + Description = p.Description, + Editor = p.PropertyEditorAlias, + Validation = new PropertyTypeValidation {Mandatory = p.Mandatory, Pattern = p.ValidationRegExp}, + Label = p.Name, + View = propertyEditor.ValueEditor.View, + Config = propertyEditor.PreValueEditor.ConvertDbToEditor(propertyEditor.DefaultPreValues, preValues), + //Value = "", + GroupId = groupId, + Inherited = inherited, + DataTypeId = p.DataTypeDefinitionId, + SortOrder = p.SortOrder, + ContentTypeId = contentType.Id + }); } return mappedProperties;