Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the ability to convert Media Picker v2 to v3 progressively #10517

Merged
merged 6 commits into from
Jun 23, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

99 changes: 82 additions & 17 deletions src/Umbraco.Web/PropertyEditors/MediaPicker3PropertyEditor.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -22,43 +27,103 @@ namespace Umbraco.Web.PropertyEditors
public class MediaPicker3PropertyEditor : DataEditor
{
/// <summary>
/// Initializes a new instance of the <see cref="MediaPicker3PropertyEditor"/> class.
/// Initializes a new instance of the <see cref="MediaPicker3PropertyEditor" /> class.
/// </summary>
/// <param name="logger">The logger.</param>
public MediaPicker3PropertyEditor(ILogger logger)
: base(logger)
{
}
{ }

/// <inheritdoc />
protected override IConfigurationEditor CreateConfigurationEditor() => new MediaPicker3ConfigurationEditor();

/// <inheritdoc />
protected override IDataValueEditor CreateValueEditor() => new MediaPicker3PropertyValueEditor(Attribute);

internal class MediaPicker3PropertyValueEditor : DataValueEditor, IDataValueReference
{
///<remarks>
/// 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
/// </remarks>
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 override object FromEditor(ContentPropertyData editorValue, object currentValue)
{
// TODO: Remove unneeded JSON properties (empty crops/default focal point) and remove indentation
return base.FromEditor(editorValue, currentValue);
}

public IEnumerable<UmbracoEntityReference> GetReferences(object value)
{
var rawJson = value == null ? string.Empty : value is string str ? str : value.ToString();
foreach (var dto in Deserialize(value))
{
yield return new UmbracoEntityReference(Udi.Create(Constants.UdiEntityType.Media, dto.MediaKey));
}
}

if (rawJson.IsNullOrWhiteSpace())
internal static IEnumerable<MediaWithCropsDto> Deserialize(object value)
{
var rawJson = value is string str ? str : value?.ToString();
if (string.IsNullOrWhiteSpace(rawJson))
{
yield break;
}

var mediaWithCropsDtos = JsonConvert.DeserializeObject<MediaPickerWithCropsValueConverter.MediaWithCropsDto[]>(rawJson);

foreach (var mediaWithCropsDto in mediaWithCropsDtos)
if (!rawJson.DetectIsJson())
{
// Old comma seperated UDI format
foreach (var udiStr in rawJson.Split(Constants.CharArrays.Comma))
nul800sebastiaan marked this conversation as resolved.
Show resolved Hide resolved
{
if (GuidUdi.TryParse(udiStr, out var udi))
{
yield return new MediaWithCropsDto
{
Key = Guid.NewGuid(),
MediaKey = udi.Guid,
Crops = Enumerable.Empty<ImageCropperValue.ImageCropperCrop>(),
FocalPoint = new ImageCropperValue.ImageCropperFocalPoint
{
Left = 0.5m,
Top = 0.5m
}
};
}
}
}
else
{
yield return new UmbracoEntityReference(GuidUdi.Create(Constants.UdiEntityType.Media, mediaWithCropsDto.MediaKey));
// New JSON format
foreach (var dto in JsonConvert.DeserializeObject<IEnumerable<MediaWithCropsDto>>(rawJson))
{
yield return dto;
}
}
}

/// <summary>
/// Model/DTO that represents the JSON that the MediaPicker3 stores.
/// </summary>
[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<ImageCropperValue.ImageCropperCrop> Crops { get; set; }

[DataMember(Name = "focalPoint")]
public ImageCropperValue.ImageCropperFocalPoint FocalPoint { get; set; }
}
}
}
}
3 changes: 0 additions & 3 deletions src/Umbraco.Web/PropertyEditors/MediaPickerConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ namespace Umbraco.Web.PropertyEditors
/// </summary>
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; }

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using Newtonsoft.Json;
using System;
using System.Collections;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
Expand All @@ -14,7 +13,6 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
[DefaultPropertyValueConverter]
public class MediaPickerWithCropsValueConverter : PropertyValueConverterBase
{

private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;

public MediaPickerWithCropsValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor)
Expand All @@ -24,96 +22,58 @@ public MediaPickerWithCropsValueConverter(IPublishedSnapshotAccessor publishedSn

public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot;

/// <summary>
/// Enusre this property value convertor is for the New Media Picker with Crops aka MediaPicker 3
/// </summary>
public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.Equals(Core.Constants.PropertyEditors.Aliases.MediaPicker3);

/// <summary>
/// Check if the raw JSON value is not an empty array
/// </summary>
public override bool? IsValue(object value, PropertyValueLevel level) => value?.ToString() != "[]";
public override bool? IsValue(object value, PropertyValueLevel level)
=> value?.ToString() is string stringValue &&
stringValue != null &&
ronaldbarendse marked this conversation as resolved.
Show resolved Hide resolved
!string.IsNullOrEmpty(stringValue) &&
stringValue != "[]";

/// <summary>
/// What C# model type does the raw JSON return for Models & Views
/// </summary>
public override Type GetPropertyValueType(IPublishedPropertyType propertyType)
{
// Check do we want to return IPublishedContent collection still or a NEW model ?
var isMultiple = IsMultipleDataType(propertyType.DataType);
return isMultiple
? typeof(IEnumerable<MediaWithCrops>)
: typeof(MediaWithCrops);
}

public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) => source?.ToString();
=> IsMultipleDataType(propertyType.DataType)
? typeof(IEnumerable<MediaWithCrops>)
: typeof(MediaWithCrops);

public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
{
var mediaItems = new List<MediaWithCrops>();
var isMultiple = IsMultipleDataType(propertyType.DataType);
if (inter == null)
{
return isMultiple ? mediaItems: null;
// Short-circuit on empty value
return isMultiple ? Enumerable.Empty<MediaWithCrops>() : null;
}

var dtos = JsonConvert.DeserializeObject<IEnumerable<MediaWithCropsDto>>(inter.ToString());
var mediaItems = new List<MediaWithCrops>();
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();
}

/// <summary>
/// Is the media picker configured to pick multiple media items
/// </summary>
/// <param name="dataType"></param>
/// <returns></returns>
private bool IsMultipleDataType(PublishedDataType dataType)
{
var config = dataType.ConfigurationAs<MediaPicker3Configuration>();
return config.Multiple;
}

private object FirstOrDefault(IList mediaItems)
{
return mediaItems.Count == 0 ? null : mediaItems[0];
}


/// <summary>
/// Model/DTO that represents the JSON that the MediaPicker3 stores
/// </summary>
[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<ImageCropperValue.ImageCropperCrop> Crops { get; set; }

[DataMember(Name = "focalPoint")]
public ImageCropperValue.ImageCropperFocalPoint FocalPoint { get; set; }
}
private bool IsMultipleDataType(PublishedDataType dataType) => dataType.ConfigurationAs<MediaPicker3Configuration>().Multiple;
}
}