// internal for tests internal string ReplaceBlockListUdis(string rawJson, Func <Guid> createGuid = null) { // used so we can test nicely if (createGuid == null) { createGuid = () => Guid.NewGuid(); } if (string.IsNullOrWhiteSpace(rawJson) || !rawJson.DetectIsJson()) { return(rawJson); } // Parse JSON // This will throw a FormatException if there are null UDIs (expected) var blockListValue = _converter.Deserialize(rawJson); UpdateBlockListRecursively(blockListValue, createGuid); return(JsonConvert.SerializeObject(blockListValue.BlockValue)); }
/// <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 configuration = propertyType.DataType.ConfigurationAs <BlockListConfiguration>(); 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 <Guid, IPublishedElement>(); var settingsPublishedElements = new Dictionary <Guid, IPublishedElement>(); var layout = new List <BlockListItem>(); var value = (string)inter; 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> >(); // convert the content data 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 foreach (var data in converted.BlockValue.SettingsData) { if (!validSettingElementTypes.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); } foreach (var layoutItem in blockListLayout) { // 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. 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 layoutType = typeof(BlockListItem <,>).MakeGenericType(contentData.GetType(), settingsData?.GetType() ?? typeof(IPublishedElement)); var layoutRef = (BlockListItem)Activator.CreateInstance(layoutType, contentGuidUdi, contentData, settingGuidUdi, settingsData); layout.Add(layoutRef); } var model = new BlockListModel(layout); return(model); } }
/// <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); } BlockEditorData converted = _blockListEditorDataConverter.Deserialize(value); if (converted.BlockValue.ContentData.Count == 0) { return(BlockListModel.Empty); } IEnumerable <BlockListLayoutItem>?blockListLayout = converted.Layout?.ToObject <IEnumerable <BlockListLayoutItem> >(); if (blockListLayout is null) { return(BlockListModel.Empty); } // Get configuration BlockListConfiguration?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 (BlockItemData data in converted.BlockValue.ContentData) { if (!blockConfigMap.ContainsKey(data.ContentTypeKey)) { continue; } IPublishedElement?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(); foreach (BlockItemData data in converted.BlockValue.SettingsData) { if (!validSettingsElementTypes.Contains(data.ContentTypeKey)) { continue; } IPublishedElement?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 (BlockListLayoutItem layoutItem in blockListLayout) { // Get the content reference var contentGuidUdi = (GuidUdi?)layoutItem.ContentUdi; if (contentGuidUdi is null || !contentPublishedElements.TryGetValue(contentGuidUdi.Guid, out IPublishedElement? contentData)) { continue; } if (!blockConfigMap.TryGetValue( contentData.ContentType.Key, out BlockListConfiguration.BlockConfiguration? 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) BlockListItem layoutRef = blockListItemActivator.CreateInstance(blockConfig.ContentElementTypeKey, blockConfig.SettingsElementTypeKey, contentGuidUdi, contentData, settingGuidUdi, settingsData); list.Add(layoutRef); } return(new BlockListModel(list)); } }