From 0e700af35d4ca4039752d1c38645a6842b5760c6 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 15 Oct 2024 12:03:05 +0200 Subject: [PATCH] Support block level variant tags (#17268) --- .../BlockEditorPropertyValueEditor.cs | 22 ++++- .../BlockGridPropertyEditorBase.cs | 10 +-- .../BlockListPropertyEditorBase.cs | 10 +-- .../BlockValuePropertyValueEditorBase.cs | 34 +++++-- .../PropertyEditors/RichTextPropertyEditor.cs | 13 +-- .../BlockListPropertyEditorTests.cs | 88 ++++++++++++++++++- .../DataValueEditorReuseTests.cs | 13 ++- 7 files changed, 149 insertions(+), 41 deletions(-) diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs index 97fcd17aa9..798cdf1d7d 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs @@ -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 : 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 : BlockVal ILogger> 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(), + StaticServiceProvider.Instance.GetRequiredService()) + { + } + + 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; /// diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockGridPropertyEditorBase.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockGridPropertyEditorBase.cs index 2b7364b1aa..b0e8ac46fc 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockGridPropertyEditorBase.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockGridPropertyEditorBase.cs @@ -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(Attribute!); + DataValueEditorFactory.Create(); internal class BlockGridEditorPropertyValueEditor : BlockEditorPropertyValueEditor { public BlockGridEditorPropertyValueEditor( - DataEditorAttribute attribute, PropertyEditorCollection propertyEditors, DataValueReferenceFactoryCollection dataValueReferenceFactories, IDataTypeConfigurationCache dataTypeConfigurationCache, @@ -50,11 +48,11 @@ public abstract class BlockGridPropertyEditorBase : DataEditor ILogger 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(new BlockGridEditorDataConverter(jsonSerializer), elementTypeCache, logger); Validators.Add(new BlockEditorValidator(propertyValidationService, BlockEditorValues, elementTypeCache)); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditorBase.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditorBase.cs index 59fa980d2b..2c194ba2c2 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditorBase.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditorBase.cs @@ -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 CreateBlockEditorDataConverter() => new BlockListEditorDataConverter(_jsonSerializer); protected override IDataValueEditor CreateValueEditor() => - DataValueEditorFactory.Create(Attribute!, CreateBlockEditorDataConverter()); + DataValueEditorFactory.Create(CreateBlockEditorDataConverter()); internal class BlockListEditorPropertyValueEditor : BlockEditorPropertyValueEditor { public BlockListEditorPropertyValueEditor( - DataEditorAttribute attribute, BlockEditorDataConverter blockEditorDataConverter, PropertyEditorCollection propertyEditors, DataValueReferenceFactoryCollection dataValueReferenceFactories, @@ -62,10 +60,10 @@ public abstract class BlockListPropertyEditorBase : DataEditor ILogger 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(blockEditorDataConverter, elementTypeCache, logger); Validators.Add(new BlockEditorValidator(propertyValidationService, BlockEditorValues, elementTypeCache)); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockValuePropertyValueEditorBase.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockValuePropertyValueEditorBase.cs index 82d66a3bd0..8a774a5cfd 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockValuePropertyValueEditorBase.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockValuePropertyValueEditorBase.cs @@ -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 : 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? _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 : DataV IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer, IIOHelper ioHelper, + DataValueReferenceFactoryCollection dataValueReferenceFactoryCollection) + : this(propertyEditors, + dataTypeConfigurationCache, + shortStringHelper, + jsonSerializer, + dataValueReferenceFactoryCollection, + StaticServiceProvider.Instance.GetRequiredService(), + StaticServiceProvider.Instance.GetRequiredService()) + { + } + + 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; } /// @@ -121,7 +140,10 @@ public abstract class BlockValuePropertyValueEditorBase : 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)); } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs index 31cabac3bf..f1281747c2 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs @@ -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 /// /// protected override IDataValueEditor CreateValueEditor() => - DataValueEditorFactory.Create(Attribute!); + DataValueEditorFactory.Create(); protected override IConfigurationEditor CreateConfigurationEditor() => new RichTextConfigurationEditor(_ioHelper); @@ -78,7 +77,6 @@ public class RichTextPropertyEditor : DataEditor internal class RichTextPropertyValueEditor : BlockValuePropertyValueEditorBase { 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 _logger; public RichTextPropertyValueEditor( - DataEditorAttribute attribute, PropertyEditorCollection propertyEditors, IDataTypeConfigurationCache dataTypeReadCache, ILogger 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; diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditorTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditorTests.cs index 0b10332352..72f4502b1a 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditorTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditorTests.cs @@ -28,6 +28,8 @@ public class BlockListPropertyEditorTests : UmbracoIntegrationTest private PropertyEditorCollection PropertyEditorCollection => GetRequiredService(); + private ILanguageService LanguageService => GetRequiredService(); + [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> + { + { + 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] diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueEditorReuseTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueEditorReuseTests.cs index 48d5c3e0c6..4fe4c34b6e 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueEditorReuseTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueEditorReuseTests.cs @@ -39,9 +39,8 @@ public class DataValueEditorReuseTests var blockVarianceHandler = new BlockEditorVarianceHandler(Mock.Of()); _dataValueEditorFactoryMock .Setup(m => - m.Create(It.IsAny(), It.IsAny>())) + m.Create(It.IsAny>())) .Returns(() => new BlockListPropertyEditorBase.BlockListEditorPropertyValueEditor( - new DataEditorAttribute("a"), new BlockListEditorDataConverter(Mock.Of()), _propertyEditorCollection, _dataValueReferenceFactories, @@ -51,9 +50,9 @@ public class DataValueEditorReuseTests Mock.Of>(), Mock.Of(), Mock.Of(), - Mock.Of(), Mock.Of(), - blockVarianceHandler)); + blockVarianceHandler, + Mock.Of())); } [Test] @@ -99,7 +98,6 @@ public class DataValueEditorReuseTests { var blockListPropertyEditor = new BlockListPropertyEditor( _dataValueEditorFactoryMock.Object, - _propertyEditorCollection, Mock.Of(), Mock.Of(), Mock.Of()); @@ -111,7 +109,7 @@ public class DataValueEditorReuseTests Assert.NotNull(dataValueEditor2); Assert.AreNotSame(dataValueEditor1, dataValueEditor2); _dataValueEditorFactoryMock.Verify( - m => m.Create(It.IsAny(), It.IsAny>()), + m => m.Create(It.IsAny>()), Times.Exactly(2)); } @@ -120,7 +118,6 @@ public class DataValueEditorReuseTests { var blockListPropertyEditor = new BlockListPropertyEditor( _dataValueEditorFactoryMock.Object, - _propertyEditorCollection, Mock.Of(), Mock.Of(), Mock.Of()); @@ -134,7 +131,7 @@ public class DataValueEditorReuseTests Assert.AreEqual("config", ((DataValueEditor)dataValueEditor2).ConfigurationObject); Assert.AreNotSame(dataValueEditor1, dataValueEditor2); _dataValueEditorFactoryMock.Verify( - m => m.Create(It.IsAny(), It.IsAny>()), + m => m.Create(It.IsAny>()), Times.Exactly(2)); } }