-
Notifications
You must be signed in to change notification settings - Fork 2.6k
/
ConfigurationEditorOfTConfiguration.cs
195 lines (166 loc) · 7.85 KB
/
ConfigurationEditorOfTConfiguration.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
using System;
using System.Collections.Generic;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
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,
Description = attribute.Description,
HideLabel = attribute.HideLabel,
View = attribute.View
};
fields.Add(field);
continue;
}
// if the field has its own type, instantiate 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;
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;
}
/// <inheritdoc />
public override IDictionary<string, object> DefaultConfiguration => ToConfigurationEditor(DefaultConfigurationObject);
/// <inheritdoc />
public override object DefaultConfigurationObject => new TConfiguration();
/// <inheritdoc />
public override bool IsConfiguration(object obj)
=> obj is TConfiguration;
/// <inheritdoc />
public override object FromDatabase(string configuration)
{
try
{
if (string.IsNullOrWhiteSpace(configuration)) return new TConfiguration();
return JsonConvert.DeserializeObject<TConfiguration>(configuration, ConfigurationJsonSettings);
}
catch (Exception e)
{
throw new InvalidOperationException($"Failed to parse configuration \"{configuration}\" as \"{typeof(TConfiguration).Name}\" (see inner exception).", e);
}
}
/// <inheritdoc />
public sealed override object FromConfigurationEditor(IDictionary<string, object> editorValues, object configuration)
{
return FromConfigurationEditor(editorValues, (TConfiguration) configuration);
}
/// <summary>
/// Converts the configuration posted by the editor.
/// </summary>
/// <param name="editorValues">The configuration object posted by the editor.</param>
/// <param name="configuration">The current configuration object.</param>
public virtual TConfiguration FromConfigurationEditor(IDictionary<string, object> editorValues, TConfiguration configuration)
{
// 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)
{
// 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);
}
}
}
return o.ToObject<TConfiguration>();
}
/// <inheritdoc />
public sealed override IDictionary<string, object> ToConfigurationEditor(object configuration)
{
return ToConfigurationEditor((TConfiguration) configuration);
}
/// <summary>
/// Converts configuration values to values for the editor.
/// </summary>
/// <param name="configuration">The configuration.</param>
public virtual Dictionary<string, object> ToConfigurationEditor(TConfiguration configuration)
{
string FieldNamer(PropertyInfo property)
{
// try the field
var field = property.GetCustomAttribute<ConfigurationFieldAttribute>();
if (field != null) return field.Key;
// but the property may not be a field just an extra thing
var json = property.GetCustomAttribute<JsonPropertyAttribute>();
return json?.PropertyName ?? property.Name;
}
return ObjectExtensions.ToObjectDictionary(configuration, FieldNamer);
}
}
}