2024-02-21 09:05:44 +01:00
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
using Umbraco.Cms.Core.Cache;
|
2023-10-31 12:52:35 +01:00
|
|
|
using Umbraco.Cms.Core.IO;
|
|
|
|
|
using Umbraco.Cms.Core.Models;
|
|
|
|
|
using Umbraco.Cms.Core.Models.Blocks;
|
|
|
|
|
using Umbraco.Cms.Core.Models.Editors;
|
|
|
|
|
using Umbraco.Cms.Core.Serialization;
|
|
|
|
|
using Umbraco.Cms.Core.Services;
|
|
|
|
|
using Umbraco.Cms.Core.Strings;
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Cms.Core.PropertyEditors;
|
|
|
|
|
|
2024-07-26 07:16:08 +01:00
|
|
|
public abstract class BlockValuePropertyValueEditorBase<TValue, TLayout> : DataValueEditor, IDataValueReference, IDataValueTags
|
2023-11-02 11:55:12 +01:00
|
|
|
where TValue : BlockValue<TLayout>, new()
|
|
|
|
|
where TLayout : class, IBlockLayoutItem, new()
|
2023-10-31 12:52:35 +01:00
|
|
|
{
|
2024-02-21 09:05:44 +01:00
|
|
|
private readonly IDataTypeConfigurationCache _dataTypeConfigurationCache;
|
2023-10-31 12:52:35 +01:00
|
|
|
private readonly PropertyEditorCollection _propertyEditors;
|
|
|
|
|
private readonly ILogger _logger;
|
2024-01-23 10:15:49 +01:00
|
|
|
private readonly DataValueReferenceFactoryCollection _dataValueReferenceFactoryCollection;
|
2023-10-31 12:52:35 +01:00
|
|
|
|
|
|
|
|
protected BlockValuePropertyValueEditorBase(
|
|
|
|
|
DataEditorAttribute attribute,
|
|
|
|
|
PropertyEditorCollection propertyEditors,
|
2024-02-21 09:05:44 +01:00
|
|
|
IDataTypeConfigurationCache dataTypeConfigurationCache,
|
2023-10-31 12:52:35 +01:00
|
|
|
ILocalizedTextService textService,
|
|
|
|
|
ILogger logger,
|
|
|
|
|
IShortStringHelper shortStringHelper,
|
|
|
|
|
IJsonSerializer jsonSerializer,
|
2024-01-23 10:15:49 +01:00
|
|
|
IIOHelper ioHelper,
|
|
|
|
|
DataValueReferenceFactoryCollection dataValueReferenceFactoryCollection)
|
2023-10-31 12:52:35 +01:00
|
|
|
: base(textService, shortStringHelper, jsonSerializer, ioHelper, attribute)
|
|
|
|
|
{
|
|
|
|
|
_propertyEditors = propertyEditors;
|
2024-02-21 09:05:44 +01:00
|
|
|
_dataTypeConfigurationCache = dataTypeConfigurationCache;
|
2023-10-31 12:52:35 +01:00
|
|
|
_logger = logger;
|
2024-01-23 10:15:49 +01:00
|
|
|
_dataValueReferenceFactoryCollection = dataValueReferenceFactoryCollection;
|
2023-10-31 12:52:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public abstract IEnumerable<UmbracoEntityReference> GetReferences(object? value);
|
|
|
|
|
|
2023-11-02 11:55:12 +01:00
|
|
|
protected IEnumerable<UmbracoEntityReference> GetBlockValueReferences(TValue blockValue)
|
2023-10-31 12:52:35 +01:00
|
|
|
{
|
2024-01-23 18:56:47 +01:00
|
|
|
var result = new HashSet<UmbracoEntityReference>();
|
2024-01-23 10:15:49 +01:00
|
|
|
BlockItemData.BlockPropertyValue[] propertyValues = blockValue.ContentData.Concat(blockValue.SettingsData)
|
|
|
|
|
.SelectMany(x => x.PropertyValues.Values).ToArray();
|
|
|
|
|
foreach (IGrouping<string, object?> valuesByPropertyEditorAlias in propertyValues.GroupBy(x => x.PropertyType.PropertyEditorAlias, x => x.Value))
|
2023-10-31 12:52:35 +01:00
|
|
|
{
|
2024-01-23 10:15:49 +01:00
|
|
|
if (!_propertyEditors.TryGet(valuesByPropertyEditorAlias.Key, out IDataEditor? dataEditor))
|
2023-10-31 12:52:35 +01:00
|
|
|
{
|
2024-01-23 10:15:49 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
2023-10-31 12:52:35 +01:00
|
|
|
|
2024-01-23 10:15:49 +01:00
|
|
|
var districtValues = valuesByPropertyEditorAlias.Distinct().ToArray();
|
2023-10-31 12:52:35 +01:00
|
|
|
|
2024-01-23 10:15:49 +01:00
|
|
|
if (dataEditor.GetValueEditor() is IDataValueReference reference)
|
|
|
|
|
{
|
|
|
|
|
foreach (UmbracoEntityReference value in districtValues.SelectMany(reference.GetReferences))
|
2023-10-31 12:52:35 +01:00
|
|
|
{
|
2024-01-23 18:56:47 +01:00
|
|
|
result.Add(value);
|
2023-10-31 12:52:35 +01:00
|
|
|
}
|
2024-01-23 10:15:49 +01:00
|
|
|
}
|
2023-10-31 12:52:35 +01:00
|
|
|
|
2024-01-23 10:15:49 +01:00
|
|
|
IEnumerable<UmbracoEntityReference> references = _dataValueReferenceFactoryCollection.GetReferences(dataEditor, districtValues);
|
2023-10-31 12:52:35 +01:00
|
|
|
|
2024-01-23 10:15:49 +01:00
|
|
|
foreach (UmbracoEntityReference value in references)
|
|
|
|
|
{
|
2024-01-23 18:56:47 +01:00
|
|
|
result.Add(value);
|
2023-10-31 12:52:35 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public abstract IEnumerable<ITag> GetTags(object? value, object? dataTypeConfiguration, int? languageId);
|
|
|
|
|
|
2023-11-02 11:55:12 +01:00
|
|
|
protected IEnumerable<ITag> GetBlockValueTags(TValue blockValue, int? languageId)
|
2023-10-31 12:52:35 +01:00
|
|
|
{
|
|
|
|
|
var result = new List<ITag>();
|
2024-02-21 09:05:44 +01:00
|
|
|
|
2023-10-31 12:52:35 +01:00
|
|
|
// loop through all content and settings data
|
|
|
|
|
foreach (BlockItemData row in blockValue.ContentData.Concat(blockValue.SettingsData))
|
|
|
|
|
{
|
|
|
|
|
foreach (KeyValuePair<string, BlockItemData.BlockPropertyValue> prop in row.PropertyValues)
|
|
|
|
|
{
|
|
|
|
|
IDataEditor? propEditor = _propertyEditors[prop.Value.PropertyType.PropertyEditorAlias];
|
|
|
|
|
|
|
|
|
|
IDataValueEditor? valueEditor = propEditor?.GetValueEditor();
|
|
|
|
|
if (valueEditor is not IDataValueTags tagsProvider)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:05:44 +01:00
|
|
|
object? configuration = _dataTypeConfigurationCache.GetConfiguration(prop.Value.PropertyType.DataTypeKey);
|
2023-10-31 12:52:35 +01:00
|
|
|
|
|
|
|
|
result.AddRange(tagsProvider.GetTags(prop.Value.Value, configuration, languageId));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-02 11:55:12 +01:00
|
|
|
protected void MapBlockValueFromEditor(TValue blockValue)
|
2023-10-31 12:52:35 +01:00
|
|
|
{
|
|
|
|
|
MapBlockItemDataFromEditor(blockValue.ContentData);
|
|
|
|
|
MapBlockItemDataFromEditor(blockValue.SettingsData);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-02 11:55:12 +01:00
|
|
|
protected void MapBlockValueToEditor(IProperty property, TValue blockValue)
|
2023-10-31 12:52:35 +01:00
|
|
|
{
|
|
|
|
|
MapBlockItemDataToEditor(property, blockValue.ContentData);
|
|
|
|
|
MapBlockItemDataToEditor(property, blockValue.SettingsData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void MapBlockItemDataToEditor(IProperty property, List<BlockItemData> items)
|
|
|
|
|
{
|
2024-02-21 09:05:44 +01:00
|
|
|
var valEditors = new Dictionary<Guid, IDataValueEditor>();
|
2023-10-31 12:52:35 +01:00
|
|
|
|
|
|
|
|
foreach (BlockItemData row in items)
|
|
|
|
|
{
|
|
|
|
|
foreach (KeyValuePair<string, BlockItemData.BlockPropertyValue> prop in row.PropertyValues)
|
|
|
|
|
{
|
|
|
|
|
// create a temp property with the value
|
|
|
|
|
// - force it to be culture invariant as the block editor can't handle culture variant element properties
|
|
|
|
|
prop.Value.PropertyType.Variations = ContentVariation.Nothing;
|
|
|
|
|
var tempProp = new Property(prop.Value.PropertyType);
|
|
|
|
|
tempProp.SetValue(prop.Value.Value);
|
|
|
|
|
|
|
|
|
|
IDataEditor? propEditor = _propertyEditors[prop.Value.PropertyType.PropertyEditorAlias];
|
|
|
|
|
if (propEditor == null)
|
|
|
|
|
{
|
|
|
|
|
// NOTE: This logic was borrowed from Nested Content and I'm unsure why it exists.
|
|
|
|
|
// if the property editor doesn't exist I think everything will break anyways?
|
|
|
|
|
// update the raw value since this is what will get serialized out
|
|
|
|
|
row.RawPropertyValues[prop.Key] = tempProp.GetValue()?.ToString();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:05:44 +01:00
|
|
|
Guid dataTypeKey = prop.Value.PropertyType.DataTypeKey;
|
|
|
|
|
if (!valEditors.TryGetValue(dataTypeKey, out IDataValueEditor? valEditor))
|
2023-10-31 12:52:35 +01:00
|
|
|
{
|
2024-02-21 09:05:44 +01:00
|
|
|
var configuration = _dataTypeConfigurationCache.GetConfiguration(dataTypeKey);
|
|
|
|
|
valEditor = propEditor.GetValueEditor(configuration);
|
2023-10-31 12:52:35 +01:00
|
|
|
|
2024-02-21 09:05:44 +01:00
|
|
|
valEditors.Add(dataTypeKey, valEditor);
|
2023-10-31 12:52:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var convValue = valEditor.ToEditor(tempProp);
|
|
|
|
|
|
|
|
|
|
// update the raw value since this is what will get serialized out
|
|
|
|
|
row.RawPropertyValues[prop.Key] = convValue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void MapBlockItemDataFromEditor(List<BlockItemData> items)
|
|
|
|
|
{
|
|
|
|
|
foreach (BlockItemData row in items)
|
|
|
|
|
{
|
|
|
|
|
foreach (KeyValuePair<string, BlockItemData.BlockPropertyValue> prop in row.PropertyValues)
|
|
|
|
|
{
|
|
|
|
|
// Fetch the property types prevalue
|
2024-02-21 09:05:44 +01:00
|
|
|
var configuration = _dataTypeConfigurationCache.GetConfiguration(prop.Value.PropertyType.DataTypeKey);
|
2023-10-31 12:52:35 +01:00
|
|
|
|
|
|
|
|
// Lookup the property editor
|
|
|
|
|
IDataEditor? propEditor = _propertyEditors[prop.Value.PropertyType.PropertyEditorAlias];
|
|
|
|
|
if (propEditor == null)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a fake content property data object
|
2024-02-21 09:05:44 +01:00
|
|
|
var contentPropData = new ContentPropertyData(prop.Value.Value, configuration);
|
2023-10-31 12:52:35 +01:00
|
|
|
|
|
|
|
|
// Get the property editor to do it's conversion
|
|
|
|
|
var newValue = propEditor.GetValueEditor().FromEditor(contentPropData, prop.Value.Value);
|
|
|
|
|
|
|
|
|
|
// update the raw value since this is what will get serialized out
|
|
|
|
|
row.RawPropertyValues[prop.Key] = newValue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|