Merge pull request #8854 from ronaldbarendse/v8/bugfix/generic-blocklistitem
8.7RC Get generic BlockListItem settings model type from configuration
This commit is contained in:
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts json block objects into <see cref="IPublishedElement"/>
|
||||
/// Converts JSON block objects into <see cref="IPublishedElement" />.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<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;
|
||||
|
||||
// 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<IEnumerable<BlockListLayoutItem>>();
|
||||
|
||||
// convert the content data
|
||||
// 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;
|
||||
}
|
||||
// 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<Guid, IPublishedElement>();
|
||||
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<BlockListItem>();
|
||||
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,8 +122,13 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
|
||||
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(), settingsData?.GetType() ?? typeof(IPublishedElement));
|
||||
var layoutType = typeof(BlockListItem<,>).MakeGenericType(contentData.GetType(), settingsType);
|
||||
var layoutRef = (BlockListItem)Activator.CreateInstance(layoutType, contentGuidUdi, contentData, settingGuidUdi, settingsData);
|
||||
|
||||
layout.Add(layoutRef);
|
||||
|
||||
Reference in New Issue
Block a user