diff --git a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs index 58c4d22c89..7ff2077d77 100644 --- a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs @@ -18,10 +18,14 @@ namespace Umbraco.Tests.PropertyEditors [TestFixture] public class BlockListPropertyValueConverterTests { - private readonly Guid Key1 = Guid.NewGuid(); - private readonly Guid Key2 = Guid.NewGuid(); - private readonly string Alias1 = "Test1"; - private readonly string Alias2 = "Test2"; + private readonly Guid ContentKey1 = Guid.NewGuid(); + private readonly Guid ContentKey2 = Guid.NewGuid(); + private const string ContentAlias1 = "Test1"; + private const string ContentAlias2 = "Test2"; + private readonly Guid SettingKey1 = Guid.NewGuid(); + private readonly Guid SettingKey2 = Guid.NewGuid(); + private const string SettingAlias1 = "Setting1"; + private const string SettingAlias2 = "Setting2"; /// /// Setup mocks for IPublishedSnapshotAccessor @@ -31,15 +35,25 @@ namespace Umbraco.Tests.PropertyEditors { var test1ContentType = Mock.Of(x => x.IsElement == true - && x.Key == Key1 - && x.Alias == Alias1); + && x.Key == ContentKey1 + && x.Alias == ContentAlias1); var test2ContentType = Mock.Of(x => x.IsElement == true - && x.Key == Key2 - && x.Alias == Alias2); + && x.Key == ContentKey2 + && x.Alias == ContentAlias2); + var test3ContentType = Mock.Of(x => + x.IsElement == true + && x.Key == SettingKey1 + && x.Alias == SettingAlias1); + var test4ContentType = Mock.Of(x => + x.IsElement == true + && x.Key == SettingKey2 + && x.Alias == SettingAlias2); var contentCache = new Mock(); - contentCache.Setup(x => x.GetContentType(Key1)).Returns(test1ContentType); - contentCache.Setup(x => x.GetContentType(Key2)).Returns(test2ContentType); + contentCache.Setup(x => x.GetContentType(ContentKey1)).Returns(test1ContentType); + contentCache.Setup(x => x.GetContentType(ContentKey2)).Returns(test2ContentType); + contentCache.Setup(x => x.GetContentType(SettingKey1)).Returns(test3ContentType); + contentCache.Setup(x => x.GetContentType(SettingKey2)).Returns(test4ContentType); var publishedSnapshot = Mock.Of(x => x.Content == contentCache.Object); var publishedSnapshotAccessor = Mock.Of(x => x.PublishedSnapshot == publishedSnapshot); return publishedSnapshotAccessor; @@ -60,11 +74,13 @@ namespace Umbraco.Tests.PropertyEditors Blocks = new[] { new BlockListConfiguration.BlockConfiguration { - Key = Key1 + ContentElementTypeKey = ContentKey1, + SettingsElementTypeKey = SettingKey2 }, new BlockListConfiguration.BlockConfiguration { - Key = Key2 + ContentElementTypeKey = ContentKey2, + SettingsElementTypeKey = SettingKey1 } } }; @@ -74,7 +90,7 @@ namespace Umbraco.Tests.PropertyEditors Blocks = new[] { new BlockListConfiguration.BlockConfiguration { - Key = Key1 + ContentElementTypeKey = ContentKey1 } } }; @@ -227,7 +243,7 @@ data: []}"; }, contentData: [ { - 'contentTypeKey': '" + Key1 + @"', + 'contentTypeKey': '" + ContentKey1 + @"', 'key': '1304E1DD-0000-4396-84FE-8A399231CB3D' } ] @@ -259,7 +275,7 @@ data: []}"; }, contentData: [ { - 'contentTypeKey': '" + Key1 + @"', + 'contentTypeKey': '" + ContentKey1 + @"', 'udi': 'umb://element/1304E1DDAC87439684FE8A399231CB3D' } ] @@ -290,43 +306,63 @@ data: []}"; layout: { '" + Constants.PropertyEditors.Aliases.BlockList + @"': [ { - 'contentUdi': 'umb://element/1304E1DDAC87439684FE8A399231CB3D' + 'contentUdi': 'umb://element/1304E1DDAC87439684FE8A399231CB3D', + 'settingsUdi': 'umb://element/1F613E26CE274898908A561437AF5100' }, { 'contentUdi': 'umb://element/0A4A416E547D464FABCC6F345C17809A', - 'settingsUdi': null + 'settingsUdi': 'umb://element/63027539B0DB45E7B70459762D4E83DD' } ] }, - contentData: [ + contentData: [ { - 'contentTypeKey': '" + Key1 + @"', + 'contentTypeKey': '" + ContentKey1 + @"', 'udi': 'umb://element/1304E1DDAC87439684FE8A399231CB3D' }, { - 'contentTypeKey': '" + Key2 + @"', + 'contentTypeKey': '" + ContentKey2 + @"', 'udi': 'umb://element/E05A034704424AB3A520E048E6197E79' }, { - 'contentTypeKey': '" + Key2 + @"', + 'contentTypeKey': '" + ContentKey2 + @"', 'udi': 'umb://element/0A4A416E547D464FABCC6F345C17809A' } - ] + ], + settingsData: [ + { + 'contentTypeKey': '" + SettingKey1 + @"', + 'udi': 'umb://element/63027539B0DB45E7B70459762D4E83DD' + }, + { + 'contentTypeKey': '" + SettingKey2 + @"', + 'udi': 'umb://element/1F613E26CE274898908A561437AF5100' + }, + { + 'contentTypeKey': '" + SettingKey2 + @"', + 'udi': 'umb://element/BCF4BA3DA40C496C93EC58FAC85F18B9' + } + ], }"; var converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel; Assert.IsNotNull(converted); Assert.AreEqual(3, converted.ContentData.Count()); + Assert.AreEqual(3, converted.SettingsData.Count()); Assert.AreEqual(2, converted.Layout.Count()); var item0 = converted.Layout.ElementAt(0); Assert.AreEqual(Guid.Parse("1304E1DD-AC87-4396-84FE-8A399231CB3D"), item0.Content.Key); Assert.AreEqual("Test1", item0.Content.ContentType.Alias); + Assert.AreEqual(Guid.Parse("1F613E26CE274898908A561437AF5100"), item0.Settings.Key); + Assert.AreEqual("Setting2", item0.Settings.ContentType.Alias); var item1 = converted.Layout.ElementAt(1); Assert.AreEqual(Guid.Parse("0A4A416E-547D-464F-ABCC-6F345C17809A"), item1.Content.Key); Assert.AreEqual("Test2", item1.Content.ContentType.Alias); + Assert.AreEqual(Guid.Parse("63027539B0DB45E7B70459762D4E83DD"), item1.Settings.Key); + Assert.AreEqual("Setting1", item1.Settings.ContentType.Alias); } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js index 4d520c02d0..7496c15b52 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js @@ -710,6 +710,7 @@ }, // private + // TODO: Then this can just be a method in the outer scope _createSettingsEntry: function(elementTypeKey) { var settings = { contentTypeKey: elementTypeKey, @@ -718,7 +719,9 @@ this.value.settingsData.push(settings); return settings.udi; }, + // private + // TODO: Then this can just be a method in the outer scope _getSettingsByUdi: function(udi) { return this.value.settingsData.find(entry => entry.udi === udi) || null; }, diff --git a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs index 0ea48512d1..bfbbc80179 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -27,11 +27,12 @@ namespace Umbraco.Web.PropertyEditors [JsonProperty("thumbnail")] public string Thumbnail { get; set; } + // TODO: This is named inconsistently in JS but renaming it needs to be done in quite a lot of places, this should be contentElementTypeKey [JsonProperty("contentTypeKey")] - public Guid Key { get; set; } + public Guid ContentElementTypeKey { get; set; } [JsonProperty("settingsElementTypeKey")] - public string SettingsElementTypeKey { get; set; } + public Guid? SettingsElementTypeKey { get; set; } [JsonProperty("view")] public string View { get; set; } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs index b2822dada9..2b04106288 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -51,7 +51,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { var configuration = propertyType.DataType.ConfigurationAs(); var contentTypes = configuration.Blocks; - var contentTypeMap = contentTypes.ToDictionary(x => x.Key); + var contentElementTypeMap = contentTypes.ToDictionary(x => x.ContentElementTypeKey); var contentPublishedElements = new Dictionary(); var settingsPublishedElements = new Dictionary(); @@ -100,12 +100,19 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters if (!contentData.ContentType.TryGetKey(out var contentTypeKey)) throw new InvalidOperationException("The content type was not of type " + typeof(IPublishedContentType2)); - if (!contentTypeMap.TryGetValue(contentTypeKey, out var blockConfig)) + if (!contentElementTypeMap.TryGetValue(contentTypeKey, out var blockConfig)) continue; // this can happen if they have a settings type, save content, remove the settings type, and display the front-end page before saving the content again - if (settingsData != null && string.IsNullOrWhiteSpace(blockConfig.SettingsElementTypeKey)) - settingsData = null; + // we also ensure that the content type's match since maybe the settings type has been changed after this has been persisted. + if (settingsData != null) + { + if (!settingsData.ContentType.TryGetKey(out var settingsElementTypeKey)) + throw new InvalidOperationException("The settings element type was not of type " + typeof(IPublishedContentType2)); + + if (!blockConfig.SettingsElementTypeKey.HasValue || settingsElementTypeKey != blockConfig.SettingsElementTypeKey) + settingsData = null; + } var layoutRef = new BlockListLayoutReference(contentGuidUdi, contentData, settingGuidUdi, settingsData); layout.Add(layoutRef);