diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index e35d474204..8dba129d93 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -853,7 +853,7 @@ AND umbracoNode.id <> @id", //first we want to get the main content type data this is 1 : 1 with umbraco node data var ct = result - .Where(x => x.ctId == currentCtId) + .Where(x => (int)x.ctId == currentCtId) .Select(x => new { x.ctPk, x.ctId, x.ctAlias, x.ctAllowAtRoot, x.ctDesc, x.ctIcon, x.ctIsContainer, x.ctThumb, x.nName, x.nCreateDate, x.nLevel, x.nObjectType, x.nUser, x.nParentId, x.nPath, x.nSortOrder, x.nTrashed, x.nUniqueId }) .DistinctBy(x => (int)x.ctId) .FirstOrDefault(); diff --git a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs index 0e45a24adb..6ace0f6203 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs @@ -4,6 +4,7 @@ using System.Linq; using AutoMapper; using Moq; using NUnit.Framework; +using umbraco; using umbraco.cms.presentation; using Umbraco.Core; using Umbraco.Core.Logging; @@ -177,11 +178,35 @@ namespace Umbraco.Tests.Models.Mapping Assert.AreEqual(display.Thumbnail, result.Thumbnail); Assert.AreEqual(display.IsContainer, result.IsContainer); Assert.AreEqual(display.AllowAsRoot, result.AllowedAsRoot); + Assert.AreEqual(display.CreateDate, result.CreateDate); + Assert.AreEqual(display.UpdateDate, result.UpdateDate); //TODO: Now we need to assert all of the more complicated parts - Assert.AreEqual(1, result.PropertyGroups.Count); - Assert.AreEqual(1, result.PropertyGroups[0].PropertyTypes.Count); + Assert.AreEqual(display.Groups.Count(), result.PropertyGroups.Count); + for (var i = 0; i < display.Groups.Count(); i++) + { + Assert.AreEqual(display.Groups.ElementAt(i).Id, result.PropertyGroups.ElementAt(i).Id); + Assert.AreEqual(display.Groups.ElementAt(i).Name, result.PropertyGroups.ElementAt(i).Name); + var propTypes = display.Groups.ElementAt(i).Properties; + Assert.AreEqual(propTypes.Count(), result.PropertyTypes.Count()); + for (var j = 0; j < propTypes.Count(); j++) + { + Assert.AreEqual(propTypes.ElementAt(j).Id, result.PropertyTypes.ElementAt(j).Id); + Assert.AreEqual(propTypes.ElementAt(j).DataTypeId, result.PropertyTypes.ElementAt(j).DataTypeDefinitionId); + } + } + Assert.AreEqual(display.AllowedTemplates.Count(), result.AllowedTemplates.Count()); + for (var i = 0; i < display.AllowedTemplates.Count(); i++) + { + Assert.AreEqual(display.AllowedTemplates.ElementAt(i).Id, result.AllowedTemplates.ElementAt(i).Id); + } + + Assert.AreEqual(display.AllowedContentTypes.Count(), result.AllowedContentTypes.Count()); + for (var i = 0; i < display.AllowedContentTypes.Count(); i++) + { + Assert.AreEqual(display.AllowedContentTypes.ElementAt(i), result.AllowedContentTypes.ElementAt(i).Id.Value); + } } [Test] @@ -231,13 +256,39 @@ namespace Umbraco.Tests.Models.Mapping Assert.AreEqual(contentType.Path, result.Path); Assert.AreEqual(contentType.Thumbnail, result.Thumbnail); Assert.AreEqual(contentType.IsContainer, result.IsContainer); - + Assert.AreEqual(contentType.CreateDate, result.CreateDate); + Assert.AreEqual(contentType.UpdateDate, result.UpdateDate); Assert.AreEqual(contentType.DefaultTemplate.Alias, result.DefaultTemplate.Alias); //TODO: Now we need to assert all of the more complicated parts - Assert.AreEqual(2, result.Groups.Count()); - Assert.AreEqual(2, result.Groups.ElementAt(0).Properties.Count()); - Assert.AreEqual(2, result.Groups.ElementAt(1).Properties.Count()); + + Assert.AreEqual(contentType.PropertyGroups.Count(), result.Groups.Count()); + for (var i = 0; i < contentType.PropertyGroups.Count(); i++) + { + Assert.AreEqual(contentType.PropertyGroups.ElementAt(i).Id, result.Groups.ElementAt(i).Id); + Assert.AreEqual(contentType.PropertyGroups.ElementAt(i).Name, result.Groups.ElementAt(i).Name); + var propTypes = contentType.PropertyGroups.ElementAt(i).PropertyTypes; + + Assert.AreEqual(propTypes.Count(), result.Groups.ElementAt(i).Properties.Count()); + for (var j = 0; j < propTypes.Count(); j++) + { + Assert.AreEqual(propTypes.ElementAt(j).Id, result.Groups.ElementAt(i).Properties.ElementAt(j).Id); + Assert.AreEqual(propTypes.ElementAt(j).DataTypeDefinitionId, result.Groups.ElementAt(i).Properties.ElementAt(j).DataTypeId); + } + } + + Assert.AreEqual(contentType.AllowedTemplates.Count(), result.AllowedTemplates.Count()); + for (var i = 0; i < contentType.AllowedTemplates.Count(); i++) + { + Assert.AreEqual(contentType.AllowedTemplates.ElementAt(i).Id, result.AllowedTemplates.ElementAt(i).Id); + } + + Assert.AreEqual(contentType.AllowedContentTypes.Count(), result.AllowedContentTypes.Count()); + for (var i = 0; i < contentType.AllowedContentTypes.Count(); i++) + { + Assert.AreEqual(contentType.AllowedContentTypes.ElementAt(i).Id.Value, result.AllowedContentTypes.ElementAt(i)); + } + } private ContentTypeDisplay CreateSimpleContentTypeDisplay() @@ -246,7 +297,22 @@ namespace Umbraco.Tests.Models.Mapping { Alias = "test", AllowAsRoot = true, - AllowedTemplates = new List(), + AllowedTemplates = new List + { + new EntityBasic + { + Id = 555, + Alias = "template1", + Name = "Template1" + }, + new EntityBasic + { + Id = 556, + Alias = "template2", + Name = "Template2" + } + }, + AllowedContentTypes = new [] {666, 667}, AvailableCompositeContentTypes = new List(), DefaultTemplate = new EntityBasic(){ Alias = "test" }, Description = "hello world", diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs index de7fed05bf..9aa22a6e69 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using AutoMapper; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -16,9 +17,11 @@ using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; +using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Tests.Persistence.Repositories { + [RequiresAutoMapperMappings] [DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)] [TestFixture] public class ContentTypeRepositoryTest : BaseDatabaseFactoryTest @@ -149,6 +152,63 @@ namespace Umbraco.Tests.Persistence.Repositories } + [Test] + public void Can_Perform_Update_On_ContentTypeRepository_After_Model_Mapping() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + // Act + var contentType = repository.Get(NodeDto.NodeIdSeed + 1); + + var display = Mapper.Map(contentType); + + display.Thumbnail = "Doc2.png"; + var contentGroup = display.Groups.Single(x => x.Name == "Content"); + + //add property + contentGroup.Properties = contentGroup.Properties.Concat(new[] + { + new PropertyTypeDisplay() + { + Alias = "subtitle", + Editor = "test", + Label = "Subtitle", + Description = "Optional Subtitle", + Validation = new PropertyTypeValidation() + { + Mandatory = false, + Pattern = "" + }, + SortOrder = 1, + DataTypeId = -88 + } + }); + + //simulate what would happen in the controller, we'd never map to a 'new' content type, + // we'd map to an existing content type when updating. + var mapped = Mapper.Map(display, contentType); + + repository.AddOrUpdate(mapped); + unitOfWork.Commit(); + + var dirty = mapped.IsDirty(); + + // Assert + Assert.That(mapped.HasIdentity, Is.True); + Assert.That(dirty, Is.False); + Assert.That(mapped.Thumbnail, Is.EqualTo("Doc2.png")); + Assert.That(mapped.PropertyTypes.Any(x => x.Alias == "subtitle"), Is.True); + foreach (var propertyType in mapped.PropertyTypes) + { + Assert.IsTrue(propertyType.HasIdentity); + Assert.Greater(propertyType.Id, 0); + } + } + } + [Test] public void Can_Perform_Delete_On_ContentTypeRepository() { diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs index 7408429fa4..02ca5afcdb 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs @@ -13,6 +13,7 @@ using Umbraco.Tests.TestHelpers.Entities; namespace Umbraco.Tests.Services { + [DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)] [TestFixture, RequiresSTA] public class ContentTypeServiceTests : BaseServiceTest diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeBasic.cs b/src/Umbraco.Web/Models/ContentEditing/ContentTypeBasic.cs index 2e527cfd93..b12233b1b3 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentTypeBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentTypeBasic.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations; +using System; +using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; using Umbraco.Core; using Umbraco.Core.IO; @@ -14,6 +15,11 @@ namespace Umbraco.Web.Models.ContentEditing [DataContract(Name = "contentType", Namespace = "")] public class ContentTypeBasic : EntityBasic { + [DataMember(Name = "updateDate")] + public DateTime UpdateDate { get; set; } + + [DataMember(Name = "createDate")] + public DateTime CreateDate { get; set; } [DataMember(Name = "description")] public string Description { get; set; } diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs index 5e3e6c2ec2..f1bef30ae7 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs @@ -46,8 +46,6 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dto => dto.AllowedAsRoot, expression => expression.MapFrom(display => display.AllowAsRoot)) .ForMember(dto => dto.CreatorId, expression => expression.Ignore()) .ForMember(dto => dto.Level, expression => expression.Ignore()) - .ForMember(dto => dto.CreateDate, expression => expression.Ignore()) - .ForMember(dto => dto.UpdateDate, expression => expression.Ignore()) .ForMember(dto => dto.SortOrder, expression => expression.Ignore()) //mapped in aftermap .ForMember(dto => dto.AllowedContentTypes, expression => expression.Ignore()) @@ -65,7 +63,7 @@ namespace Umbraco.Web.Models.Mapping //Sync allowed child types var allowedTypes = source.AllowedContentTypes.Select((t, i) => new ContentTypeSort(t, i)); - dest.AllowedContentTypes = allowedTypes; + dest.AllowedContentTypes = allowedTypes.ToArray(); //sync compositions var current = dest.CompositionAliases().ToArray(); @@ -110,22 +108,17 @@ namespace Umbraco.Web.Models.Mapping .ForMember( dto => dto.CompositeContentTypes, expression => expression.MapFrom(dto => dto.ContentTypeComposition)) - - .ForMember( - dto => dto.CompositeContentTypes, - expression => expression.MapFrom(dto => dto.ContentTypeComposition)) - + .ForMember( dto => dto.Groups, expression => expression.ResolveUsing(new PropertyTypeGroupResolver(applicationContext, _propertyEditorResolver))); config.CreateMap() .ForMember(dest => dest.Id, expression => expression.Condition(source => source.Id > 0)) - .ForMember(g => g.CreateDate, expression => expression.Ignore()) .ForMember(g => g.Key, expression => expression.Ignore()) - .ForMember(g => g.CreateDate, expression => expression.Ignore()) - .ForMember(g => g.HasIdentity, expression => expression.Ignore()) - .ForMember(g => g.UpdateDate, expression => expression.Ignore()) + .ForMember(g => g.HasIdentity, expression => expression.Ignore()) + .ForMember(dto => dto.CreateDate, expression => expression.Ignore()) + .ForMember(dto => dto.UpdateDate, expression => expression.Ignore()) //only map if a parent is actually set .ForMember(g => g.ParentId, expression => expression.Condition(display => display.ParentGroupId > 0)) @@ -152,6 +145,8 @@ namespace Umbraco.Web.Models.Mapping if (dataType == null) throw new NullReferenceException("No data type found with id " + propertyTypeDisplay.DataTypeId); return new PropertyType(dataType, propertyTypeDisplay.Alias); }) + .ForMember(dto => dto.CreateDate, expression => expression.Ignore()) + .ForMember(dto => dto.UpdateDate, expression => expression.Ignore()) .ForMember(type => type.PropertyGroupId, expression => expression.MapFrom(display => new Lazy(() => display.GroupId, LazyThreadSafetyMode.None))) .ForMember(type => type.Key, expression => expression.Ignore()) .ForMember(type => type.HelpText, expression => expression.Ignore()) @@ -160,9 +155,6 @@ namespace Umbraco.Web.Models.Mapping .ForMember(type => type.Alias, expression => expression.Ignore()) //ignore because this is obsolete and shouldn't be used .ForMember(type => type.DataTypeId, expression => expression.Ignore()) - //ignore because these are 'readonly' - .ForMember(type => type.CreateDate, expression => expression.Ignore()) - .ForMember(type => type.UpdateDate, expression => expression.Ignore()) .ForMember(type => type.Mandatory, expression => expression.MapFrom(display => display.Validation.Mandatory)) .ForMember(type => type.ValidationRegExp, expression => expression.MapFrom(display => display.Validation.Pattern)) .ForMember(type => type.PropertyEditorAlias, expression => expression.MapFrom(display => display.Editor)) diff --git a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs index a30bab1e3d..ae247ab73d 100644 --- a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs @@ -165,7 +165,9 @@ namespace Umbraco.Web.Models.Mapping ContentTypeId = contentType.Id, ContentTypeName = contentType.Name, GroupId = groupId, - Inherited = inherited + Inherited = inherited, + DataTypeId = p.DataTypeDefinitionId, + SortOrder = p.SortOrder }); }