Merge branch 'v11/dev' into v11/contrib
This commit is contained in:
@@ -13,7 +13,7 @@ using Umbraco.Cms.Core.Strings;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors;
|
||||
|
||||
internal abstract class BlockEditorPropertyValueEditor : DataValueEditor, IDataValueReference
|
||||
internal abstract class BlockEditorPropertyValueEditor : DataValueEditor, IDataValueReference, IDataValueTags
|
||||
{
|
||||
private BlockEditorValues? _blockEditorValues;
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
@@ -77,6 +77,40 @@ internal abstract class BlockEditorPropertyValueEditor : DataValueEditor, IDataV
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<ITag> GetTags(object? value, object? dataTypeConfiguration, int? languageId)
|
||||
{
|
||||
var rawJson = value == null ? string.Empty : value is string str ? str : value.ToString();
|
||||
|
||||
BlockEditorData? blockEditorData = BlockEditorValues.DeserializeAndClean(rawJson);
|
||||
if (blockEditorData == null)
|
||||
{
|
||||
return Enumerable.Empty<ITag>();
|
||||
}
|
||||
|
||||
var result = new List<ITag>();
|
||||
// loop through all content and settings data
|
||||
foreach (BlockItemData row in blockEditorData.BlockValue.ContentData.Concat(blockEditorData.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;
|
||||
}
|
||||
|
||||
object? configuration = _dataTypeService.GetDataType(prop.Value.PropertyType.DataTypeKey)?.Configuration;
|
||||
|
||||
result.AddRange(tagsProvider.GetTags(prop.Value.Value, configuration, languageId));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#region Convert database // editor
|
||||
|
||||
// note: there is NO variant support here
|
||||
|
||||
@@ -69,7 +69,7 @@ public class NestedContentPropertyEditor : DataEditor
|
||||
protected override IDataValueEditor CreateValueEditor()
|
||||
=> DataValueEditorFactory.Create<NestedContentPropertyValueEditor>(Attribute!);
|
||||
|
||||
internal class NestedContentPropertyValueEditor : DataValueEditor, IDataValueReference
|
||||
internal class NestedContentPropertyValueEditor : DataValueEditor, IDataValueReference, IDataValueTags
|
||||
{
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
private readonly ILogger<NestedContentPropertyEditor> _logger;
|
||||
@@ -150,6 +150,36 @@ public class NestedContentPropertyEditor : DataEditor
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<ITag> GetTags(object? value, object? dataTypeConfiguration, int? languageId)
|
||||
{
|
||||
IReadOnlyList<NestedContentValues.NestedContentRowValue> rows =
|
||||
_nestedContentValues.GetPropertyValues(value);
|
||||
|
||||
var result = new List<ITag>();
|
||||
|
||||
foreach (NestedContentValues.NestedContentRowValue row in rows.ToList())
|
||||
{
|
||||
foreach (KeyValuePair<string, NestedContentValues.NestedContentPropertyValue> prop in row.PropertyValues
|
||||
.ToList())
|
||||
{
|
||||
IDataEditor? propEditor = _propertyEditors[prop.Value.PropertyType.PropertyEditorAlias];
|
||||
|
||||
IDataValueEditor? valueEditor = propEditor?.GetValueEditor();
|
||||
if (valueEditor is not IDataValueTags tagsProvider)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
object? configuration = _dataTypeService.GetDataType(prop.Value.PropertyType.DataTypeKey)?.Configuration;
|
||||
|
||||
result.AddRange(tagsProvider.GetTags(prop.Value.Value, configuration, languageId));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#region DB to String
|
||||
|
||||
public override string ConvertDbToString(IPropertyType propertyType, object? propertyValue)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
@@ -68,18 +69,67 @@ public class TagsPropertyEditor : DataEditor
|
||||
protected override IConfigurationEditor CreateConfigurationEditor() =>
|
||||
new TagConfigurationEditor(_validators, _ioHelper, _localizedTextService, _editorConfigurationParser);
|
||||
|
||||
internal class TagPropertyValueEditor : DataValueEditor
|
||||
internal class TagPropertyValueEditor : DataValueEditor, IDataValueTags
|
||||
{
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
|
||||
public TagPropertyValueEditor(
|
||||
ILocalizedTextService localizedTextService,
|
||||
IShortStringHelper shortStringHelper,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IIOHelper ioHelper,
|
||||
DataEditorAttribute attribute)
|
||||
DataEditorAttribute attribute,
|
||||
IDataTypeService dataTypeService)
|
||||
: base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute)
|
||||
{
|
||||
_dataTypeService = dataTypeService;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<ITag> GetTags(object? value, object? dataTypeConfiguration, int? languageId)
|
||||
{
|
||||
var strValue = value?.ToString();
|
||||
if (string.IsNullOrWhiteSpace(strValue)) return Enumerable.Empty<ITag>();
|
||||
|
||||
var tagConfiguration = ConfigurationEditor.ConfigurationAs<TagConfiguration>(dataTypeConfiguration) ?? new TagConfiguration();
|
||||
|
||||
if (tagConfiguration.Delimiter == default)
|
||||
tagConfiguration.Delimiter = ',';
|
||||
|
||||
IEnumerable<string> tags;
|
||||
|
||||
switch (tagConfiguration.StorageType)
|
||||
{
|
||||
case TagsStorageType.Csv:
|
||||
tags = strValue.Split(new[] { tagConfiguration.Delimiter }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim());
|
||||
break;
|
||||
|
||||
case TagsStorageType.Json:
|
||||
try
|
||||
{
|
||||
tags = JsonConvert.DeserializeObject<string[]>(strValue)?.Select(x => x.Trim()) ?? Enumerable.Empty<string>();
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
//cannot parse, malformed
|
||||
tags = Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException($"Value \"{tagConfiguration.StorageType}\" is not a valid TagsStorageType.");
|
||||
}
|
||||
|
||||
return tags.Select(x => new Tag
|
||||
{
|
||||
Group = tagConfiguration.Group,
|
||||
Text = x,
|
||||
LanguageId = languageId,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IValueRequiredValidator RequiredValidator => new RequiredJsonValueValidator();
|
||||
|
||||
@@ -93,14 +143,33 @@ public class TagsPropertyEditor : DataEditor
|
||||
return null;
|
||||
}
|
||||
|
||||
var tagConfiguration = editorValue.DataTypeConfiguration as TagConfiguration ?? new TagConfiguration();
|
||||
if (tagConfiguration.Delimiter == default)
|
||||
tagConfiguration.Delimiter = ',';
|
||||
|
||||
string[] trimmedTags = Array.Empty<string>();
|
||||
|
||||
if (editorValue.Value is JArray json)
|
||||
{
|
||||
return json.HasValues ? json.Select(x => x.Value<string>()) : null;
|
||||
trimmedTags = json.HasValues ? json.Select(x => x.Value<string>()).OfType<string>().ToArray() : Array.Empty<string>();
|
||||
}
|
||||
else if (string.IsNullOrWhiteSpace(value) == false)
|
||||
{
|
||||
trimmedTags = value.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(value) == false)
|
||||
if (trimmedTags.Length == 0)
|
||||
{
|
||||
return value.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries);
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (tagConfiguration.StorageType)
|
||||
{
|
||||
case TagsStorageType.Csv:
|
||||
return string.Join(tagConfiguration.Delimiter.ToString(), trimmedTags).NullOrWhiteSpaceAsNull();
|
||||
|
||||
case TagsStorageType.Json:
|
||||
return trimmedTags.Length == 0 ? null : JsonConvert.SerializeObject(trimmedTags);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Logging;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Blocks;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Web.Common.DependencyInjection;
|
||||
using Umbraco.Extensions;
|
||||
using static Umbraco.Cms.Core.PropertyEditors.BlockListConfiguration;
|
||||
|
||||
@@ -12,18 +17,70 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters;
|
||||
[DefaultPropertyValueConverter(typeof(JsonValueConverter))]
|
||||
public class BlockListPropertyValueConverter : BlockPropertyValueConverterBase<BlockListModel, BlockListItem, BlockListLayoutItem, BlockConfiguration>
|
||||
{
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
private readonly BlockEditorConverter _blockConverter;
|
||||
private readonly BlockListEditorDataConverter _blockListEditorDataConverter;
|
||||
private readonly IProfilingLogger _proflog;
|
||||
|
||||
public BlockListPropertyValueConverter(IProfilingLogger proflog, BlockEditorConverter blockConverter)
|
||||
[Obsolete("Use the constructor with the IContentTypeService")]
|
||||
public BlockListPropertyValueConverter(IProfilingLogger proflog, BlockEditorConverter blockConverter) : this(proflog, blockConverter, StaticServiceProvider.Instance.GetRequiredService<IContentTypeService>()) { }
|
||||
|
||||
public BlockListPropertyValueConverter(IProfilingLogger proflog, BlockEditorConverter blockConverter, IContentTypeService contentTypeService)
|
||||
: base(blockConverter)
|
||||
{
|
||||
_proflog = proflog;
|
||||
_blockConverter = blockConverter;
|
||||
_blockListEditorDataConverter = new BlockListEditorDataConverter();
|
||||
_contentTypeService = contentTypeService;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsConverter(IPublishedPropertyType propertyType)
|
||||
=> propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.BlockList);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type GetPropertyValueType(IPublishedPropertyType propertyType)
|
||||
{
|
||||
var isSingleBlockMode = IsSingleBlockMode(propertyType.DataType);
|
||||
if (isSingleBlockMode)
|
||||
{
|
||||
BlockListConfiguration.BlockConfiguration? block =
|
||||
ConfigurationEditor.ConfigurationAs<BlockListConfiguration>(propertyType.DataType.Configuration)?.Blocks.FirstOrDefault();
|
||||
|
||||
ModelType? contentElementType = block?.ContentElementTypeKey is Guid contentElementTypeKey && _contentTypeService.Get(contentElementTypeKey) is IContentType contentType ? ModelType.For(contentType.Alias) : null;
|
||||
ModelType? settingsElementType = block?.SettingsElementTypeKey is Guid settingsElementTypeKey && _contentTypeService.Get(settingsElementTypeKey) is IContentType settingsType ? ModelType.For(settingsType.Alias) : null;
|
||||
|
||||
if (contentElementType is not null)
|
||||
{
|
||||
if (settingsElementType is not null)
|
||||
{
|
||||
return typeof(BlockListItem<,>).MakeGenericType(contentElementType, settingsElementType);
|
||||
}
|
||||
|
||||
return typeof(BlockListItem<>).MakeGenericType(contentElementType);
|
||||
}
|
||||
|
||||
return typeof(BlockListItem);
|
||||
}
|
||||
|
||||
return typeof(BlockListModel);
|
||||
}
|
||||
|
||||
private bool IsSingleBlockMode(PublishedDataType dataType)
|
||||
{
|
||||
BlockListConfiguration? config =
|
||||
ConfigurationEditor.ConfigurationAs<BlockListConfiguration>(dataType.Configuration);
|
||||
return (config?.UseSingleBlockMode ?? false) && config?.Blocks.Length == 1 && config?.ValidationLimit?.Min == 1 && config?.ValidationLimit?.Max == 1;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType)
|
||||
=> PropertyCacheLevel.Element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object? ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object? source, bool preview)
|
||||
=> source?.ToString();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object? ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object? inter, bool preview)
|
||||
{
|
||||
@@ -44,7 +101,7 @@ public class BlockListPropertyValueConverter : BlockPropertyValueConverterBase<B
|
||||
|
||||
BlockListModel blockModel = UnwrapBlockModel(referenceCacheLevel, inter, preview, configuration.Blocks, CreateEmptyModel, CreateModel);
|
||||
|
||||
return blockModel;
|
||||
return IsSingleBlockMode(propertyType.DataType) ? blockModel.FirstOrDefault() : blockModel;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user