203 lines
9.1 KiB
C#
203 lines
9.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using Newtonsoft.Json;
|
|
using Umbraco.Core.Logging;
|
|
using Umbraco.Core.Models;
|
|
|
|
namespace Umbraco.Core.PropertyEditors
|
|
{
|
|
/// <summary>
|
|
/// Defines a pre-value editor
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// A pre-value editor is made up of multiple pre-value fields, each field defines a key that the value is stored against.
|
|
/// Each field can have any editor and the value from each field can store any data such as a simple string or a json structure.
|
|
///
|
|
/// The Json serialization attributes are required for manifest property editors to work.
|
|
/// </remarks>
|
|
public class PreValueEditor
|
|
{
|
|
public PreValueEditor()
|
|
{
|
|
var fields = new List<PreValueField>();
|
|
|
|
//the ctor checks if we have PreValueFieldAttributes applied and if so we construct our fields from them
|
|
var props = TypeHelper.CachedDiscoverableProperties(GetType())
|
|
.Where(x => x.Name != "Fields");
|
|
foreach (var p in props)
|
|
{
|
|
var att = p.GetCustomAttributes(typeof (PreValueFieldAttribute), false).OfType<PreValueFieldAttribute>().SingleOrDefault();
|
|
if (att != null)
|
|
{
|
|
if (att.PreValueFieldType != null)
|
|
{
|
|
//try to create it
|
|
try
|
|
{
|
|
var instance = (PreValueField) Activator.CreateInstance(att.PreValueFieldType);
|
|
//overwrite values if they are assigned
|
|
if (!att.Key.IsNullOrWhiteSpace())
|
|
{
|
|
instance.Key = att.Key;
|
|
}
|
|
//if the key is still empty then assign it to be the property name
|
|
if (instance.Key.IsNullOrWhiteSpace())
|
|
{
|
|
instance.Key = p.Name;
|
|
}
|
|
|
|
if (!att.Name.IsNullOrWhiteSpace())
|
|
instance.Name = att.Name;
|
|
if (!att.View.IsNullOrWhiteSpace())
|
|
instance.View = att.View;
|
|
if (!att.Description.IsNullOrWhiteSpace())
|
|
instance.Description = att.Description;
|
|
if (att.HideLabel)
|
|
instance.HideLabel = att.HideLabel;
|
|
|
|
//add the custom field
|
|
fields.Add(instance);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogHelper.WarnWithException<PreValueEditor>("Could not create an instance of " + att.PreValueFieldType, ex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fields.Add(MapAttributeToField(att, p));
|
|
}
|
|
}
|
|
}
|
|
|
|
Fields = fields;
|
|
}
|
|
|
|
private static PreValueField MapAttributeToField(PreValueFieldAttribute att, PropertyInfo prop)
|
|
{
|
|
return new PreValueField
|
|
{
|
|
//set the key to the property name if it is empty
|
|
Key = att.Key.IsNullOrWhiteSpace() ? prop.Name : att.Key,
|
|
Name = att.Name,
|
|
Description = att.Description,
|
|
HideLabel = att.HideLabel,
|
|
View = att.View
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// A collection of pre-value fields to be edited
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// If fields are specified then the master View and Validators will be ignored
|
|
/// </remarks>
|
|
[JsonProperty("fields")]
|
|
public List<PreValueField> Fields { get; private set; }
|
|
|
|
/// <summary>
|
|
/// A method to format the posted values from the editor to the values to be persisted
|
|
/// </summary>
|
|
/// <param name="editorValue"></param>
|
|
/// <param name="currentValue">
|
|
/// The current value that has been persisted to the database for this pre-value editor. This value may be usesful for
|
|
/// how the value then get's deserialized again to be re-persisted. In most cases it will probably not be used.
|
|
/// </param>
|
|
/// <returns></returns>
|
|
/// <remarks>
|
|
/// By default this will just return the Posted editorValue.
|
|
///
|
|
/// This can be overridden if perhaps you have a comma delimited string posted value but want to convert those to individual rows, or to convert
|
|
/// a json structure to multiple rows.
|
|
/// </remarks>
|
|
public virtual IDictionary<string, string> ConvertEditorToDb(IDictionary<string, object> editorValue, PreValueCollection currentValue)
|
|
{
|
|
//convert to a string based value to be saved in the db
|
|
return editorValue.ToDictionary(x => x.Key, x => x.Value == null ? null : x.Value.ToString());
|
|
}
|
|
|
|
/// <summary>
|
|
/// This can be used to re-format the currently saved pre-values that will be passed to the editor,
|
|
/// by default this returns the merged default and persisted pre-values.
|
|
/// </summary>
|
|
/// <param name="defaultPreVals">
|
|
/// The default/static pre-vals for the property editor
|
|
/// </param>
|
|
/// <param name="persistedPreVals">
|
|
/// The persisted pre-vals for the property editor
|
|
/// </param>
|
|
/// <returns></returns>
|
|
/// <remarks>
|
|
/// This is generally not going to be used by anything unless a property editor wants to change the merging
|
|
/// functionality or needs to convert some legacy persisted data, or convert the string values to strongly typed values in json (i.e. booleans)
|
|
/// </remarks>
|
|
public virtual IDictionary<string, object> ConvertDbToEditor(IDictionary<string, object> defaultPreVals, PreValueCollection persistedPreVals)
|
|
{
|
|
if (defaultPreVals == null)
|
|
{
|
|
defaultPreVals = new Dictionary<string, object>();
|
|
}
|
|
|
|
if (persistedPreVals.IsDictionaryBased)
|
|
{
|
|
//we just need to merge the dictionaries now, the persisted will replace default.
|
|
foreach (var item in persistedPreVals.PreValuesAsDictionary)
|
|
{
|
|
//The persisted dictionary contains values of type PreValue which contain the ID and the Value, we don't care
|
|
// about the Id, just the value so ignore the id.
|
|
defaultPreVals[item.Key] = item.Value.Value;
|
|
}
|
|
//now we're going to try to see if any of the values are JSON, if they are we'll convert them to real JSON objects
|
|
// so they can be consumed as real json in angular!
|
|
ConvertItemsToJsonIfDetected(defaultPreVals);
|
|
|
|
return defaultPreVals;
|
|
}
|
|
|
|
//it's an array so need to format it
|
|
var result = new Dictionary<string, object>();
|
|
var asArray = persistedPreVals.PreValuesAsArray.ToArray();
|
|
for (var i = 0; i < asArray.Length; i++)
|
|
{
|
|
//each item is of type PreValue but we don't want the ID, just the value so ignore the ID
|
|
result.Add(i.ToInvariantString(), asArray[i].Value);
|
|
}
|
|
|
|
//now we're going to try to see if any of the values are JSON, if they are we'll convert them to real JSON objects
|
|
// so they can be consumed as real json in angular!
|
|
ConvertItemsToJsonIfDetected(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
private void ConvertItemsToJsonIfDetected(IDictionary<string, object> result)
|
|
{
|
|
//now we're going to try to see if any of the values are JSON, if they are we'll convert them to real JSON objects
|
|
// so they can be consumed as real json in angular!
|
|
|
|
var keys = result.Keys.ToArray();
|
|
for (var i = 0; i < keys.Length; i++)
|
|
{
|
|
if (result[keys[i]] is string)
|
|
{
|
|
var asString = result[keys[i]].ToString();
|
|
if (asString.DetectIsJson())
|
|
{
|
|
try
|
|
{
|
|
var json = JsonConvert.DeserializeObject(asString);
|
|
result[keys[i]] = json;
|
|
}
|
|
catch
|
|
{
|
|
//swallow this exception, we thought it was json but it really isn't so continue returning a string
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
} |