/// <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)); } }
//A copy of the core BlockListValuePropertyConverter method, but if the propertyType is null, it doesn't validate each block agains the property type configuration and just returns the block model. //Could move this to the core, but it feels like there's a bigger problem with blocks here if they are supposed to be share across data types, there shuold be a single resolver here just to get the block models public object ConvertIntermediateToObjectCustom(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 BlockListConfiguration configuration = propertyType != null?propertyType.DataType.ConfigurationAs <BlockListConfiguration>() : null; Dictionary <Guid, BlockListConfiguration.BlockConfiguration> blockConfigMap = configuration != null?configuration.Blocks.ToDictionary(x => x.ContentElementTypeKey) : null; IList <Guid?> validSettingElementTypes = blockConfigMap != null?blockConfigMap.Values.Select(x => x.SettingsElementTypeKey).Where(x => x.HasValue).Distinct().ToList() : null; 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 != null && !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 != null && !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)); } BlockListConfiguration.BlockConfiguration blockConfig = null; if (blockConfigMap != null && !blockConfigMap.TryGetValue(contentTypeKey, out 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 != null && (!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); }