Files
Umbraco-CMS/src/Umbraco.Core/PropertyEditors/ConfigurationEditorOfTConfiguration.cs

196 lines
7.8 KiB
C#
Raw Normal View History

2018-01-24 11:44:44 +01:00
using System;
using System.Collections.Generic;
using System.Reflection;
using Newtonsoft.Json;
2018-02-02 19:43:03 +01:00
using Newtonsoft.Json.Linq;
2018-01-24 11:44:44 +01:00
using Umbraco.Core.Composing;
namespace Umbraco.Core.PropertyEditors
{
/// <summary>
/// Represents a data type configuration editor with a typed configuration.
/// </summary>
public abstract class ConfigurationEditor<TConfiguration> : ConfigurationEditor
where TConfiguration : new()
{
/// <summary>
/// Initializes a new instance of the <see cref="ConfigurationEditor{TConfiguration}"/> class.
/// </summary>
protected ConfigurationEditor()
: base(DiscoverFields())
{ }
/// <summary>
/// Discovers fields from configuration properties marked with the field attribute.
/// </summary>
private static List<ConfigurationField> DiscoverFields()
{
var fields = new List<ConfigurationField>();
var properties = TypeHelper.CachedDiscoverableProperties(typeof(TConfiguration));
foreach (var property in properties)
{
var attribute = property.GetCustomAttribute<ConfigurationFieldAttribute>(false);
if (attribute == null) continue;
ConfigurationField field;
// if the field does not have its own type, use the base type
if (attribute.Type == null)
{
field = new ConfigurationField
{
// if the key is empty then use the property name
Key = string.IsNullOrWhiteSpace(attribute.Key) ? property.Name : attribute.Key,
Name = attribute.Name,
PropertyName = property.Name,
PropertyType = property.PropertyType,
2018-01-24 11:44:44 +01:00
Description = attribute.Description,
HideLabel = attribute.HideLabel,
View = attribute.View
};
fields.Add(field);
continue;
}
// if the field has its own type, instanciate it
try
{
field = (ConfigurationField) Activator.CreateInstance(attribute.Type);
}
catch (Exception ex)
{
throw new Exception($"Failed to create an instance of type \"{attribute.Type}\" for property \"{property.Name}\" of configuration \"{typeof(TConfiguration).Name}\" (see inner exception).", ex);
}
// then add it, and overwrite values if they are assigned in the attribute
fields.Add(field);
field.PropertyName = property.Name;
field.PropertyType = property.PropertyType;
2018-01-24 11:44:44 +01:00
if (!string.IsNullOrWhiteSpace(attribute.Key))
field.Key = attribute.Key;
// if the key is still empty then use the property name
if (string.IsNullOrWhiteSpace(field.Key))
field.Key = property.Name;
if (!string.IsNullOrWhiteSpace(attribute.Name))
field.Name = attribute.Name;
if (!string.IsNullOrWhiteSpace(attribute.View))
field.View = attribute.View;
if (!string.IsNullOrWhiteSpace(attribute.Description))
field.Description = attribute.Description;
if (attribute.HideLabelSettable.HasValue)
field.HideLabel = attribute.HideLabel;
}
return fields;
}
2018-01-26 17:55:20 +01:00
/// <inheritdoc />
public override IDictionary<string, object> DefaultConfiguration => ToConfigurationEditor(DefaultConfigurationObject);
/// <inheritdoc />
public override object DefaultConfigurationObject => new TConfiguration();
2018-02-05 17:48:54 +01:00
2018-02-07 13:35:59 +01:00
/// <inheritdoc />
public override bool IsConfiguration(object obj)
=> obj is TConfiguration;
2018-02-05 17:48:54 +01:00
/// <inheritdoc />
public override object FromDatabase(string configuration)
2018-01-24 11:44:44 +01:00
{
2018-01-26 17:55:20 +01:00
try
{
2018-02-02 19:43:03 +01:00
if (string.IsNullOrWhiteSpace(configuration)) return new TConfiguration();
2018-03-05 14:59:23 +01:00
return JsonConvert.DeserializeObject<TConfiguration>(configuration, ConfigurationJsonSettings);
2018-01-26 17:55:20 +01:00
}
catch (Exception e)
{
throw new Exception($"Failed to parse configuration \"{configuration}\" as \"{typeof(TConfiguration).Name}\" (see inner exception).", e);
}
2018-01-24 11:44:44 +01:00
}
/// <inheritdoc />
public sealed override object FromConfigurationEditor(IDictionary<string, object> editorValues, object configuration)
2018-01-24 11:44:44 +01:00
{
2018-02-05 17:48:54 +01:00
return FromConfigurationEditor(editorValues, (TConfiguration) configuration);
2018-01-24 11:44:44 +01:00
}
/// <summary>
/// Converts the configuration posted by the editor.
/// </summary>
2018-02-05 17:48:54 +01:00
/// <param name="editorValues">The configuration object posted by the editor.</param>
2018-01-24 11:44:44 +01:00
/// <param name="configuration">The current configuration object.</param>
public virtual TConfiguration FromConfigurationEditor(IDictionary<string, object> editorValues, TConfiguration configuration)
2018-01-24 11:44:44 +01:00
{
2018-02-02 19:43:03 +01:00
// note - editorValue contains a mix of Clr types (string, int...) and JToken
// turning everything back into a JToken... might not be fastest but is simplest
// for now
var o = new JObject();
foreach (var field in Fields)
{
2018-02-05 17:48:54 +01:00
// field only, JsonPropertyAttribute is ignored here
// only keep fields that have a non-null/empty value
// rest will fall back to default during ToObject()
if (editorValues.TryGetValue(field.Key, out var value) && value != null && (!(value is string stringValue) || !string.IsNullOrWhiteSpace(stringValue)))
{
if (value is JToken jtoken)
{
//if it's a jtoken then set it
o[field.PropertyName] = jtoken;
}
else if (field.PropertyType == typeof(bool) && value is string sBool)
{
//if it's a boolean property type but a string is found, try to do a conversion
var converted = sBool.TryConvertTo<bool>();
if (converted)
o[field.PropertyName] = converted.Result;
}
else
{
//default behavior
o[field.PropertyName] = JToken.FromObject(value);
}
}
2018-02-02 19:43:03 +01:00
}
return o.ToObject<TConfiguration>();
2018-01-24 11:44:44 +01:00
}
/// <inheritdoc />
public sealed override IDictionary<string, object> ToConfigurationEditor(object configuration)
2018-01-24 11:44:44 +01:00
{
2018-02-05 17:48:54 +01:00
return ToConfigurationEditor((TConfiguration) configuration);
2018-01-24 11:44:44 +01:00
}
/// <summary>
/// Converts configuration values to values for the editor.
/// </summary>
/// <param name="configuration">The configuration.</param>
2018-02-05 17:48:54 +01:00
public virtual Dictionary<string, object> ToConfigurationEditor(TConfiguration configuration)
2018-01-24 11:44:44 +01:00
{
2018-02-02 19:43:03 +01:00
string FieldNamer(PropertyInfo property)
{
2018-02-05 17:48:54 +01:00
// try the field
2018-02-02 19:43:03 +01:00
var field = property.GetCustomAttribute<ConfigurationFieldAttribute>();
if (field != null) return field.Key;
2018-02-05 17:48:54 +01:00
// but the property may not be a field just an extra thing
var json = property.GetCustomAttribute<JsonPropertyAttribute>();
return json?.PropertyName ?? property.Name;
2018-02-02 19:43:03 +01:00
}
2018-03-16 09:06:44 +01:00
return ObjectExtensions.ToObjectDictionary(configuration, FieldNamer);
2018-01-24 11:44:44 +01:00
}
}
}