Skip to content

Instantly share code, notes, and snippets.

@JimBobSquarePants
Last active March 10, 2016 04:49
Show Gist options
  • Save JimBobSquarePants/9a41a19524ad9a0c210b to your computer and use it in GitHub Desktop.
Save JimBobSquarePants/9a41a19524ad9a0c210b to your computer and use it in GitHub Desktop.
ImageCropperPropertyConverter for Umbraco
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ImageCropperPropertyConverter.cs" company="Deepend">
// Copyright (c) Deepend
// </copyright>
// <summary>
// The image cropper property converter. This allows Ditto to map the image cropper using the built in Umbraco
// methods.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace Deepend.Umbraco.ComponentModel
{
using System;
using System.Globalization;
using System.Linq;
using Newtonsoft.Json;
using global::Umbraco.Core;
using global::Umbraco.Core.Logging;
using global::Umbraco.Core.Models;
using global::Umbraco.Core.Models.PublishedContent;
using global::Umbraco.Core.PropertyEditors;
using global::Umbraco.Core.Services;
using global::Umbraco.Web.Models;
using Newtonsoft.Json.Linq;
/// <summary>
/// The image cropper property converter. This allows Ditto to map the image cropper using the built in Umbraco
/// methods.
/// </summary>
/// <remarks>
/// This converter is based on the ImageCropperValueConverter added in Umbraco 7.4.0.
/// When we update the base library to that version we will remove this one.
/// </remarks>
public class ImageCropperPropertyConverter : PropertyValueConverterBase, IPropertyValueConverterMeta
{
/// <summary>
/// The data type service
/// </summary>
private readonly IDataTypeService dataTypeService;
/// <summary>
/// Initializes a new instance of the <see cref="ImageCropperPropertyConverter"/> class.
/// </summary>
public ImageCropperPropertyConverter()
{
this.dataTypeService = ApplicationContext.Current.Services.DataTypeService;
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageCropperPropertyConverter"/> class.
/// </summary>
/// <param name="dataTypeService">
/// Provides easy access to operations involving <see cref="T:Umbraco.Core.Models.IDataTypeDefinition"/>
/// </param>
public ImageCropperPropertyConverter(IDataTypeService dataTypeService)
{
if (dataTypeService == null)
{
throw new ArgumentNullException(nameof(dataTypeService));
}
this.dataTypeService = dataTypeService;
}
/// <summary>
/// Converts a property Data value to a Source value.
/// </summary>
/// <param name="propertyType">The property type.</param><param name="source">The data value.</param>
/// <param name="preview">A value indicating whether conversion should take place in preview mode.</param>
/// <returns>
/// The result of the conversion.
/// </returns>
/// <remarks>
/// <para>
/// The converter should know how to convert a <c>null</c> raw value, meaning that no
/// value has been assigned to the property. The source value can be <c>null</c>.
/// </para>
/// <para>
/// With the XML cache, raw values come from the XML cache and therefore are strings.
/// </para>
/// <para>
/// With objects caches, raw values would come from the database and therefore be either
/// ints, DateTimes, or strings.
/// </para>
/// <para>
/// The converter should be prepared to handle both situations.
/// </para>
/// <para>
/// When raw values are strings, the converter must handle empty strings, whitespace
/// strings, and xml-whitespace strings appropriately, ie it should know whether to preserve
/// whitespaces.
/// </para>
/// </remarks>
public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
{
if (source == null)
{
return null;
}
string sourceString = source.ToString();
try
{
JObject json = JsonConvert.DeserializeObject<JObject>(
sourceString,
new JsonSerializerSettings
{
Culture = CultureInfo.InvariantCulture,
FloatParseHandling = FloatParseHandling.Decimal
});
MergePreValues(json, this.dataTypeService, propertyType.DataTypeId);
JsonSerializer serializer = new JsonSerializer
{
Culture = CultureInfo.InvariantCulture,
FloatParseHandling = FloatParseHandling.Decimal
};
// Return the strongly typed model
return json.ToObject<ImageCropDataSet>(serializer);
}
catch (Exception ex)
{
LogHelper.Error<ImageCropperPropertyConverter>(
$"Could not parse the string {sourceString} to a ImageCropDataSet object",
ex);
// It's not json, just return the string
return sourceString;
}
}
/// <summary>
/// Gets a value indicating whether the converter supports a property type.
/// </summary>
/// <param name="propertyType">The property type.</param>
/// <returns>
/// A value indicating whether the converter supports a property type.
/// </returns>
public override bool IsConverter(PublishedPropertyType propertyType)
{
return Constants.PropertyEditors.ImageCropperAlias.InvariantEquals(propertyType.PropertyEditorAlias);
}
/// <summary>
/// Gets the property cache level of a specified value.
/// </summary>
/// <param name="propertyType">The property type.</param><param name="cacheValue">The property value.</param>
/// <returns>
/// The property cache level of the specified value.
/// </returns>
public PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType, PropertyCacheValue cacheValue)
{
return PropertyCacheLevel.Content;
}
/// <summary>
/// Gets the type of values returned by the converter.
/// </summary>
/// <param name="propertyType">The property type.</param>
/// <returns>
/// The CLR type of values returned by the converter.
/// </returns>
public Type GetPropertyValueType(PublishedPropertyType propertyType)
{
return typeof(ImageCropDataSet);
}
/// <summary>
/// Merges pre values with the values for this data type.
/// </summary>
/// <param name="currentValue">The current value.</param>
/// <param name="dataTypeService">
/// Provides easy access to operations involving <see cref="T:Umbraco.Core.Models.IDataTypeDefinition"/>
/// </param>
/// <param name="dataTypeId">The data type id.</param>
internal static void MergePreValues(JObject currentValue, IDataTypeService dataTypeService, int dataTypeId)
{
// Need to lookup the pre-values for this data type
// TODO: Change all singleton access to use ctor injection in v8!!!
PreValueCollection prevalues = dataTypeService.GetPreValuesCollectionByDataTypeId(dataTypeId);
if (prevalues != null && prevalues.IsDictionaryBased && prevalues.PreValuesAsDictionary.ContainsKey("crops"))
{
string cropsString = prevalues.PreValuesAsDictionary["crops"].Value;
JArray preValueCrops;
try
{
preValueCrops = JsonConvert.DeserializeObject<JArray>(cropsString);
}
catch (Exception ex)
{
LogHelper.Error<ImageCropperPropertyConverter>($"Could not parse the string {cropsString} to a json object", ex);
return;
}
// Now we need to merge the crop values - the alias + width + height comes from pre-configured pre-values,
// however, each crop can store it's own coordinates
JArray existingCropsArray;
if (currentValue["crops"] != null)
{
existingCropsArray = (JArray)currentValue["crops"];
}
else
{
currentValue["crops"] = existingCropsArray = new JArray();
}
foreach (JToken preValueCrop in preValueCrops.Where(x => x.HasValues))
{
JToken found = existingCropsArray.FirstOrDefault(x =>
{
if (x.HasValues && x["alias"] != null)
{
return x["alias"].Value<string>() == preValueCrop["alias"].Value<string>();
}
return false;
});
if (found != null)
{
found["width"] = preValueCrop["width"];
found["height"] = preValueCrop["height"];
}
else
{
existingCropsArray.Add(preValueCrop);
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment