/// <inheritdoc /> public override object?ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object?inter, bool preview) { // NOTE: The intermediate object is just a JSON string, we don't actually convert from source -> intermediate since source is always just a JSON string using (_proflog.DebugDuration <BlockListPropertyValueConverter>($"ConvertPropertyToBlockList ({propertyType.DataType.Id})")) { var value = (string?)inter; // Short-circuit on empty values if (string.IsNullOrWhiteSpace(value)) { return(BlockListModel.Empty); } var converted = _blockListEditorDataConverter.Deserialize(value); if (converted.BlockValue.ContentData.Count == 0) { return(BlockListModel.Empty); } var blockListLayout = converted.Layout?.ToObject <IEnumerable <BlockListLayoutItem> >(); if (blockListLayout is null) { return(BlockListModel.Empty); } // Get configuration var configuration = propertyType.DataType.ConfigurationAs <BlockListConfiguration>(); if (configuration is null) { return(null); } var blockConfigMap = configuration.Blocks.ToDictionary(x => x.ContentElementTypeKey); // Convert the content data var contentPublishedElements = new Dictionary <Guid, IPublishedElement>(); 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; } // 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 <Guid, IPublishedElement>(); var validSettingsElementTypes = blockConfigMap.Values.Select(x => x.SettingsElementTypeKey).Where(x => x.HasValue).Distinct().ToList(); if (validSettingsElementTypes is not null) { foreach (var data in converted.BlockValue.SettingsData) { if (!validSettingsElementTypes.Contains(data.ContentTypeKey)) { continue; } var element = _blockConverter.ConvertToElement(data, referenceCacheLevel, preview); if (element is null) { continue; } settingsPublishedElements[element.Key] = element; } } // Cache constructors locally (it's tied to the current IPublishedSnapshot and IPublishedModelFactory) var blockListItemActivator = new BlockListItemActivator(_blockConverter); var list = new List <BlockListItem>(); foreach (var layoutItem in blockListLayout) { // Get the content reference var contentGuidUdi = (GuidUdi?)layoutItem.ContentUdi; if (contentGuidUdi is null || !contentPublishedElements.TryGetValue(contentGuidUdi.Guid, out var contentData)) { continue; } if (!blockConfigMap.TryGetValue(contentData.ContentType.Key, out var blockConfig)) { continue; } // Get the setting reference IPublishedElement?settingsData = null; var settingGuidUdi = (GuidUdi?)layoutItem.SettingsUdi; if (settingGuidUdi is not 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 is not null && (!blockConfig.SettingsElementTypeKey.HasValue || settingsData.ContentType.Key != blockConfig.SettingsElementTypeKey)) { settingsData = null; } // Create instance (use content/settings type from configuration) var layoutRef = blockListItemActivator.CreateInstance(blockConfig.ContentElementTypeKey, blockConfig.SettingsElementTypeKey, contentGuidUdi, contentData, settingGuidUdi, settingsData); list.Add(layoutRef); } return(new BlockListModel(list)); } }
/// <inheritdoc /> public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { // NOTE: The intermediate object is just a json string, we don't actually convert from source -> intermediate since source is always just a json string using (_proflog.DebugDuration <BlockListPropertyValueConverter>($"ConvertPropertyToBlockList ({propertyType.DataType.Id})")) { var value = (string)inter; // Short-circuit on empty values if (string.IsNullOrWhiteSpace(value)) { return(BlockListModel.Empty); } var converted = _blockListEditorDataConverter.Deserialize(value); if (converted.BlockValue.ContentData.Count == 0) { return(BlockListModel.Empty); } var blockListLayout = converted.Layout.ToObject <IEnumerable <BlockListLayoutItem> >(); // Get configuration var configuration = propertyType.DataType.ConfigurationAs <BlockListConfiguration>(); 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 <Guid, IPublishedElement>(); 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; } // 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 <Guid, IPublishedElement>(); foreach (var data in converted.BlockValue.SettingsData) { if (!validSettingsElementTypes.Contains(data.ContentTypeKey)) { continue; } var element = _blockConverter.ConvertToElement(data, referenceCacheLevel, preview); if (element == null) { continue; } settingsPublishedElements[element.Key] = element; } var layout = new List <BlockListItem>(); foreach (var layoutItem in blockListLayout) { // Get the content reference var contentGuidUdi = (GuidUdi)layoutItem.ContentUdi; if (!contentPublishedElements.TryGetValue(contentGuidUdi.Guid, out var contentData)) { continue; } if (!blockConfigMap.TryGetValue(contentData.ContentType.Key, out var blockConfig)) { 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); } // 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 && (!blockConfig.SettingsElementTypeKey.HasValue || settingsData.ContentType.Key != blockConfig.SettingsElementTypeKey)) { settingsData = null; } // Get settings type from configuration var settingsType = blockConfig.SettingsElementTypeKey.HasValue ? _blockConverter.GetModelType(blockConfig.SettingsElementTypeKey.Value) : typeof(IPublishedElement); // TODO: This should be optimized/cached, as calling Activator.CreateInstance is slow var layoutType = typeof(BlockListItem <,>).MakeGenericType(contentData.GetType(), settingsType); var layoutRef = (BlockListItem)Activator.CreateInstance(layoutType, contentGuidUdi, contentData, settingGuidUdi, settingsData); layout.Add(layoutRef); } var model = new BlockListModel(layout); return(model); } }