diff --git a/src/Umbraco.Core/Models/ContentType.cs b/src/Umbraco.Core/Models/ContentType.cs index 710efb9e3a..5fb1fa3100 100644 --- a/src/Umbraco.Core/Models/ContentType.cs +++ b/src/Umbraco.Core/Models/ContentType.cs @@ -143,35 +143,19 @@ namespace Umbraco.Core.Models } /// - /// //TODO: REmove this as it's mostly just a shallow clone and not thread safe - /// Creates a clone of the current entity + /// Creates a deep clone of the current entity with its identity/alias and it's property identities reset /// /// public IContentType Clone(string alias) { - var clone = (ContentType)this.MemberwiseClone(); + var clone = (ContentType)DeepClone(); clone.Alias = alias; clone.Key = Guid.Empty; - var propertyGroups = this.PropertyGroups.Select(x => x.Clone()).ToList(); + var propertyGroups = PropertyGroups.Select(x => x.Clone()).ToList(); clone.PropertyGroups = new PropertyGroupCollection(propertyGroups); - clone.PropertyTypes = this.PropertyTypeCollection.Select(x => x.Clone()).ToList(); + clone.PropertyTypes = PropertyTypeCollection.Select(x => x.Clone()).ToList(); clone.ResetIdentity(); clone.ResetDirtyProperties(false); - - foreach (var propertyGroup in clone.PropertyGroups) - { - propertyGroup.ResetIdentity(); - foreach (var propertyType in propertyGroup.PropertyTypes) - { - propertyType.ResetIdentity(); - } - } - - foreach (var propertyType in clone.PropertyTypes.Where(x => x.HasIdentity)) - { - propertyType.ResetIdentity(); - } - return clone; } diff --git a/src/Umbraco.Core/Models/PropertyGroup.cs b/src/Umbraco.Core/Models/PropertyGroup.cs index 3158dfe6d6..b432cced08 100644 --- a/src/Umbraco.Core/Models/PropertyGroup.cs +++ b/src/Umbraco.Core/Models/PropertyGroup.cs @@ -143,16 +143,17 @@ namespace Umbraco.Core.Models return hashName ^ hashId; } - //TODO: Remove this, its mostly a shallow clone and is not thread safe + /// + /// Creates a deep clone of the current entity with its identity and it's property identities reset + /// + /// internal PropertyGroup Clone() { - var clone = (PropertyGroup)this.MemberwiseClone(); + var clone = (PropertyGroup)DeepClone(); var collection = new PropertyTypeCollection(); - foreach (var propertyType in this.PropertyTypes) + foreach (var propertyType in PropertyTypes) { - var property = propertyType.Clone(); - property.ResetIdentity(); - property.ResetDirtyProperties(false); + var property = propertyType.Clone(); collection.Add(property); } clone.PropertyTypes = collection; diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index 83a8c09a99..f59a79748f 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -443,10 +443,13 @@ namespace Umbraco.Core.Models return hashName ^ hashAlias; } - //TODO: Remove this + /// + /// Creates a deep clone of the current entity with its identity and it's property identities reset + /// + /// internal PropertyType Clone() { - var clone = (PropertyType)this.MemberwiseClone(); + var clone = (PropertyType)DeepClone(); clone.ResetIdentity(); clone.ResetDirtyProperties(false); return clone; diff --git a/src/Umbraco.Tests/Models/ContentTypeTests.cs b/src/Umbraco.Tests/Models/ContentTypeTests.cs index af94f80fd4..0fc1a52a28 100644 --- a/src/Umbraco.Tests/Models/ContentTypeTests.cs +++ b/src/Umbraco.Tests/Models/ContentTypeTests.cs @@ -24,6 +24,69 @@ namespace Umbraco.Tests.Models } + [Test] + public void Can_Deep_Clone_Content_Type_With_Reset_Identities() + { + var contentType = MockedContentTypes.CreateTextpageContentType(); + contentType.Id = 99; + + var i = 200; + foreach (var propertyType in contentType.PropertyTypes) + { + propertyType.Id = ++i; + } + foreach (var group in contentType.PropertyGroups) + { + group.Id = ++i; + } + //add a property type without a property group + contentType.PropertyTypeCollection.Add( + new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "title2", Name = "Title2", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 }); + + contentType.AllowedTemplates = new[] { new Template("-1,2", "Name", "name") { Id = 200 }, new Template("-1,3", "Name2", "name2") { Id = 201 } }; + contentType.AllowedContentTypes = new[] { new ContentTypeSort(new Lazy(() => 888), 8, "sub"), new ContentTypeSort(new Lazy(() => 889), 9, "sub2") }; + contentType.Id = 10; + contentType.CreateDate = DateTime.Now; + contentType.CreatorId = 22; + contentType.SetDefaultTemplate(new Template("-1,2,3,4", "Test Template", "testTemplate") + { + Id = 88 + }); + contentType.Description = "test"; + contentType.Icon = "icon"; + contentType.IsContainer = true; + contentType.Thumbnail = "thumb"; + contentType.Key = Guid.NewGuid(); + contentType.Level = 3; + contentType.Path = "-1,4,10"; + contentType.SortOrder = 5; + contentType.Trashed = false; + contentType.UpdateDate = DateTime.Now; + + //ensure that nothing is marked as dirty + contentType.ResetDirtyProperties(false); + + var clone = (ContentType)contentType.Clone("newAlias"); + + Assert.AreEqual("newAlias", clone.Alias); + Assert.AreNotEqual("newAlias", contentType.Alias); + Assert.IsFalse(clone.HasIdentity); + + foreach (var propertyGroup in clone.PropertyGroups) + { + Assert.IsFalse(propertyGroup.HasIdentity); + foreach (var propertyType in propertyGroup.PropertyTypes) + { + Assert.IsFalse(propertyType.HasIdentity); + } + } + + foreach (var propertyType in clone.PropertyTypes.Where(x => x.HasIdentity)) + { + Assert.IsFalse(propertyType.HasIdentity); + } + } + [Test] public void Can_Deep_Clone_Content_Type() { @@ -36,6 +99,10 @@ namespace Umbraco.Tests.Models { propertyType.Id = ++i; } + foreach (var group in contentType.PropertyGroups) + { + group.Id = ++i; + } contentType.AllowedTemplates = new[] { new Template("-1,2", "Name", "name") { Id = 200 }, new Template("-1,3", "Name2", "name2") { Id = 201 } }; contentType.AllowedContentTypes = new[] {new ContentTypeSort(new Lazy(() => 888), 8, "sub"), new ContentTypeSort(new Lazy(() => 889), 9, "sub2")}; contentType.Id = 10;