Support block level variant tags (#17268)

This commit is contained in:
Kenn Jacobsen
2024-10-15 12:03:05 +02:00
committed by GitHub
parent fc0e62faa1
commit 0e700af35d
7 changed files with 149 additions and 41 deletions

View File

@@ -1,8 +1,10 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Blocks;
@@ -20,6 +22,7 @@ public abstract class BlockEditorPropertyValueEditor<TValue, TLayout> : BlockVal
{
private readonly IJsonSerializer _jsonSerializer;
[Obsolete("Please use the non-obsolete constructor. Will be removed in V16.")]
protected BlockEditorPropertyValueEditor(
DataEditorAttribute attribute,
PropertyEditorCollection propertyEditors,
@@ -29,9 +32,22 @@ public abstract class BlockEditorPropertyValueEditor<TValue, TLayout> : BlockVal
ILogger<BlockEditorPropertyValueEditor<TValue, TLayout>> logger,
IShortStringHelper shortStringHelper,
IJsonSerializer jsonSerializer,
IIOHelper ioHelper,
BlockEditorVarianceHandler blockEditorVarianceHandler)
: base(attribute, propertyEditors, dataTypeConfigurationCache, textService, logger, shortStringHelper, jsonSerializer, ioHelper, dataValueReferenceFactories, blockEditorVarianceHandler) =>
IIOHelper ioHelper)
: this(propertyEditors, dataValueReferenceFactories, dataTypeConfigurationCache, shortStringHelper, jsonSerializer,
StaticServiceProvider.Instance.GetRequiredService<BlockEditorVarianceHandler>(),
StaticServiceProvider.Instance.GetRequiredService<ILanguageService>())
{
}
protected BlockEditorPropertyValueEditor(
PropertyEditorCollection propertyEditors,
DataValueReferenceFactoryCollection dataValueReferenceFactories,
IDataTypeConfigurationCache dataTypeConfigurationCache,
IShortStringHelper shortStringHelper,
IJsonSerializer jsonSerializer,
BlockEditorVarianceHandler blockEditorVarianceHandler,
ILanguageService languageService)
: base(propertyEditors, dataTypeConfigurationCache, shortStringHelper, jsonSerializer, dataValueReferenceFactories, blockEditorVarianceHandler, languageService) =>
_jsonSerializer = jsonSerializer;
/// <inheritdoc />

View File

@@ -5,7 +5,6 @@ using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Cache.PropertyEditors;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Blocks;
using Umbraco.Cms.Core.PropertyEditors.ValueConverters;
@@ -37,12 +36,11 @@ public abstract class BlockGridPropertyEditorBase : DataEditor
#region Value Editor
protected override IDataValueEditor CreateValueEditor() =>
DataValueEditorFactory.Create<BlockGridEditorPropertyValueEditor>(Attribute!);
DataValueEditorFactory.Create<BlockGridEditorPropertyValueEditor>();
internal class BlockGridEditorPropertyValueEditor : BlockEditorPropertyValueEditor<BlockGridValue, BlockGridLayoutItem>
{
public BlockGridEditorPropertyValueEditor(
DataEditorAttribute attribute,
PropertyEditorCollection propertyEditors,
DataValueReferenceFactoryCollection dataValueReferenceFactories,
IDataTypeConfigurationCache dataTypeConfigurationCache,
@@ -50,11 +48,11 @@ public abstract class BlockGridPropertyEditorBase : DataEditor
ILogger<BlockGridEditorPropertyValueEditor> logger,
IShortStringHelper shortStringHelper,
IJsonSerializer jsonSerializer,
IIOHelper ioHelper,
IBlockEditorElementTypeCache elementTypeCache,
IPropertyValidationService propertyValidationService,
BlockEditorVarianceHandler blockEditorVarianceHandler)
: base(attribute, propertyEditors, dataValueReferenceFactories, dataTypeConfigurationCache, textService, logger, shortStringHelper, jsonSerializer, ioHelper, blockEditorVarianceHandler)
BlockEditorVarianceHandler blockEditorVarianceHandler,
ILanguageService languageService)
: base(propertyEditors, dataValueReferenceFactories, dataTypeConfigurationCache, shortStringHelper, jsonSerializer, blockEditorVarianceHandler, languageService)
{
BlockEditorValues = new BlockEditorValues<BlockGridValue, BlockGridLayoutItem>(new BlockGridEditorDataConverter(jsonSerializer), elementTypeCache, logger);
Validators.Add(new BlockEditorValidator<BlockGridValue, BlockGridLayoutItem>(propertyValidationService, BlockEditorValues, elementTypeCache));

View File

@@ -3,7 +3,6 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Cache.PropertyEditors;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Blocks;
using Umbraco.Cms.Core.PropertyEditors.ValueConverters;
@@ -47,12 +46,11 @@ public abstract class BlockListPropertyEditorBase : DataEditor
protected virtual BlockEditorDataConverter<BlockListValue, BlockListLayoutItem> CreateBlockEditorDataConverter() => new BlockListEditorDataConverter(_jsonSerializer);
protected override IDataValueEditor CreateValueEditor() =>
DataValueEditorFactory.Create<BlockListEditorPropertyValueEditor>(Attribute!, CreateBlockEditorDataConverter());
DataValueEditorFactory.Create<BlockListEditorPropertyValueEditor>(CreateBlockEditorDataConverter());
internal class BlockListEditorPropertyValueEditor : BlockEditorPropertyValueEditor<BlockListValue, BlockListLayoutItem>
{
public BlockListEditorPropertyValueEditor(
DataEditorAttribute attribute,
BlockEditorDataConverter<BlockListValue, BlockListLayoutItem> blockEditorDataConverter,
PropertyEditorCollection propertyEditors,
DataValueReferenceFactoryCollection dataValueReferenceFactories,
@@ -62,10 +60,10 @@ public abstract class BlockListPropertyEditorBase : DataEditor
ILogger<BlockListEditorPropertyValueEditor> logger,
IShortStringHelper shortStringHelper,
IJsonSerializer jsonSerializer,
IIOHelper ioHelper,
IPropertyValidationService propertyValidationService,
BlockEditorVarianceHandler blockEditorVarianceHandler)
: base(attribute, propertyEditors, dataValueReferenceFactories, dataTypeConfigurationCache, textService, logger, shortStringHelper, jsonSerializer, ioHelper, blockEditorVarianceHandler)
BlockEditorVarianceHandler blockEditorVarianceHandler,
ILanguageService languageService)
: base(propertyEditors, dataValueReferenceFactories, dataTypeConfigurationCache, shortStringHelper, jsonSerializer, blockEditorVarianceHandler, languageService)
{
BlockEditorValues = new BlockEditorValues<BlockListValue, BlockListLayoutItem>(blockEditorDataConverter, elementTypeCache, logger);
Validators.Add(new BlockEditorValidator<BlockListValue, BlockListLayoutItem>(propertyValidationService, BlockEditorValues, elementTypeCache));

View File

@@ -1,5 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Blocks;
@@ -8,7 +10,6 @@ using Umbraco.Cms.Core.PropertyEditors.ValueConverters;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Strings;
using Umbraco.Extensions;
namespace Umbraco.Cms.Core.PropertyEditors;
@@ -18,12 +19,13 @@ public abstract class BlockValuePropertyValueEditorBase<TValue, TLayout> : DataV
{
private readonly IDataTypeConfigurationCache _dataTypeConfigurationCache;
private readonly PropertyEditorCollection _propertyEditors;
private readonly ILogger _logger;
private readonly IJsonSerializer _jsonSerializer;
private readonly DataValueReferenceFactoryCollection _dataValueReferenceFactoryCollection;
private readonly BlockEditorVarianceHandler _blockEditorVarianceHandler;
private BlockEditorValues<TValue, TLayout>? _blockEditorValues;
private readonly ILanguageService _languageService;
[Obsolete("Please use the non-obsolete constructor. Will be removed in V16.")]
protected BlockValuePropertyValueEditorBase(
DataEditorAttribute attribute,
PropertyEditorCollection propertyEditors,
@@ -33,16 +35,33 @@ public abstract class BlockValuePropertyValueEditorBase<TValue, TLayout> : DataV
IShortStringHelper shortStringHelper,
IJsonSerializer jsonSerializer,
IIOHelper ioHelper,
DataValueReferenceFactoryCollection dataValueReferenceFactoryCollection)
: this(propertyEditors,
dataTypeConfigurationCache,
shortStringHelper,
jsonSerializer,
dataValueReferenceFactoryCollection,
StaticServiceProvider.Instance.GetRequiredService<BlockEditorVarianceHandler>(),
StaticServiceProvider.Instance.GetRequiredService<ILanguageService>())
{
}
protected BlockValuePropertyValueEditorBase(
PropertyEditorCollection propertyEditors,
IDataTypeConfigurationCache dataTypeConfigurationCache,
IShortStringHelper shortStringHelper,
IJsonSerializer jsonSerializer,
DataValueReferenceFactoryCollection dataValueReferenceFactoryCollection,
BlockEditorVarianceHandler blockEditorVarianceHandler)
: base(textService, shortStringHelper, jsonSerializer, ioHelper, attribute)
BlockEditorVarianceHandler blockEditorVarianceHandler,
ILanguageService languageService)
: base(shortStringHelper, jsonSerializer)
{
_propertyEditors = propertyEditors;
_dataTypeConfigurationCache = dataTypeConfigurationCache;
_logger = logger;
_jsonSerializer = jsonSerializer;
_dataValueReferenceFactoryCollection = dataValueReferenceFactoryCollection;
_blockEditorVarianceHandler = blockEditorVarianceHandler;
_languageService = languageService;
}
/// <inheritdoc />
@@ -121,7 +140,10 @@ public abstract class BlockValuePropertyValueEditorBase<TValue, TLayout> : DataV
object? configuration = _dataTypeConfigurationCache.GetConfiguration(blockPropertyValue.PropertyType.DataTypeKey);
result.AddRange(tagsProvider.GetTags(blockPropertyValue.Value, configuration, languageId));
var tagLanguageId = blockPropertyValue.Culture is not null
? _languageService.GetAsync(blockPropertyValue.Culture).GetAwaiter().GetResult()?.Id
: languageId;
result.AddRange(tagsProvider.GetTags(blockPropertyValue.Value, configuration, tagLanguageId));
}
}

View File

@@ -2,7 +2,6 @@
// See LICENSE for more details.
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Cache.PropertyEditors;
@@ -66,7 +65,7 @@ public class RichTextPropertyEditor : DataEditor
/// </summary>
/// <returns></returns>
protected override IDataValueEditor CreateValueEditor() =>
DataValueEditorFactory.Create<RichTextPropertyValueEditor>(Attribute!);
DataValueEditorFactory.Create<RichTextPropertyValueEditor>();
protected override IConfigurationEditor CreateConfigurationEditor() =>
new RichTextConfigurationEditor(_ioHelper);
@@ -78,7 +77,6 @@ public class RichTextPropertyEditor : DataEditor
internal class RichTextPropertyValueEditor : BlockValuePropertyValueEditorBase<RichTextBlockValue, RichTextBlockLayoutItem>
{
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
private readonly ILocalizedTextService _localizedTextService;
private readonly IHtmlSanitizer _htmlSanitizer;
private readonly HtmlImageSourceParser _imageSourceParser;
private readonly HtmlLocalLinkParser _localLinkParser;
@@ -89,28 +87,25 @@ public class RichTextPropertyEditor : DataEditor
private readonly ILogger<RichTextPropertyValueEditor> _logger;
public RichTextPropertyValueEditor(
DataEditorAttribute attribute,
PropertyEditorCollection propertyEditors,
IDataTypeConfigurationCache dataTypeReadCache,
ILogger<RichTextPropertyValueEditor> logger,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
ILocalizedTextService localizedTextService,
IShortStringHelper shortStringHelper,
HtmlImageSourceParser imageSourceParser,
HtmlLocalLinkParser localLinkParser,
RichTextEditorPastedImages pastedImages,
IJsonSerializer jsonSerializer,
IIOHelper ioHelper,
IHtmlSanitizer htmlSanitizer,
IBlockEditorElementTypeCache elementTypeCache,
IPropertyValidationService propertyValidationService,
DataValueReferenceFactoryCollection dataValueReferenceFactoryCollection,
IRichTextRequiredValidator richTextRequiredValidator,
BlockEditorVarianceHandler blockEditorVarianceHandler)
: base(attribute, propertyEditors, dataTypeReadCache, localizedTextService, logger, shortStringHelper, jsonSerializer, ioHelper, dataValueReferenceFactoryCollection, blockEditorVarianceHandler)
BlockEditorVarianceHandler blockEditorVarianceHandler,
ILanguageService languageService)
: base(propertyEditors, dataTypeReadCache, shortStringHelper, jsonSerializer, dataValueReferenceFactoryCollection, blockEditorVarianceHandler, languageService)
{
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
_localizedTextService = localizedTextService;
_imageSourceParser = imageSourceParser;
_localLinkParser = localLinkParser;
_pastedImages = pastedImages;

View File

@@ -28,6 +28,8 @@ public class BlockListPropertyEditorTests : UmbracoIntegrationTest
private PropertyEditorCollection PropertyEditorCollection => GetRequiredService<PropertyEditorCollection>();
private ILanguageService LanguageService => GetRequiredService<ILanguageService>();
[Test]
public async Task Can_Track_References()
{
@@ -147,9 +149,89 @@ public class BlockListPropertyEditorTests : UmbracoIntegrationTest
var tags = valueEditor.GetTags(content.GetValue("blocks"), null, null).ToArray();
Assert.AreEqual(3, tags.Length);
Assert.IsNotNull(tags.Single(tag => tag.Text == "Tag One"));
Assert.IsNotNull(tags.Single(tag => tag.Text == "Tag Two"));
Assert.IsNotNull(tags.Single(tag => tag.Text == "Tag Three"));
Assert.IsNotNull(tags.Single(tag => tag.Text == "Tag One" && tag.LanguageId == null));
Assert.IsNotNull(tags.Single(tag => tag.Text == "Tag Two" && tag.LanguageId == null));
Assert.IsNotNull(tags.Single(tag => tag.Text == "Tag Three" && tag.LanguageId == null));
}
[Test]
public async Task Can_Track_Tags_For_Block_Level_Variance()
{
var result = await LanguageService.CreateAsync(
new Language("da-DK", "Danish"), Constants.Security.SuperUserKey);
Assert.IsTrue(result.Success);
var daDkId = result.Result.Id;
var elementType = ContentTypeBuilder.CreateAllTypesContentType("myElementType", "My Element Type");
elementType.IsElement = true;
elementType.Variations = ContentVariation.Culture;
elementType.PropertyTypes.First(p => p.Alias == "tags").Variations = ContentVariation.Culture;
ContentTypeService.Save(elementType);
var blockListContentType = await CreateBlockListContentType(elementType);
blockListContentType.Variations = ContentVariation.Culture;
ContentTypeService.Save(blockListContentType);
var contentElementKey = Guid.NewGuid();
var blockListValue = new BlockListValue
{
Layout = new Dictionary<string, IEnumerable<IBlockLayoutItem>>
{
{
Constants.PropertyEditors.Aliases.BlockList,
new IBlockLayoutItem[]
{
new BlockListLayoutItem { ContentKey = contentElementKey }
}
}
},
ContentData =
[
new()
{
Key = contentElementKey,
ContentTypeAlias = elementType.Alias,
ContentTypeKey = elementType.Key,
Values =
[
new ()
{
Alias = "tags",
// this is a little skewed, but the tags editor expects a serialized array of strings
Value = JsonSerializer.Serialize(new[] { "Tag One EN", "Tag Two EN", "Tag Three EN" }),
Culture = "en-US"
},
new ()
{
Alias = "tags",
// this is a little skewed, but the tags editor expects a serialized array of strings
Value = JsonSerializer.Serialize(new[] { "Tag One DA", "Tag Two DA", "Tag Three DA" }),
Culture = "da-DK"
}
]
}
]
};
var blocksPropertyValue = JsonSerializer.Serialize(blockListValue);
var content = new ContentBuilder()
.WithContentType(blockListContentType)
.WithCultureName("en-US", "My Blocks EN")
.WithCultureName("da-DK", "My Blocks DA")
.WithPropertyValues(new { blocks = blocksPropertyValue })
.Build();
ContentService.Save(content);
var valueEditor = await GetValueEditor(blockListContentType);
var tags = valueEditor.GetTags(content.GetValue("blocks"), null, null).ToArray();
Assert.AreEqual(6, tags.Length);
Assert.IsNotNull(tags.Single(tag => tag.Text == "Tag One EN" && tag.LanguageId == 1));
Assert.IsNotNull(tags.Single(tag => tag.Text == "Tag Two EN" && tag.LanguageId == 1));
Assert.IsNotNull(tags.Single(tag => tag.Text == "Tag Three EN" && tag.LanguageId == 1));
Assert.IsNotNull(tags.Single(tag => tag.Text == "Tag One DA" && tag.LanguageId == daDkId));
Assert.IsNotNull(tags.Single(tag => tag.Text == "Tag Two DA" && tag.LanguageId == daDkId));
Assert.IsNotNull(tags.Single(tag => tag.Text == "Tag Three DA" && tag.LanguageId == daDkId));
}
[Test]

View File

@@ -39,9 +39,8 @@ public class DataValueEditorReuseTests
var blockVarianceHandler = new BlockEditorVarianceHandler(Mock.Of<ILanguageService>());
_dataValueEditorFactoryMock
.Setup(m =>
m.Create<BlockListPropertyEditorBase.BlockListEditorPropertyValueEditor>(It.IsAny<DataEditorAttribute>(), It.IsAny<BlockEditorDataConverter<BlockListValue, BlockListLayoutItem>>()))
m.Create<BlockListPropertyEditorBase.BlockListEditorPropertyValueEditor>(It.IsAny<BlockEditorDataConverter<BlockListValue, BlockListLayoutItem>>()))
.Returns(() => new BlockListPropertyEditorBase.BlockListEditorPropertyValueEditor(
new DataEditorAttribute("a"),
new BlockListEditorDataConverter(Mock.Of<IJsonSerializer>()),
_propertyEditorCollection,
_dataValueReferenceFactories,
@@ -51,9 +50,9 @@ public class DataValueEditorReuseTests
Mock.Of<ILogger<BlockListPropertyEditorBase.BlockListEditorPropertyValueEditor>>(),
Mock.Of<IShortStringHelper>(),
Mock.Of<IJsonSerializer>(),
Mock.Of<IIOHelper>(),
Mock.Of<IPropertyValidationService>(),
blockVarianceHandler));
blockVarianceHandler,
Mock.Of<ILanguageService>()));
}
[Test]
@@ -99,7 +98,6 @@ public class DataValueEditorReuseTests
{
var blockListPropertyEditor = new BlockListPropertyEditor(
_dataValueEditorFactoryMock.Object,
_propertyEditorCollection,
Mock.Of<IIOHelper>(),
Mock.Of<IBlockValuePropertyIndexValueFactory>(),
Mock.Of<IJsonSerializer>());
@@ -111,7 +109,7 @@ public class DataValueEditorReuseTests
Assert.NotNull(dataValueEditor2);
Assert.AreNotSame(dataValueEditor1, dataValueEditor2);
_dataValueEditorFactoryMock.Verify(
m => m.Create<BlockListPropertyEditorBase.BlockListEditorPropertyValueEditor>(It.IsAny<DataEditorAttribute>(), It.IsAny<BlockEditorDataConverter<BlockListValue, BlockListLayoutItem>>()),
m => m.Create<BlockListPropertyEditorBase.BlockListEditorPropertyValueEditor>(It.IsAny<BlockEditorDataConverter<BlockListValue, BlockListLayoutItem>>()),
Times.Exactly(2));
}
@@ -120,7 +118,6 @@ public class DataValueEditorReuseTests
{
var blockListPropertyEditor = new BlockListPropertyEditor(
_dataValueEditorFactoryMock.Object,
_propertyEditorCollection,
Mock.Of<IIOHelper>(),
Mock.Of<IBlockValuePropertyIndexValueFactory>(),
Mock.Of<IJsonSerializer>());
@@ -134,7 +131,7 @@ public class DataValueEditorReuseTests
Assert.AreEqual("config", ((DataValueEditor)dataValueEditor2).ConfigurationObject);
Assert.AreNotSame(dataValueEditor1, dataValueEditor2);
_dataValueEditorFactoryMock.Verify(
m => m.Create<BlockListPropertyEditorBase.BlockListEditorPropertyValueEditor>(It.IsAny<DataEditorAttribute>(), It.IsAny<BlockEditorDataConverter<BlockListValue, BlockListLayoutItem>>()),
m => m.Create<BlockListPropertyEditorBase.BlockListEditorPropertyValueEditor>(It.IsAny<BlockEditorDataConverter<BlockListValue, BlockListLayoutItem>>()),
Times.Exactly(2));
}
}