Files
Umbraco-CMS/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs
Matt Brailsford b14ac811ef Make block editor base classes public (#16774)
* Make block editor base classes public

* Update BlockEditorValues.cs

Change to trigger a new build for #16774

---------

Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2024-07-26 08:16:08 +02:00

176 lines
6.7 KiB
C#

// Copyright (c) Umbraco.
// See LICENSE for more details.
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Cache;
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;
public abstract class BlockEditorPropertyValueEditor<TValue, TLayout> : BlockValuePropertyValueEditorBase<TValue, TLayout>
where TValue : BlockValue<TLayout>, new()
where TLayout : class, IBlockLayoutItem, new()
{
private readonly IJsonSerializer _jsonSerializer;
private BlockEditorValues<TValue, TLayout>? _blockEditorValues;
private readonly IDataTypeConfigurationCache _dataTypeConfigurationCache;
private readonly PropertyEditorCollection _propertyEditors;
private readonly DataValueReferenceFactoryCollection _dataValueReferenceFactories;
protected BlockEditorPropertyValueEditor(
DataEditorAttribute attribute,
PropertyEditorCollection propertyEditors,
DataValueReferenceFactoryCollection dataValueReferenceFactories,
IDataTypeConfigurationCache dataTypeConfigurationCache,
ILocalizedTextService textService,
ILogger<BlockEditorPropertyValueEditor<TValue, TLayout>> logger,
IShortStringHelper shortStringHelper,
IJsonSerializer jsonSerializer,
IIOHelper ioHelper)
: base(attribute, propertyEditors, dataTypeConfigurationCache, textService, logger, shortStringHelper, jsonSerializer, ioHelper, dataValueReferenceFactories)
{
_propertyEditors = propertyEditors;
_dataValueReferenceFactories = dataValueReferenceFactories;
_dataTypeConfigurationCache = dataTypeConfigurationCache;
_jsonSerializer = jsonSerializer;
}
protected BlockEditorValues<TValue, TLayout> BlockEditorValues
{
get => _blockEditorValues ?? throw new NullReferenceException($"The property {nameof(BlockEditorValues)} must be initialized at value editor construction");
set => _blockEditorValues = value;
}
/// <inheritdoc />
public override IEnumerable<UmbracoEntityReference> GetReferences(object? value)
{
// Group by property editor alias to avoid duplicate lookups and optimize value parsing
foreach (var valuesByPropertyEditorAlias in GetAllPropertyValues(value).GroupBy(x => x.PropertyType.PropertyEditorAlias, x => x.Value))
{
if (!_propertyEditors.TryGet(valuesByPropertyEditorAlias.Key, out IDataEditor? dataEditor))
{
continue;
}
// Use distinct values to avoid duplicate parsing of the same value
foreach (UmbracoEntityReference reference in _dataValueReferenceFactories.GetReferences(dataEditor, valuesByPropertyEditorAlias.Distinct()))
{
yield return reference;
}
}
}
/// <inheritdoc />
public override IEnumerable<ITag> GetTags(object? value, object? dataTypeConfiguration, int? languageId)
{
foreach (BlockItemData.BlockPropertyValue propertyValue in GetAllPropertyValues(value))
{
if (!_propertyEditors.TryGet(propertyValue.PropertyType.PropertyEditorAlias, out IDataEditor? dataEditor) ||
dataEditor.GetValueEditor() is not IDataValueTags dataValueTags)
{
continue;
}
object? configuration = _dataTypeConfigurationCache.GetConfiguration(propertyValue.PropertyType.DataTypeKey);
foreach (ITag tag in dataValueTags.GetTags(propertyValue.Value, configuration, languageId))
{
yield return tag;
}
}
}
private IEnumerable<BlockItemData.BlockPropertyValue> GetAllPropertyValues(object? value)
{
var rawJson = value == null ? string.Empty : value is string str ? str : value.ToString();
BlockEditorData<TValue, TLayout>? blockEditorData = BlockEditorValues.DeserializeAndClean(rawJson);
if (blockEditorData is null)
{
yield break;
}
// Return all property values from the content and settings data
IEnumerable<BlockItemData> data = blockEditorData.BlockValue.ContentData.Concat(blockEditorData.BlockValue.SettingsData);
foreach (BlockItemData.BlockPropertyValue propertyValue in data.SelectMany(x => x.PropertyValues.Select(x => x.Value)))
{
yield return propertyValue;
}
}
// note: there is NO variant support here
/// <summary>
/// Ensure that sub-editor values are translated through their ToEditor methods
/// </summary>
/// <param name="property"></param>
/// <param name="culture"></param>
/// <param name="segment"></param>
/// <returns></returns>
public override object ToEditor(IProperty property, string? culture = null, string? segment = null)
{
var val = property.GetValue(culture, segment);
BlockEditorData<TValue, TLayout>? blockEditorData;
try
{
blockEditorData = BlockEditorValues.DeserializeAndClean(val);
}
catch
{
// if this occurs it means the data is invalid, shouldn't happen but has happened if we change the data format.
return string.Empty;
}
if (blockEditorData == null)
{
return string.Empty;
}
MapBlockValueToEditor(property, blockEditorData.BlockValue);
// return json convertable object
return blockEditorData.BlockValue;
}
/// <summary>
/// Ensure that sub-editor values are translated through their FromEditor methods
/// </summary>
/// <param name="editorValue"></param>
/// <param name="currentValue"></param>
/// <returns></returns>
public override object? FromEditor(ContentPropertyData editorValue, object? currentValue)
{
if (editorValue.Value == null || string.IsNullOrWhiteSpace(editorValue.Value.ToString()))
{
return null;
}
BlockEditorData<TValue, TLayout>? blockEditorData;
try
{
blockEditorData = BlockEditorValues.DeserializeAndClean(editorValue.Value);
}
catch
{
// if this occurs it means the data is invalid, shouldn't happen but has happened if we change the data format.
return string.Empty;
}
if (blockEditorData == null || blockEditorData.BlockValue.ContentData.Count == 0)
{
return string.Empty;
}
MapBlockValueFromEditor(blockEditorData.BlockValue);
// return json
return _jsonSerializer.Serialize(blockEditorData.BlockValue);
}
}