Merge branch 'v11/dev' into v11/contrib

This commit is contained in:
Sebastiaan Janssen
2022-12-12 13:59:03 +01:00
116 changed files with 1609 additions and 776 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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;

View File

@@ -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;
}
}