diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index 9f5fb22966..92195c9271 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -126,7 +126,7 @@ namespace Umbraco.Core { foreach (var m in ApplicationEventsResolver.Current.ApplicationEventHandlers.OfType()) { - m.ConfigureMappings(configuration); + m.ConfigureMappings(configuration, ApplicationContext); } }); } diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index 860acdd852..1ca6ff4b7c 100644 --- a/src/Umbraco.Core/Models/ContentExtensions.cs +++ b/src/Umbraco.Core/Models/ContentExtensions.cs @@ -158,7 +158,9 @@ namespace Umbraco.Core.Models public static IEnumerable GetNonGroupedProperties(this IContentBase content) { var propertyIdsInTabs = content.PropertyGroups.SelectMany(pg => pg.PropertyTypes).Select(pt => pt.Id); - return content.Properties.Where(property => propertyIdsInTabs.Contains(property.PropertyTypeId) == false); + return content.Properties + .Where(property => propertyIdsInTabs.Contains(property.PropertyTypeId) == false) + .OrderBy(x => x.PropertyType.SortOrder); } /// @@ -173,7 +175,8 @@ namespace Umbraco.Core.Models return content.Properties .Where(property => propertyGroup.PropertyTypes .Select(propertyType => propertyType.Id) - .Contains(property.PropertyTypeId)); + .Contains(property.PropertyTypeId)) + .OrderBy(x => x.PropertyType.SortOrder); } /// diff --git a/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs b/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs index 0ca76417de..8ea7c46d64 100644 --- a/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs +++ b/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs @@ -17,6 +17,6 @@ namespace Umbraco.Core.Models.Mapping /// internal interface IMapperConfiguration : IApplicationEventHandler { - void ConfigureMappings(IConfiguration config); + void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext); } } diff --git a/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs b/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs index 8bde82d13f..fcc3410f46 100644 --- a/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs +++ b/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs @@ -10,6 +10,6 @@ namespace Umbraco.Core.Models.Mapping /// internal abstract class MapperConfiguration : ApplicationEventHandler, IMapperConfiguration { - public abstract void ConfigureMappings(IConfiguration config); + public abstract void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/PropertyGroup.cs b/src/Umbraco.Core/Models/PropertyGroup.cs index 044ecd164e..94ed57569a 100644 --- a/src/Umbraco.Core/Models/PropertyGroup.cs +++ b/src/Umbraco.Core/Models/PropertyGroup.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Specialized; +using System.Diagnostics; using System.Reflection; using System.Runtime.Serialization; using Umbraco.Core.Models.EntityBase; @@ -12,6 +13,7 @@ namespace Umbraco.Core.Models /// [Serializable] [DataContract(IsReference = true)] + [DebuggerDisplay("Id: {Id}, Name: {Name}")] public class PropertyGroup : Entity, IEquatable { private string _name; diff --git a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs index 5c3c25e28b..e9e8121860 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs @@ -3,12 +3,14 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using AutoMapper; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; +using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Models.Mapping; namespace Umbraco.Tests.Models.Mapping @@ -22,6 +24,11 @@ namespace Umbraco.Tests.Models.Mapping } + protected override DatabaseBehavior DatabaseTestBehavior + { + get { return DatabaseBehavior.NewSchemaPerFixture; } + } + protected override void FreezeResolution() { PropertyEditorResolver.Current = new PropertyEditorResolver( @@ -30,6 +37,113 @@ namespace Umbraco.Tests.Models.Mapping base.FreezeResolution(); } + [Test] + public void To_Media_Item_Simple() + { + var contentType = MockedContentTypes.CreateImageMediaType(); + var content = MockedMedia.CreateMediaImage(contentType, -1); + + var result = Mapper.Map>(content); + + AssertBasics(result, content); + + foreach (var p in content.Properties) + { + AssertBasicProperty(result, p); + } + } + + [Test] + public void To_Content_Item_Simple() + { + var contentType = MockedContentTypes.CreateSimpleContentType(); + var content = MockedContent.CreateSimpleContent(contentType); + + var result = Mapper.Map>(content); + + AssertBasics(result, content); + + foreach (var p in content.Properties) + { + AssertBasicProperty(result, p); + } + } + + [Test] + public void To_Content_Item_Dto() + { + var contentType = MockedContentTypes.CreateSimpleContentType(); + var content = MockedContent.CreateSimpleContent(contentType); + + var result = Mapper.Map>(content); + + AssertContentItem(result, content); + } + + [Test] + public void To_Media_Item_Dto() + { + var contentType = MockedContentTypes.CreateImageMediaType(); + var content = MockedMedia.CreateMediaImage(contentType, -1); + + var result = Mapper.Map>(content); + + AssertContentItem(result, content); + } + + #region Assertions + private void AssertBasics(ContentItemBasic result, TPersisted content) + where T : ContentPropertyBasic + where TPersisted : IContentBase + { + Assert.AreEqual(content.Id, result.Id); + Assert.AreEqual(0, result.Owner.UserId); + Assert.AreEqual("admin", result.Owner.Name); + Assert.AreEqual(content.ParentId, result.ParentId); + Assert.AreEqual(content.UpdateDate, result.UpdateDate); + Assert.AreEqual(content.CreateDate, result.CreateDate); + Assert.AreEqual(content.Name, result.Name); + Assert.AreEqual(content.Properties.Count(), result.Properties.Count()); + } + + private void AssertBasicProperty(ContentItemBasic result, Property p) + where T : ContentPropertyBasic + where TPersisted : IContentBase + { + var pDto = result.Properties.SingleOrDefault(x => x.Alias == p.Alias); + Assert.IsNotNull(pDto); + Assert.AreEqual(p.Alias, pDto.Alias); + Assert.AreEqual(p.Id, pDto.Id); + Assert.AreEqual(p.Value, pDto.Value); + } + + private void AssertProperty(ContentItemBasic result, Property p) + where TPersisted : IContentBase + { + AssertBasicProperty(result, p); + + var pDto = result.Properties.SingleOrDefault(x => x.Alias == p.Alias); + Assert.IsNotNull(pDto); + Assert.AreEqual(p.PropertyType.Mandatory, pDto.IsRequired); + Assert.AreEqual(p.PropertyType.ValidationRegExp, pDto.ValidationRegExp); + Assert.AreEqual(p.PropertyType.Description, pDto.Description); + Assert.AreEqual(p.PropertyType.Name, pDto.Label); + Assert.AreEqual(ApplicationContext.Services.DataTypeService.GetDataTypeDefinitionById(p.PropertyType.DataTypeDefinitionId), pDto.DataType); + Assert.AreEqual(PropertyEditorResolver.Current.GetById(p.PropertyType.DataTypeId), pDto.PropertyEditor); + } + + private void AssertContentItem(ContentItemBasic result, T content) + where T : IContentBase + { + AssertBasics(result, content); + + foreach (var p in content.Properties) + { + AssertProperty(result, p); + } + } + #endregion + [Test] public void To_Display_Model() { @@ -38,7 +152,7 @@ namespace Umbraco.Tests.Models.Mapping var mapper = new ContentModelMapper(ApplicationContext, new UserModelMapper()); - var result = mapper.ToContentItemDisplay(content); + var result = Mapper.Map(content); Assert.AreEqual(content.Name, result.Name); Assert.AreEqual(content.Id, result.Id); @@ -77,7 +191,7 @@ namespace Umbraco.Tests.Models.Mapping var mapper = new ContentModelMapper(ApplicationContext, new UserModelMapper()); - var result = mapper.ToContentItemDisplay(content); + var result = Mapper.Map(content); Assert.AreEqual(content.Name, result.Name); Assert.AreEqual(content.Id, result.Id); diff --git a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs index 60fa93fd85..677a425fa9 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs @@ -56,7 +56,7 @@ namespace Umbraco.Tests.TestHelpers var mappers = PluginManager.Current.FindAndCreateInstances(); foreach (var mapper in mappers) { - mapper.ConfigureMappings(configuration); + mapper.ConfigureMappings(configuration, ApplicationContext); } }); } diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 372b0671ae..900fab5090 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -5,6 +5,7 @@ using System.Net; using System.Net.Http; using System.Web.Http; using System.Web.Http.ModelBinding; +using AutoMapper; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; @@ -52,7 +53,7 @@ namespace Umbraco.Web.Editors { var foundContent = ((ContentService) Services.ContentService).GetByIds(ids); - return foundContent.Select(x => _contentModelMapper.ToContentItemDisplay(x)); + return foundContent.Select(Mapper.Map); } /// @@ -67,7 +68,7 @@ namespace Umbraco.Web.Editors { HandleContentNotFound(id); } - return _contentModelMapper.ToContentItemDisplay(foundContent); + return Mapper.Map(foundContent); } /// @@ -85,7 +86,7 @@ namespace Umbraco.Web.Editors } var emptyContent = new Content("", parentId, contentType); - return _contentModelMapper.ToContentItemDisplay(emptyContent); + return Mapper.Map(emptyContent); } /// @@ -124,7 +125,7 @@ namespace Umbraco.Web.Editors { //ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue! // add the modelstate to the outgoing object and throw a 403 - var forDisplay = _contentModelMapper.ToContentItemDisplay(contentItem.PersistedContent); + var forDisplay = Mapper.Map(contentItem.PersistedContent); forDisplay.Errors = ModelState.ToErrorDictionary(); throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Forbidden, forDisplay)); @@ -158,7 +159,7 @@ namespace Umbraco.Web.Editors //return the updated model - var display = _contentModelMapper.ToContentItemDisplay(contentItem.PersistedContent); + var display = Mapper.Map(contentItem.PersistedContent); //lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403 HandleInvalidModelState(display); diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs index 237d75939c..ba5c98d89e 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.Models.ContentEditing { @@ -20,5 +21,11 @@ namespace Umbraco.Web.Models.ContentEditing [Required(AllowEmptyStrings = false)] public string Alias { get; set; } + /// + /// Used internally during model mapping + /// + [IgnoreDataMember] + internal PropertyEditor PropertyEditor { get; set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/BaseContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/BaseContentModelMapper.cs index ed3a13e204..7b938642bb 100644 --- a/src/Umbraco.Web/Models/Mapping/BaseContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/BaseContentModelMapper.cs @@ -20,20 +20,20 @@ namespace Umbraco.Web.Models.Mapping UserMapper = userMapper; } - protected ContentItemDto ToContentItemDtoBase(IContentBase content) - where TPersisted : IContentBase - { - return CreateContent, ContentPropertyDto, TPersisted>(content, null, (propertyDto, originalProperty, propEditor) => - { - propertyDto.IsRequired = originalProperty.PropertyType.Mandatory; - propertyDto.ValidationRegExp = originalProperty.PropertyType.ValidationRegExp; - propertyDto.Alias = originalProperty.Alias; - propertyDto.Description = originalProperty.PropertyType.Description; - propertyDto.Label = originalProperty.PropertyType.Name; - propertyDto.DataType = ApplicationContext.Services.DataTypeService.GetDataTypeDefinitionById(originalProperty.PropertyType.DataTypeDefinitionId); - propertyDto.PropertyEditor = PropertyEditorResolver.Current.GetById(originalProperty.PropertyType.DataTypeId); - }); - } + //protected ContentItemDto ToContentItemDtoBase(IContentBase content) + // where TPersisted : IContentBase + //{ + // return CreateContent, ContentPropertyDto, TPersisted>(content, null, (propertyDto, originalProperty, propEditor) => + // { + // propertyDto.IsRequired = originalProperty.PropertyType.Mandatory; + // propertyDto.ValidationRegExp = originalProperty.PropertyType.ValidationRegExp; + // propertyDto.Alias = originalProperty.Alias; + // propertyDto.Description = originalProperty.PropertyType.Description; + // propertyDto.Label = originalProperty.PropertyType.Name; + // propertyDto.DataType = ApplicationContext.Services.DataTypeService.GetDataTypeDefinitionById(originalProperty.PropertyType.DataTypeDefinitionId); + // propertyDto.PropertyEditor = PropertyEditorResolver.Current.GetById(originalProperty.PropertyType.DataTypeId); + // }); + //} protected ContentItemBasic ToContentItemSimpleBase(IContentBase content) where TPersisted : IContentBase diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs index eb3cda0ed2..efd7dd2c22 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs @@ -15,59 +15,50 @@ namespace Umbraco.Web.Models.Mapping { } - public ContentItemDto ToContentItemDto(IContent content) - { - var result = base.ToContentItemDtoBase(content); - //NOTE: we don't need this for the dto and it's an extra lookup - //result.ContentTypeAlias = content.ContentType.Alias; - //result.Icon = content.ContentType.Icon; - //result.Updator = userMapper.ToUserBasic(content.GetWriterProfile()); - return result; - } - public ContentItemBasic ToContentItemSimple(IContent content) - { - var result = base.ToContentItemSimpleBase(content); - result.ContentTypeAlias = content.ContentType.Alias; - result.Icon = content.ContentType.Icon; - result.Updator = UserMapper.ToUserBasic(content.GetWriterProfile()); - return result; - } + //public ContentItemBasic ToContentItemSimple(IContent content) + //{ + // var result = base.ToContentItemSimpleBase(content); + // result.ContentTypeAlias = content.ContentType.Alias; + // result.Icon = content.ContentType.Icon; + // result.Updator = UserMapper.ToUserBasic(content.GetWriterProfile()); + // return result; + //} - public ContentItemDisplay ToContentItemDisplay(IContent content) - { - //create the list of tabs for properties assigned to tabs. - var tabs = GetTabs(content); + //public ContentItemDisplay ToContentItemDisplay(IContent content) + //{ + // //create the list of tabs for properties assigned to tabs. + // var tabs = GetTabs(content); - var result = CreateContent(content, (display, originalContent) => - { - //fill in the rest - display.Updator = UserMapper.ToUserBasic(content.GetWriterProfile()); - display.ContentTypeAlias = content.ContentType.Alias; - display.Icon = content.ContentType.Icon; + // var result = CreateContent(content, (display, originalContent) => + // { + // //fill in the rest + // display.Updator = UserMapper.ToUserBasic(content.GetWriterProfile()); + // display.ContentTypeAlias = content.ContentType.Alias; + // display.Icon = content.ContentType.Icon; - //set display props after the normal properties are alraedy mapped - display.Name = originalContent.Name; - display.Tabs = tabs; - //look up the published version of this item if it is not published - if (content.Published) - { - display.PublishDate = content.UpdateDate; - } - else if (content.HasPublishedVersion()) - { - var published = ApplicationContext.Services.ContentService.GetPublishedVersion(content.Id); - display.PublishDate = published.UpdateDate; - } - else - { - display.PublishDate = null; - } + // //set display props after the normal properties are alraedy mapped + // display.Name = originalContent.Name; + // display.Tabs = tabs; + // //look up the published version of this item if it is not published + // if (content.Published) + // { + // display.PublishDate = content.UpdateDate; + // } + // else if (content.HasPublishedVersion()) + // { + // var published = ApplicationContext.Services.ContentService.GetPublishedVersion(content.Id); + // display.PublishDate = published.UpdateDate; + // } + // else + // { + // display.PublishDate = null; + // } - }, null, false); + // }, null, false); - return result; - } + // return result; + //} } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs index 30fcb50e32..168217b473 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs @@ -25,26 +25,4 @@ namespace Umbraco.Web.Models.Mapping }; } } - - internal class MediaTypeModelMapper - { - private readonly ApplicationContext _applicationContext; - - public MediaTypeModelMapper(ApplicationContext applicationContext) - { - _applicationContext = applicationContext; - } - - public ContentTypeBasic ToMediaTypeBasic(IMediaType contentType) - { - return new ContentTypeBasic - { - Alias = contentType.Alias, - Id = contentType.Id, - Description = contentType.Description, - Icon = contentType.Icon, - Name = contentType.Name - }; - } - } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs index 7b079664fd..55926afa6d 100644 --- a/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs @@ -17,16 +17,6 @@ namespace Umbraco.Web.Models.Mapping { } - public ContentItemDto ToMediaItemDto(IMedia content) - { - var result = base.ToContentItemDtoBase(content); - //NOTE: we don't need this for the dto and it's an extra lookup - //result.ContentTypeAlias = content.ContentType.Alias; - //result.Icon = content.ContentType.Icon; - //result.Updator = userMapper.ToUserBasic(content.GetWriterProfile()); - return result; - } - public ContentItemBasic ToMediaItemSimple(IMedia content) { var result = base.ToContentItemSimpleBase(content); diff --git a/src/Umbraco.Web/Models/Mapping/MediaTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MediaTypeModelMapper.cs new file mode 100644 index 0000000000..c54c700adf --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/MediaTypeModelMapper.cs @@ -0,0 +1,28 @@ +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Models.Mapping +{ + internal class MediaTypeModelMapper + { + private readonly ApplicationContext _applicationContext; + + public MediaTypeModelMapper(ApplicationContext applicationContext) + { + _applicationContext = applicationContext; + } + + public ContentTypeBasic ToMediaTypeBasic(IMediaType contentType) + { + return new ContentTypeBasic + { + Alias = contentType.Alias, + Id = contentType.Id, + Description = contentType.Description, + Icon = contentType.Icon, + Name = contentType.Name + }; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/NewContentMapper.cs b/src/Umbraco.Web/Models/Mapping/NewContentMapper.cs new file mode 100644 index 0000000000..443da7b913 --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/NewContentMapper.cs @@ -0,0 +1,314 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using AutoMapper; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Mapping; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.PropertyEditors; +using Umbraco.Web.Models.ContentEditing; +using System.Linq; + +namespace Umbraco.Web.Models.Mapping +{ + internal class NewContentMapper : MapperConfiguration + { + public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext) + { + //FROM Property TO ContentPropertyBasic + config.CreateMap>() + .ForMember(tab => tab.Label, expression => expression.MapFrom(@group => @group.Name)) + .ForMember(tab => tab.IsActive, expression => expression.UseValue(true)) + .ForMember(tab => tab.Properties, expression => expression.Ignore()); + + //FROM Property TO ContentPropertyBasic + config.CreateMap() + .ConvertUsing>(); + + //FROM Property TO ContentPropertyDto + config.CreateMap() + .ConvertUsing(new ContentPropertyDtoConverter(applicationContext)); + + //FROM Property TO ContentPropertyDisplay + config.CreateMap() + .ConvertUsing(new ContentPropertyDisplayConverter(applicationContext)); + + //FROM IContent TO ContentItemDisplay + config.CreateMap() + .ForMember( + dto => dto.Owner, + expression => expression.ResolveUsing>()) + .ForMember( + dto => dto.Updator, + expression => expression.ResolveUsing()) + .ForMember( + dto => dto.Icon, + expression => expression.MapFrom(content => content.ContentType.Icon)) + .ForMember( + dto => dto.ContentTypeAlias, + expression => expression.MapFrom(content => content.ContentType.Alias)) + .ForMember( + dto => dto.PublishDate, + expression => expression.MapFrom(content => GetPublishedDate(content, applicationContext))) + .ForMember(display => display.Properties, expression => expression.Ignore()) + .ForMember(display => display.Tabs, expression => expression.Ignore()) + .AfterMap((content, display) => MapTabsAndProperties(content, display)); + + //FROM IContent TO ContentItemBasic + config.CreateMap>() + .ForMember( + dto => dto.Owner, + expression => expression.ResolveUsing>()) + .ForMember( + dto => dto.Updator, + expression => expression.ResolveUsing()) + .ForMember( + dto => dto.Icon, + expression => expression.MapFrom(content => content.ContentType.Icon)) + .ForMember( + dto => dto.ContentTypeAlias, + expression => expression.MapFrom(content => content.ContentType.Alias)); + + //FROM IMedia TO ContentItemBasic + config.CreateMap>() + .ForMember( + dto => dto.Owner, + expression => expression.ResolveUsing>()) + .ForMember( + dto => dto.Icon, + expression => expression.MapFrom(content => content.ContentType.Icon)) + .ForMember( + dto => dto.ContentTypeAlias, + expression => expression.MapFrom(content => content.ContentType.Alias)); + + //FROM IContent TO ContentItemDto + config.CreateMap>() + .ForMember( + dto => dto.Owner, + expression => expression.ResolveUsing>()); + + //FROM IMedia TO ContentItemDto + config.CreateMap>() + .ForMember( + dto => dto.Owner, + expression => expression.ResolveUsing>()); + } + + /// + /// Gets the published date value for the IContent object + /// + /// + /// + /// + private static DateTime? GetPublishedDate(IContent content, ApplicationContext applicationContext) + { + if (content.Published) + { + return content.UpdateDate; + } + if (content.HasPublishedVersion()) + { + var published = applicationContext.Services.ContentService.GetPublishedVersion(content.Id); + return published.UpdateDate; + } + return null; + } + + private static void MapTabsAndProperties(IContentBase content, TabbedContentItem display) + { + var aggregateTabs = new List>(); + + //now we need to aggregate the tabs and properties since we might have duplicate tabs (based on aliases) because + // of how content composition works. + foreach (var propertyGroups in content.PropertyGroups.GroupBy(x => x.Name)) + { + var aggregateProperties = new List(); + + //there will always be one group with a null parent id (the top-most) + //then we'll iterate over all of the groups and ensure the properties are + //added in order so that when they render they are rendered with highest leve + //parent properties first. + int? currentParentId = null; + for (var i = 0; i < propertyGroups.Count(); i++) + { + var current = propertyGroups.Single(x => x.ParentId == currentParentId); + aggregateProperties.AddRange( + Mapper.Map, IEnumerable>( + content.GetPropertiesForGroup(current))); + currentParentId = current.Id; + } + + //then we'll just use the root group's data to make the composite tab + var rootGroup = propertyGroups.Single(x => x.ParentId == null); + aggregateTabs.Add(new Tab + { + Id = rootGroup.Id, + Alias = rootGroup.Name, + Label = rootGroup.Name, + Properties = aggregateProperties, + IsActive = false + }); + } + + //now add the generic properties tab for any properties that don't belong to a tab + var orphanProperties = content.GetNonGroupedProperties(); + + //now add the generic properties tab + aggregateTabs.Add(new Tab + { + Id = 0, + Label = "Generic properties", + Alias = "Generic properties", + Properties = Mapper.Map, IEnumerable>(orphanProperties) + }); + + //set the first tab to active + aggregateTabs.First().IsActive = true; + + display.Tabs = aggregateTabs; + } + + } + + + + internal class ContentDisplayConverter : TypeConverter + { + protected override ContentItemDisplay ConvertCore(IContent source) + { + throw new NotImplementedException(); + } + } + + /// + /// Maps the Creator for content + /// + internal class CreatorResolver : ValueResolver + { + protected override UserBasic ResolveCore(IContent source) + { + return Mapper.Map(source.GetWriterProfile()); + } + } + + /// + /// Maps the Owner for IContentBase + /// + /// + internal class OwnerResolver : ValueResolver + where TPersisted : IContentBase + { + protected override UserBasic ResolveCore(TPersisted source) + { + return Mapper.Map(source.GetCreatorProfile()); + } + } + + /// + /// Creates a ContentPropertyDto from a Property + /// + internal class ContentPropertyDisplayConverter : ContentPropertyBasicConverter + { + private readonly ApplicationContext _applicationContext; + + public ContentPropertyDisplayConverter(ApplicationContext applicationContext) + { + _applicationContext = applicationContext; + } + + protected override ContentPropertyDisplay ConvertCore(Property originalProp) + { + var display = base.ConvertCore(originalProp); + + //set the display properties after mapping + display.Alias = originalProp.Alias; + display.Description = originalProp.PropertyType.Description; + display.Label = originalProp.PropertyType.Name; + display.Config = _applicationContext.Services.DataTypeService.GetPreValuesByDataTypeId(originalProp.PropertyType.DataTypeDefinitionId); + if (display.PropertyEditor == null) + { + //if there is no property editor it means that it is a legacy data type + // we cannot support editing with that so we'll just render the readonly value view. + display.View = GlobalSettings.Path.EnsureEndsWith('/') + + "views/propertyeditors/umbraco/readonlyvalue/readonlyvalue.html"; + } + else + { + display.View = display.PropertyEditor.ValueEditor.View; + } + + return display; + } + } + + /// + /// Creates a ContentPropertyDto from a Property + /// + internal class ContentPropertyDtoConverter : ContentPropertyBasicConverter + { + private readonly ApplicationContext _applicationContext; + + public ContentPropertyDtoConverter(ApplicationContext applicationContext) + { + _applicationContext = applicationContext; + } + + protected override ContentPropertyDto ConvertCore(Property originalProperty) + { + var propertyDto = base.ConvertCore(originalProperty); + + propertyDto.IsRequired = originalProperty.PropertyType.Mandatory; + propertyDto.ValidationRegExp = originalProperty.PropertyType.ValidationRegExp; + propertyDto.Alias = originalProperty.Alias; + propertyDto.Description = originalProperty.PropertyType.Description; + propertyDto.Label = originalProperty.PropertyType.Name; + propertyDto.DataType = _applicationContext.Services.DataTypeService.GetDataTypeDefinitionById(originalProperty.PropertyType.DataTypeDefinitionId); + propertyDto.PropertyEditor = PropertyEditorResolver.Current.GetById(originalProperty.PropertyType.DataTypeId); + + return propertyDto; + } + } + + /// + /// Creates a base generic ContentPropertyBasic from a Property + /// + /// + internal class ContentPropertyBasicConverter : TypeConverter + where T : ContentPropertyBasic, new() + { + protected override T ConvertCore(Property property) + { + var editor = PropertyEditorResolver.Current.GetById(property.PropertyType.DataTypeId); + if (editor == null) + { + //TODO: Remove this check as we shouldn't support this at all! + var legacyEditor = DataTypesResolver.Current.GetById(property.PropertyType.DataTypeId); + if (legacyEditor == null) + { + throw new NullReferenceException("The property editor with id " + property.PropertyType.DataTypeId + " does not exist"); + } + + var legacyResult = new T + { + Id = property.Id, + Value = property.Value == null ? "" : property.Value.ToString(), + Alias = property.Alias + }; + return legacyResult; + } + var result = new T + { + Id = property.Id, + Value = editor.ValueEditor.SerializeValue(property.Value), + Alias = property.Alias + }; + + result.PropertyEditor = editor; + + return result; + } + } + +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/SectionModelMapper.cs b/src/Umbraco.Web/Models/Mapping/SectionModelMapper.cs index 3363d817fe..3e7ad602fd 100644 --- a/src/Umbraco.Web/Models/Mapping/SectionModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/SectionModelMapper.cs @@ -1,4 +1,5 @@ using AutoMapper; +using Umbraco.Core; using Umbraco.Core.Models.Mapping; using Umbraco.Web.Models.ContentEditing; @@ -6,7 +7,7 @@ namespace Umbraco.Web.Models.Mapping { internal class SectionModelMapper : MapperConfiguration { - public override void ConfigureMappings(IConfiguration config) + public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext) { config.CreateMap() .ReverseMap(); //backwards too! diff --git a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs b/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs index 9ea7f082f4..8ad785348c 100644 --- a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs @@ -11,7 +11,7 @@ namespace Umbraco.Web.Models.Mapping { #region Mapper config - public override void ConfigureMappings(IConfiguration config) + public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext) { config.CreateMap() .ForMember(detail => detail.UserId, opt => opt.MapFrom(user => GetIntId(user.Id))) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index fd44567e6f..cb33192d9f 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -325,6 +325,8 @@ + + diff --git a/src/Umbraco.Web/WebApi/Binders/ContentItemBinder.cs b/src/Umbraco.Web/WebApi/Binders/ContentItemBinder.cs index d50c1849f1..c3c3e2bf84 100644 --- a/src/Umbraco.Web/WebApi/Binders/ContentItemBinder.cs +++ b/src/Umbraco.Web/WebApi/Binders/ContentItemBinder.cs @@ -1,4 +1,5 @@ using System; +using AutoMapper; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Web.Models.ContentEditing; @@ -8,19 +9,17 @@ namespace Umbraco.Web.WebApi.Binders { internal class ContentItemBinder : ContentItemBaseBinder { - private readonly ContentModelMapper _contentModelMapper; - public ContentItemBinder(ApplicationContext applicationContext, ContentModelMapper contentModelMapper) + public ContentItemBinder(ApplicationContext applicationContext) : base(applicationContext) - { - _contentModelMapper = contentModelMapper; + { } /// /// Constructor /// public ContentItemBinder() - : this(ApplicationContext.Current, new ContentModelMapper(ApplicationContext.Current, new UserModelMapper())) + : this(ApplicationContext.Current) { } @@ -41,7 +40,7 @@ namespace Umbraco.Web.WebApi.Binders protected override ContentItemDto MapFromPersisted(ContentItemSave model) { - return _contentModelMapper.ToContentItemDto(model.PersistedContent); + return Mapper.Map>(model.PersistedContent); } } } \ No newline at end of file diff --git a/src/Umbraco.Web/WebApi/Binders/MediaItemBinder.cs b/src/Umbraco.Web/WebApi/Binders/MediaItemBinder.cs index 02d12ba8b0..4281a31fe7 100644 --- a/src/Umbraco.Web/WebApi/Binders/MediaItemBinder.cs +++ b/src/Umbraco.Web/WebApi/Binders/MediaItemBinder.cs @@ -1,4 +1,5 @@ using System; +using AutoMapper; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Web.Models.ContentEditing; @@ -41,7 +42,7 @@ namespace Umbraco.Web.WebApi.Binders protected override ContentItemDto MapFromPersisted(ContentItemSave model) { - return _mediaModelMapper.ToMediaItemDto(model.PersistedContent); + return Mapper.Map>(model.PersistedContent); } } } \ No newline at end of file diff --git a/src/umbraco.businesslogic/ApplicationRegistrar.cs b/src/umbraco.businesslogic/ApplicationRegistrar.cs index be1e9db85d..313ccb93ef 100644 --- a/src/umbraco.businesslogic/ApplicationRegistrar.cs +++ b/src/umbraco.businesslogic/ApplicationRegistrar.cs @@ -45,7 +45,7 @@ namespace umbraco.BusinessLogic }, true); } - public void ConfigureMappings(IConfiguration config) + public void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext) { config.CreateMap() .ForMember(x => x.alias, expression => expression.MapFrom(x => x.Alias)) diff --git a/src/umbraco.businesslogic/ApplicationTreeRegistrar.cs b/src/umbraco.businesslogic/ApplicationTreeRegistrar.cs index 2414702709..ace259bd57 100644 --- a/src/umbraco.businesslogic/ApplicationTreeRegistrar.cs +++ b/src/umbraco.businesslogic/ApplicationTreeRegistrar.cs @@ -30,7 +30,7 @@ namespace umbraco.BusinessLogic /// /// Configures automapper model mappings /// - public void ConfigureMappings(IConfiguration config) + public void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext) { config.CreateMap() .ReverseMap(); //two way