From 636d0c9ccd46f55c629b5a828264915c8bd389d8 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 7 Feb 2020 12:18:27 +1100 Subject: [PATCH] Ensures that we track relations for values on all variant property values --- .../DataValueReferenceFactoryCollection.cs | 51 ++-- ...ataValueReferenceFactoryCollectionTests.cs | 223 ++++++++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + 3 files changed, 255 insertions(+), 20 deletions(-) create mode 100644 src/Umbraco.Tests/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs diff --git a/src/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollection.cs b/src/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollection.cs index 386ab6a8f3..e7b051e17f 100644 --- a/src/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollection.cs +++ b/src/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollection.cs @@ -13,35 +13,46 @@ namespace Umbraco.Core.PropertyEditors public IEnumerable GetAllReferences(PropertyCollection properties, PropertyEditorCollection propertyEditors) { - var trackedRelations = new List(); + var trackedRelations = new HashSet(); foreach (var p in properties) { if (!propertyEditors.TryGet(p.PropertyType.PropertyEditorAlias, out var editor)) continue; - //TODO: Support variants/segments! This is not required for this initial prototype which is why there is a check here - if (!p.PropertyType.VariesByNothing()) continue; - var val = p.GetValue(); // get the invariant value + //TODO: We will need to change this once we support tracking via variants/segments + // for now, we are tracking values from ALL variants - var valueEditor = editor.GetValueEditor(); - if (valueEditor is IDataValueReference reference) + foreach(var propertyVal in p.Values) { - var refs = reference.GetReferences(val); - trackedRelations.AddRange(refs); - } + var val = propertyVal.EditedValue; - // Loop over collection that may be add to existing property editors - // implementation of GetReferences in IDataValueReference. - // Allows developers to add support for references by a - // package /property editor that did not implement IDataValueReference themselves - foreach (var item in this) - { - // Check if this value reference is for this datatype/editor - // Then call it's GetReferences method - to see if the value stored - // in the dataeditor/property has referecnes to media/content items - if (item.IsForEditor(editor)) - trackedRelations.AddRange(item.GetDataValueReference().GetReferences(val)); + var valueEditor = editor.GetValueEditor(); + if (valueEditor is IDataValueReference reference) + { + var refs = reference.GetReferences(val); + foreach(var r in refs) + trackedRelations.Add(r); + } + + // Loop over collection that may be add to existing property editors + // implementation of GetReferences in IDataValueReference. + // Allows developers to add support for references by a + // package /property editor that did not implement IDataValueReference themselves + foreach (var item in this) + { + // Check if this value reference is for this datatype/editor + // Then call it's GetReferences method - to see if the value stored + // in the dataeditor/property has referecnes to media/content items + if (item.IsForEditor(editor)) + { + foreach(var r in item.GetDataValueReference().GetReferences(val)) + trackedRelations.Add(r); + } + + } } + + } return trackedRelations; diff --git a/src/Umbraco.Tests/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs b/src/Umbraco.Tests/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs new file mode 100644 index 0000000000..ae6f80b32d --- /dev/null +++ b/src/Umbraco.Tests/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs @@ -0,0 +1,223 @@ +using Moq; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Editors; +using Umbraco.Core.PropertyEditors; +using Umbraco.Web.PropertyEditors; +using static Umbraco.Core.Models.Property; + +namespace Umbraco.Tests.PropertyEditors +{ + [TestFixture] + public class DataValueReferenceFactoryCollectionTests + { + [Test] + public void GetAllReferences_All_Variants_With_IDataValueReferenceFactory() + { + var collection = new DataValueReferenceFactoryCollection(new TestDataValueReferenceFactory().Yield()); + + // label does not implement IDataValueReference + var labelEditor = new LabelPropertyEditor(Mock.Of()); + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(labelEditor.Yield())); + var trackedUdi1 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi2 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi3 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi4 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var property = new Property(new PropertyType(new DataType(labelEditor)) + { + Variations = ContentVariation.CultureAndSegment + }) + { + Values = new List + { + // Ignored (no culture) + new PropertyValue + { + EditedValue = trackedUdi1 + }, + new PropertyValue + { + Culture = "en-US", + EditedValue = trackedUdi2 + }, + new PropertyValue + { + Culture = "en-US", + Segment = "A", + EditedValue = trackedUdi3 + }, + // Ignored (no culture) + new PropertyValue + { + Segment = "A", + EditedValue = trackedUdi4 + }, + // duplicate + new PropertyValue + { + Culture = "en-US", + Segment = "B", + EditedValue = trackedUdi3 + } + } + }; + var properties = new PropertyCollection + { + property + }; + var result = collection.GetAllReferences(properties, propertyEditors); + + Assert.AreEqual(2, result.Count()); + Assert.AreEqual(trackedUdi2, result.ElementAt(0).Udi.ToString()); + Assert.AreEqual(trackedUdi3, result.ElementAt(1).Udi.ToString()); + } + + [Test] + public void GetAllReferences_All_Variants_With_IDataValueReference_Editor() + { + var collection = new DataValueReferenceFactoryCollection(Enumerable.Empty()); + + // mediaPicker does implement IDataValueReference + var mediaPicker = new MediaPickerPropertyEditor(Mock.Of()); + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(mediaPicker.Yield())); + var trackedUdi1 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi2 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi3 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi4 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var property = new Property(new PropertyType(new DataType(mediaPicker)) + { + Variations = ContentVariation.CultureAndSegment + }) + { + Values = new List + { + // Ignored (no culture) + new PropertyValue + { + EditedValue = trackedUdi1 + }, + new PropertyValue + { + Culture = "en-US", + EditedValue = trackedUdi2 + }, + new PropertyValue + { + Culture = "en-US", + Segment = "A", + EditedValue = trackedUdi3 + }, + // Ignored (no culture) + new PropertyValue + { + Segment = "A", + EditedValue = trackedUdi4 + }, + // duplicate + new PropertyValue + { + Culture = "en-US", + Segment = "B", + EditedValue = trackedUdi3 + } + } + }; + var properties = new PropertyCollection + { + property + }; + var result = collection.GetAllReferences(properties, propertyEditors); + + Assert.AreEqual(2, result.Count()); + Assert.AreEqual(trackedUdi2, result.ElementAt(0).Udi.ToString()); + Assert.AreEqual(trackedUdi3, result.ElementAt(1).Udi.ToString()); + } + + [Test] + public void GetAllReferences_Invariant_With_IDataValueReference_Editor() + { + var collection = new DataValueReferenceFactoryCollection(Enumerable.Empty()); + + // mediaPicker does implement IDataValueReference + var mediaPicker = new MediaPickerPropertyEditor(Mock.Of()); + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(mediaPicker.Yield())); + var trackedUdi1 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi2 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi3 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi4 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var property = new Property(new PropertyType(new DataType(mediaPicker)) + { + Variations = ContentVariation.Nothing | ContentVariation.Segment + }) + { + Values = new List + { + new PropertyValue + { + EditedValue = trackedUdi1 + }, + // Ignored (has culture) + new PropertyValue + { + Culture = "en-US", + EditedValue = trackedUdi2 + }, + // Ignored (has culture) + new PropertyValue + { + Culture = "en-US", + Segment = "A", + EditedValue = trackedUdi3 + }, + new PropertyValue + { + Segment = "A", + EditedValue = trackedUdi4 + }, + // duplicate + new PropertyValue + { + Segment = "B", + EditedValue = trackedUdi4 + } + } + }; + var properties = new PropertyCollection + { + property + }; + var result = collection.GetAllReferences(properties, propertyEditors); + + Assert.AreEqual(2, result.Count()); + Assert.AreEqual(trackedUdi1, result.ElementAt(0).Udi.ToString()); + Assert.AreEqual(trackedUdi4, result.ElementAt(1).Udi.ToString()); + } + + private class TestDataValueReferenceFactory : IDataValueReferenceFactory + { + public IDataValueReference GetDataValueReference() => new TestMediaDataValueReference(); + + public bool IsForEditor(IDataEditor dataEditor) => dataEditor.Alias == Constants.PropertyEditors.Aliases.Label; + + private class TestMediaDataValueReference : IDataValueReference + { + public IEnumerable GetReferences(object value) + { + // This is the same as the media picker, it will just try to parse the value directly as a UDI + + var asString = value is string str ? str : value?.ToString(); + + if (string.IsNullOrEmpty(asString)) yield break; + + if (Udi.TryParse(asString, out var udi)) + yield return new UmbracoEntityReference(udi); + } + } + } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index ff923bb04b..e6f273301f 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -145,6 +145,7 @@ +