From 0613de2a092b2b8e25640ddcf83f13b43770407d Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Tue, 8 Sep 2020 23:47:08 +0200 Subject: [PATCH] Get settings model type from configuration (instead of data) --- .../ValueConverters/BlockEditorConverter.cs | 66 +++++++++++------ .../BlockListPropertyValueConverter.cs | 71 +++++++++++-------- 2 files changed, 85 insertions(+), 52 deletions(-) diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs index f043c8e66e..5c7fc6f14d 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; +using System; using Umbraco.Core; using Umbraco.Core.Models.Blocks; using Umbraco.Core.Models.PublishedContent; @@ -10,7 +8,7 @@ using Umbraco.Web.PublishedCache; namespace Umbraco.Web.PropertyEditors.ValueConverters { /// - /// Converts json block objects into + /// Converts JSON block objects into . /// public sealed class BlockEditorConverter { @@ -23,30 +21,52 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters _publishedModelFactory = publishedModelFactory; } - public IPublishedElement ConvertToElement( - BlockItemData data, - PropertyCacheLevel referenceCacheLevel, bool preview) + public IPublishedElement ConvertToElement(BlockItemData data, PropertyCacheLevel referenceCacheLevel, bool preview) { - // hack! we need to cast, we have no choice beacuse we cannot make breaking changes. + var publishedContentType = GetContentType(data.ContentTypeKey); + + // Only convert element types + if (publishedContentType == null || publishedContentType.IsElement == false) + { + return null; + } + + var propertyValues = data.RawPropertyValues; + + // Get the UDI from the deserialized object. If this is empty, we can fallback to checking the 'key' if there is one + var key = (data.Udi is GuidUdi gudi) ? gudi.Guid : Guid.Empty; + if (key == Guid.Empty && propertyValues.TryGetValue("key", out var keyo)) + { + Guid.TryParse(keyo.ToString(), out key); + } + + IPublishedElement element = new PublishedElement(publishedContentType, key, propertyValues, preview, referenceCacheLevel, _publishedSnapshotAccessor); + element = _publishedModelFactory.CreateModel(element); + + return element; + } + + public Type GetModelType(Guid contentTypeKey) + { + var publishedContentType = GetContentType(contentTypeKey); + if (publishedContentType != null) + { + var modelType = ModelType.For(publishedContentType.Alias); + + return _publishedModelFactory.MapModelType(modelType); + } + + return typeof(IPublishedElement); + } + + private IPublishedContentType GetContentType(Guid contentTypeKey) + { + // HACK! We need to cast, we have no choice because we can't make breaking changes (and we need the GUID overload) var publishedContentCache = _publishedSnapshotAccessor.PublishedSnapshot.Content as IPublishedContentCache2; if (publishedContentCache == null) throw new InvalidOperationException("The published content cache is not " + typeof(IPublishedContentCache2)); - // only convert element types - content types will cause an exception when PublishedModelFactory creates the model - var publishedContentType = publishedContentCache.GetContentType(data.ContentTypeKey); - if (publishedContentType == null || publishedContentType.IsElement == false) - return null; - - var propertyValues = data.RawPropertyValues; - - // Get the udi from the deserialized object. If this is empty we can fallback to checking the 'key' if there is one - var key = (data.Udi is GuidUdi gudi) ? gudi.Guid : Guid.Empty; - if (propertyValues.TryGetValue("key", out var keyo)) - Guid.TryParse(keyo.ToString(), out key); - - IPublishedElement element = new PublishedElement(publishedContentType, key, propertyValues, preview, referenceCacheLevel, _publishedSnapshotAccessor); - element = _publishedModelFactory.CreateModel(element); - return element; + return publishedContentCache.GetContentType(contentTypeKey); } } } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs index f46c118174..a4a9dd69df 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; +using System; using System.Collections.Generic; using System.Linq; using Umbraco.Core; @@ -51,16 +49,9 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters using (_proflog.DebugDuration($"ConvertPropertyToBlockList ({propertyType.DataType.Id})")) { - var configuration = propertyType.DataType.ConfigurationAs(); - var blockConfigMap = configuration.Blocks.ToDictionary(x => x.ContentElementTypeKey); - var validSettingElementTypes = blockConfigMap.Values.Select(x => x.SettingsElementTypeKey).Where(x => x.HasValue).Distinct().ToList(); - - var contentPublishedElements = new Dictionary(); - var settingsPublishedElements = new Dictionary(); - - var layout = new List(); - var value = (string)inter; + + // Short-circuit on empty values if (string.IsNullOrWhiteSpace(value)) return BlockListModel.Empty; var converted = _blockListEditorDataConverter.Deserialize(value); @@ -68,49 +59,60 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters var blockListLayout = converted.Layout.ToObject>(); - // convert the content data + // Get configuration + var configuration = propertyType.DataType.ConfigurationAs(); + var blockConfigMap = configuration.Blocks.ToDictionary(x => x.ContentElementTypeKey); + var validSettingsElementTypes = blockConfigMap.Values.Select(x => x.SettingsElementTypeKey).Where(x => x.HasValue).Distinct().ToList(); + + // Convert the content data + var contentPublishedElements = new Dictionary(); foreach (var data in converted.BlockValue.ContentData) { if (!blockConfigMap.ContainsKey(data.ContentTypeKey)) continue; var element = _blockConverter.ConvertToElement(data, referenceCacheLevel, preview); if (element == null) continue; + contentPublishedElements[element.Key] = element; } - // convert the settings data + + // If there are no content elements, it doesn't matter what is stored in layout + if (contentPublishedElements.Count == 0) return BlockListModel.Empty; + + // Convert the settings data + var settingsPublishedElements = new Dictionary(); foreach (var data in converted.BlockValue.SettingsData) { - if (!validSettingElementTypes.Contains(data.ContentTypeKey)) continue; + if (!validSettingsElementTypes.Contains(data.ContentTypeKey)) continue; var element = _blockConverter.ConvertToElement(data, referenceCacheLevel, preview); if (element == null) continue; + settingsPublishedElements[element.Key] = element; } - // if there's no elements just return since if there's no data it doesn't matter what is stored in layout - if (contentPublishedElements.Count == 0) return BlockListModel.Empty; - + var layout = new List(); foreach (var layoutItem in blockListLayout) { - // get the content reference - var contentGuidUdi = (GuidUdi)layoutItem.ContentUdi; + // Get the content reference + var contentGuidUdi = (GuidUdi)layoutItem.ContentUdi; if (!contentPublishedElements.TryGetValue(contentGuidUdi.Guid, out var contentData)) continue; - // get the setting reference - IPublishedElement settingsData = null; - var settingGuidUdi = layoutItem.SettingsUdi != null ? (GuidUdi)layoutItem.SettingsUdi : null; - if (settingGuidUdi != null) - settingsPublishedElements.TryGetValue(settingGuidUdi.Guid, out settingsData); - if (!contentData.ContentType.TryGetKey(out var contentTypeKey)) throw new InvalidOperationException("The content type was not of type " + typeof(IPublishedContentType2)); if (!blockConfigMap.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 - // we also ensure that the content type's match since maybe the settings type has been changed after this has been persisted. + // Get the setting reference + IPublishedElement settingsData = null; + var settingGuidUdi = layoutItem.SettingsUdi != null ? (GuidUdi)layoutItem.SettingsUdi : null; + if (settingGuidUdi != null) + settingsPublishedElements.TryGetValue(settingGuidUdi.Guid, out settingsData); + + // 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 + // We also ensure that the content types match, since maybe the settings type has been changed after this has been persisted if (settingsData != null) { if (!settingsData.ContentType.TryGetKey(out var settingsElementTypeKey)) @@ -120,7 +122,18 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters settingsData = null; } - var layoutType = typeof(BlockListItem<,>).MakeGenericType(contentData.GetType(), settingsData?.GetType() ?? typeof(IPublishedElement)); + // Get settings type from configuration + Type settingsType; + if (blockConfig.SettingsElementTypeKey.HasValue) + { + settingsType = _blockConverter.GetModelType(blockConfig.SettingsElementTypeKey.Value); + } + else + { + settingsType = typeof(IPublishedElement); + } + + var layoutType = typeof(BlockListItem<,>).MakeGenericType(contentData.GetType(), settingsType); var layoutRef = (BlockListItem)Activator.CreateInstance(layoutType, contentGuidUdi, contentData, settingGuidUdi, settingsData); layout.Add(layoutRef);