From 9382f6c32d430a4bafe0bfdbc3098c02f1f00925 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 23 Jun 2021 10:10:28 +0200 Subject: [PATCH] Add the ability to convert Media Picker v2 to v3 progressively (#10517) Co-authored-by: Ronald Barendse --- .../obsoletemediapickernotice.html | 1 - .../MediaPicker3PropertyEditor.cs | 99 ++++++++++++---- .../MediaPickerConfiguration.cs | 3 - .../MediaPickerWithCropsValueConverter.cs | 109 ++++++------------ 4 files changed, 116 insertions(+), 96 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/views/prevalueeditors/obsoletemediapickernotice.html diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/obsoletemediapickernotice.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/obsoletemediapickernotice.html deleted file mode 100644 index 617ffd80d6..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/obsoletemediapickernotice.html +++ /dev/null @@ -1 +0,0 @@ -

Important: switching from "Media Picker (legacy)" to "Media Picker" is not supported and doing so will mean all data (references to previously selected media items) will no longer be available on existing content items.

diff --git a/src/Umbraco.Web/PropertyEditors/MediaPicker3PropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MediaPicker3PropertyEditor.cs index 4ce376f543..43d190e173 100644 --- a/src/Umbraco.Web/PropertyEditors/MediaPicker3PropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MediaPicker3PropertyEditor.cs @@ -1,10 +1,15 @@ -using System.Collections.Generic; -using Newtonsoft.Json; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; using Umbraco.Core; using Umbraco.Core.Logging; +using Umbraco.Core.Models; using Umbraco.Core.Models.Editors; using Umbraco.Core.PropertyEditors; -using Umbraco.Web.PropertyEditors.ValueConverters; +using Umbraco.Core.PropertyEditors.ValueConverters; +using Umbraco.Core.Services; namespace Umbraco.Web.PropertyEditors { @@ -22,43 +27,97 @@ namespace Umbraco.Web.PropertyEditors public class MediaPicker3PropertyEditor : DataEditor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// + /// The logger. public MediaPicker3PropertyEditor(ILogger logger) : base(logger) - { - } + { } /// protected override IConfigurationEditor CreateConfigurationEditor() => new MediaPicker3ConfigurationEditor(); + /// protected override IDataValueEditor CreateValueEditor() => new MediaPicker3PropertyValueEditor(Attribute); internal class MediaPicker3PropertyValueEditor : DataValueEditor, IDataValueReference { - /// - /// Note: no FromEditor() and ToEditor() methods - /// We do not want to transform the way the data is stored in the DB and would like to keep a raw JSON string - /// - public MediaPicker3PropertyValueEditor(DataEditorAttribute attribute) : base(attribute) + public MediaPicker3PropertyValueEditor(DataEditorAttribute attribute) + : base(attribute) + { } + + public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null) { + var value = property.GetValue(culture, segment); + + return Deserialize(value); } public IEnumerable GetReferences(object value) { - var rawJson = value == null ? string.Empty : value is string str ? str : value.ToString(); - - if (rawJson.IsNullOrWhiteSpace()) - yield break; - - var mediaWithCropsDtos = JsonConvert.DeserializeObject(rawJson); - - foreach (var mediaWithCropsDto in mediaWithCropsDtos) + foreach (var dto in Deserialize(value)) { - yield return new UmbracoEntityReference(GuidUdi.Create(Constants.UdiEntityType.Media, mediaWithCropsDto.MediaKey)); + yield return new UmbracoEntityReference(Udi.Create(Constants.UdiEntityType.Media, dto.MediaKey)); } } + internal static IEnumerable Deserialize(object value) + { + var rawJson = value is string str ? str : value?.ToString(); + if (string.IsNullOrWhiteSpace(rawJson)) + { + yield break; + } + + if (!rawJson.DetectIsJson()) + { + // Old comma seperated UDI format + foreach (var udiStr in rawJson.Split(Constants.CharArrays.Comma)) + { + if (GuidUdi.TryParse(udiStr, out var udi)) + { + yield return new MediaWithCropsDto + { + Key = Guid.NewGuid(), + MediaKey = udi.Guid, + Crops = Enumerable.Empty(), + FocalPoint = new ImageCropperValue.ImageCropperFocalPoint + { + Left = 0.5m, + Top = 0.5m + } + }; + } + } + } + else + { + // New JSON format + foreach (var dto in JsonConvert.DeserializeObject>(rawJson)) + { + yield return dto; + } + } + } + + /// + /// Model/DTO that represents the JSON that the MediaPicker3 stores. + /// + [DataContract] + internal class MediaWithCropsDto + { + [DataMember(Name = "key")] + public Guid Key { get; set; } + + [DataMember(Name = "mediaKey")] + public Guid MediaKey { get; set; } + + [DataMember(Name = "crops")] + public IEnumerable Crops { get; set; } + + [DataMember(Name = "focalPoint")] + public ImageCropperValue.ImageCropperFocalPoint FocalPoint { get; set; } + } } } } diff --git a/src/Umbraco.Web/PropertyEditors/MediaPickerConfiguration.cs b/src/Umbraco.Web/PropertyEditors/MediaPickerConfiguration.cs index 7266be9c26..b8b9476184 100644 --- a/src/Umbraco.Web/PropertyEditors/MediaPickerConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/MediaPickerConfiguration.cs @@ -8,9 +8,6 @@ namespace Umbraco.Web.PropertyEditors /// public class MediaPickerConfiguration : IIgnoreUserStartNodesConfig { - [ConfigurationField("notice", "You can NOT change the property editor", "obsoletemediapickernotice")] - public bool Notice { get; set; } - [ConfigurationField("multiPicker", "Pick multiple items", "boolean")] public bool Multiple { get; set; } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs index f9b2ad75e1..f2f055d698 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs @@ -1,8 +1,6 @@ -using Newtonsoft.Json; -using System; -using System.Collections; +using System; using System.Collections.Generic; -using System.Runtime.Serialization; +using System.Linq; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; @@ -14,7 +12,6 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class MediaPickerWithCropsValueConverter : PropertyValueConverterBase { - private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; public MediaPickerWithCropsValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor) @@ -22,98 +19,66 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters _publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); } - public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot; - - /// - /// Enusre this property value convertor is for the New Media Picker with Crops aka MediaPicker 3 - /// public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.Equals(Core.Constants.PropertyEditors.Aliases.MediaPicker3); - /// - /// Check if the raw JSON value is not an empty array - /// - public override bool? IsValue(object value, PropertyValueLevel level) => value?.ToString() != "[]"; - - /// - /// What C# model type does the raw JSON return for Models & Views - /// - public override Type GetPropertyValueType(IPublishedPropertyType propertyType) + public override bool? IsValue(object value, PropertyValueLevel level) { - // Check do we want to return IPublishedContent collection still or a NEW model ? - var isMultiple = IsMultipleDataType(propertyType.DataType); - return isMultiple - ? typeof(IEnumerable) - : typeof(MediaWithCrops); + var isValue = base.IsValue(value, level); + if (isValue != false && level == PropertyValueLevel.Source) + { + // Empty JSON array is not a value + isValue = value?.ToString() != "[]"; + } + + return isValue; } - public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) => source?.ToString(); + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) + => IsMultipleDataType(propertyType.DataType) + ? typeof(IEnumerable) + : typeof(MediaWithCrops); + + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot; public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { - var mediaItems = new List(); var isMultiple = IsMultipleDataType(propertyType.DataType); - if (inter == null) + if (string.IsNullOrEmpty(inter?.ToString())) { - return isMultiple ? mediaItems: null; + // Short-circuit on empty value + return isMultiple ? Enumerable.Empty() : null; } - var dtos = JsonConvert.DeserializeObject>(inter.ToString()); + var mediaItems = new List(); + var dtos = MediaPicker3PropertyEditor.MediaPicker3PropertyValueEditor.Deserialize(inter); - foreach(var media in dtos) + foreach (var dto in dtos) { - var item = _publishedSnapshotAccessor.PublishedSnapshot.Media.GetById(media.MediaKey); - if (item != null) + var mediaItem = _publishedSnapshotAccessor.PublishedSnapshot.Media.GetById(preview, dto.MediaKey); + if (mediaItem != null) { mediaItems.Add(new MediaWithCrops { - MediaItem = item, + MediaItem = mediaItem, LocalCrops = new ImageCropperValue { - Crops = media.Crops, - FocalPoint = media.FocalPoint, - Src = item.Url() + Crops = dto.Crops, + FocalPoint = dto.FocalPoint, + Src = mediaItem.Url() } }); + + if (!isMultiple) + { + // Short-circuit on single item + break; + } } } - return isMultiple ? mediaItems : FirstOrDefault(mediaItems); + return isMultiple ? mediaItems : mediaItems.FirstOrDefault(); } - /// - /// Is the media picker configured to pick multiple media items - /// - /// - /// - private bool IsMultipleDataType(PublishedDataType dataType) - { - var config = dataType.ConfigurationAs(); - return config.Multiple; - } - - private object FirstOrDefault(IList mediaItems) - { - return mediaItems.Count == 0 ? null : mediaItems[0]; - } - - - /// - /// Model/DTO that represents the JSON that the MediaPicker3 stores - /// - [DataContract] - internal class MediaWithCropsDto - { - [DataMember(Name = "key")] - public Guid Key { get; set; } - - [DataMember(Name = "mediaKey")] - public Guid MediaKey { get; set; } - - [DataMember(Name = "crops")] - public IEnumerable Crops { get; set; } - - [DataMember(Name = "focalPoint")] - public ImageCropperValue.ImageCropperFocalPoint FocalPoint { get; set; } - } + private bool IsMultipleDataType(PublishedDataType dataType) => dataType.ConfigurationAs().Multiple; } }