diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index 13317e7b20..eed7b5a071 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -511,14 +511,10 @@ AND umbracoNode.id <> @id", switch(toVariantType) { case ContentVariation.Culture: - MovePropertyDataToVariantCulture(defaultLangId, propertyTypeIds: propertyTypeIds); - break; case ContentVariation.Nothing: - MovePropertyDataToVariantNothing(defaultLangId, propertyTypeIds: propertyTypeIds); - break; case ContentVariation.CultureAndSegment: case ContentVariation.Segment: @@ -542,14 +538,89 @@ AND umbracoNode.id <> @id", switch (to) { case ContentVariation.Culture: + //move the property data MovePropertyDataToVariantCulture(defaultLangId, sqlPropertyTypeIds: sqlPropertyTypeIds); + //now we need to move the names + //first clear out any existing names that might already exists under the default lang + //there's 2x tables to update + + //clear out the versionCultureVariation table + var sqlSelect = Sql().Select(x => x.Id) + .From() + .InnerJoin().On(x => x.Id, x => x.VersionId) + .InnerJoin().On(x => x.NodeId, x => x.NodeId) + .Where(x => x.ContentTypeId == contentType.Id) + .Where(x => x.LanguageId == defaultLangId); + var sqlDelete = Sql() + .Delete() + .WhereIn(x => x.Id, sqlSelect); + Database.Execute(sqlDelete); + + //clear out the documentCultureVariation table + sqlSelect = Sql().Select(x => x.Id) + .From() + .InnerJoin().On(x => x.NodeId, x => x.NodeId) + .Where(x => x.ContentTypeId == contentType.Id) + .Where(x => x.LanguageId == defaultLangId); + sqlDelete = Sql() + .Delete() + .WhereIn(x => x.Id, sqlSelect); + Database.Execute(sqlDelete); + + //now we need to insert names into these 2 tables based on the invariant data + + //insert rows into the versionCultureVariationDto table based on the data from contentVersionDto for the default lang + var cols = Sql().Columns(x => x.VersionId, x => x.Name, x => x.UpdateUserId, x => x.UpdateDate, x => x.LanguageId); + sqlSelect = Sql().Select(x => x.Id, x => x.Text, x => x.UserId, x => x.VersionDate) + .Append($", {defaultLangId}") //default language ID + .From() + .InnerJoin().On(x => x.NodeId, x => x.NodeId) + .Where(x => x.ContentTypeId == contentType.Id); + var sqlInsert = Sql($"INSERT INTO {ContentVersionCultureVariationDto.TableName} ({cols})").Append(sqlSelect); + Database.Execute(sqlInsert); + + //insert rows into the documentCultureVariation table + cols = Sql().Columns(x => x.NodeId, x => x.Edited, x => x.Published, x => x.Name, x => x.Available, x => x.LanguageId); + sqlSelect = Sql().Select(x => x.NodeId, x => x.Edited, x => x.Published) + .AndSelect(x => x.Text) + .Append($", 1, {defaultLangId}") //make Available + default language ID + .From() + .InnerJoin().On(x => x.NodeId, x => x.NodeId) + .InnerJoin().On(x => x.NodeId, x => x.NodeId) + .Where(x => x.ContentTypeId == contentType.Id); + sqlInsert = Sql($"INSERT INTO {DocumentCultureVariationDto.TableName} ({cols})").Append(sqlSelect); + Database.Execute(sqlInsert); + break; case ContentVariation.Nothing: + //move the property data MovePropertyDataToVariantNothing(defaultLangId, sqlPropertyTypeIds: sqlPropertyTypeIds); + //we dont need to move the names! this is because we always keep the invariant names with the name of the default language. + + //however, if we were to move names, we could do this: BUT this doesn't work with SQLCE, for that we'd have to update row by row :( + + ////now we need to move the names + + ////first update umbracoNode from documentCultureVariation + //var sqlUpdate = Sql($"UPDATE {NodeDto.TableName} SET {Sql().Columns(x => x.Text)} = {Sql().Columns(x => x.Name)}") + // .From() + // .InnerJoin().On(x => x.NodeId, x => x.NodeId) + // .InnerJoin().On(x => x.NodeId, x => x.NodeId) + // .Where(x => x.ContentTypeId == contentType.Id); + //Database.Execute(sqlUpdate); + + ////next update umbracoContentVersion from contentVersionCultureVariation + //sqlUpdate = Sql($"UPDATE {ContentVersionDto.TableName} SET {Sql().Columns(x => x.Text)} = {Sql().Columns(x => x.Name)}") + // .From() + // .InnerJoin().On(x => x.VersionId, x => x.Id) + // .InnerJoin().On(x => x.NodeId, x => x.NodeId) + // .Where(x => x.ContentTypeId == contentType.Id); + //Database.Execute(sqlUpdate); + break; case ContentVariation.CultureAndSegment: case ContentVariation.Segment: @@ -558,6 +629,12 @@ AND umbracoNode.id <> @id", } } + /// + /// This will move all property data from variant to invariant + /// + /// + /// Optional list of property type ids of the properties to be updated + /// Optional SQL statement used for the sub-query to select the properties type ids for the properties to be updated private void MovePropertyDataToVariantNothing(int defaultLangId, IReadOnlyCollection propertyTypeIds = null, Sql sqlPropertyTypeIds = null) { //first clear out any existing property data that might already exists under the default lang @@ -587,6 +664,12 @@ AND umbracoNode.id <> @id", Database.Execute(sqlInsert); } + /// + /// This will move all property data from invariant to variant + /// + /// + /// Optional list of property type ids of the properties to be updated + /// Optional SQL statement used for the sub-query to select the properties type ids for the properties to be updated private void MovePropertyDataToVariantCulture(int defaultLangId, IReadOnlyCollection propertyTypeIds = null, Sql sqlPropertyTypeIds = null) { //first clear out any existing property data that might already exists under the default lang diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs index 2026daba74..8a0ba73f85 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs @@ -219,6 +219,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement if (TypedCachePolicy != null) TypedCachePolicy.GetAllCached(PerformGetAll); + var id = GetIdByIsoCode(isoCode, throwOnNotFound: false); return id.HasValue ? Get(id.Value) : null; } @@ -234,6 +235,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // ensure cache is populated, in a non-expensive way if (TypedCachePolicy != null) TypedCachePolicy.GetAllCached(PerformGetAll); + else + PerformGetAll(); //we don't have a typed cache (i.e. unit tests) but need to populate the _codeIdMap lock (_codeIdMap) { @@ -255,6 +258,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // ensure cache is populated, in a non-expensive way if (TypedCachePolicy != null) TypedCachePolicy.GetAllCached(PerformGetAll); + else + PerformGetAll(); lock (_codeIdMap) // yes, we want to lock _codeIdMap { diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs index 5b4dcceb00..892166e566 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs @@ -25,7 +25,55 @@ namespace Umbraco.Tests.Services public class ContentTypeServiceTests : TestWithSomeContentBase { - //TODO: Then write the ones for content type changes + [Test] + public void Change_Content_Type_From_Invariant_Variant() + { + //create content type with a property type that varies by culture + var contentType = MockedContentTypes.CreateBasicContentType(); + contentType.Variations = ContentVariation.Nothing; + var contentCollection = new PropertyTypeCollection(true); + contentCollection.Add(new PropertyType("test", ValueStorageType.Ntext) + { + Alias = "title", + Name = "Title", + Description = "", + Mandatory = false, + SortOrder = 1, + DataTypeId = -88, + Variations = ContentVariation.Nothing + }); + contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); + ServiceContext.ContentTypeService.Save(contentType); + + //create some content of this content type + IContent doc = MockedContent.CreateBasicContent(contentType); + doc.Name = "Hello1"; + doc.SetValue("title", "hello world"); + ServiceContext.ContentService.Save(doc); + + Assert.AreEqual("Hello1", doc.Name); + Assert.AreEqual("hello world", doc.GetValue("title")); + + //change the content type to be variant, we will also update the name here to detect the copy changes + doc.Name = "Hello2"; + ServiceContext.ContentService.Save(doc); + contentType.Variations = ContentVariation.Culture; + ServiceContext.ContentTypeService.Save(contentType); + doc = ServiceContext.ContentService.GetById(doc.Id); //re-get + + Assert.AreEqual("Hello2", doc.GetCultureName("en-US")); + Assert.AreEqual("hello world", doc.GetValue("title")); //We are not checking against en-US here because properties will remain invariant + + //change back property type to be invariant, we will also update the name here to detect the copy changes + doc.SetCultureName("Hello3", "en-US"); + ServiceContext.ContentService.Save(doc); + contentType.Variations = ContentVariation.Nothing; + ServiceContext.ContentTypeService.Save(contentType); + doc = ServiceContext.ContentService.GetById(doc.Id); //re-get + + Assert.AreEqual("Hello3", doc.Name); + Assert.AreEqual("hello world", doc.GetValue("title")); + } [Test] public void Change_Content_Type_From_Variant_Invariant() @@ -45,25 +93,30 @@ namespace Umbraco.Tests.Services Variations = ContentVariation.Culture }); contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); - contentType.ResetDirtyProperties(false); ServiceContext.ContentTypeService.Save(contentType); //create some content of this content type IContent doc = MockedContent.CreateBasicContent(contentType); - doc.SetCultureName("Home", "en-US"); + doc.SetCultureName("Hello1", "en-US"); doc.SetValue("title", "hello world", "en-US"); ServiceContext.ContentService.Save(doc); + Assert.AreEqual("Hello1", doc.GetCultureName("en-US")); Assert.AreEqual("hello world", doc.GetValue("title", "en-US")); - //change the content type to be invariant - contentType.Variations = ContentVariation.Nothing; + //change the content type to be invariant, we will also update the name here to detect the copy changes + doc.SetCultureName("Hello2", "en-US"); + ServiceContext.ContentService.Save(doc); + contentType.Variations = ContentVariation.Nothing; ServiceContext.ContentTypeService.Save(contentType); doc = ServiceContext.ContentService.GetById(doc.Id); //re-get + Assert.AreEqual("Hello2", doc.Name); Assert.AreEqual("hello world", doc.GetValue("title")); - //change back property type to be variant + //change back property type to be variant, we will also update the name here to detect the copy changes + doc.Name = "Hello3"; + ServiceContext.ContentService.Save(doc); contentType.Variations = ContentVariation.Culture; ServiceContext.ContentTypeService.Save(contentType); doc = ServiceContext.ContentService.GetById(doc.Id); //re-get @@ -78,6 +131,7 @@ namespace Umbraco.Tests.Services ServiceContext.ContentTypeService.Save(contentType); doc = ServiceContext.ContentService.GetById(doc.Id); //re-get + Assert.AreEqual("Hello3", doc.GetCultureName("en-US")); Assert.AreEqual("hello world", doc.GetValue("title", "en-US")); } @@ -87,7 +141,7 @@ namespace Umbraco.Tests.Services { //create content type with a property type that varies by culture var contentType = MockedContentTypes.CreateBasicContentType(); - contentType.Variations = ContentVariation.Culture; + contentType.Variations = ContentVariation.Nothing; var contentCollection = new PropertyTypeCollection(true); contentCollection.Add(new PropertyType("test", ValueStorageType.Ntext) { @@ -100,12 +154,11 @@ namespace Umbraco.Tests.Services Variations = ContentVariation.Nothing }); contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); - contentType.ResetDirtyProperties(false); ServiceContext.ContentTypeService.Save(contentType); //create some content of this content type IContent doc = MockedContent.CreateBasicContent(contentType); - doc.SetCultureName("Home", "en-US"); + doc.Name = "Home"; doc.SetValue("title", "hello world"); ServiceContext.ContentService.Save(doc); @@ -144,7 +197,6 @@ namespace Umbraco.Tests.Services Variations = ContentVariation.Culture }); contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); - contentType.ResetDirtyProperties(false); ServiceContext.ContentTypeService.Save(contentType); //create some content of this content type