using System; using System.Collections.Generic; using System.Diagnostics; using Newtonsoft.Json; using Umbraco.Core.Composing; using Umbraco.Core.Logging; namespace Umbraco.Core.PropertyEditors { /// /// Represents a data editor. /// /// /// Editors can be deserialized from e.g. manifests, which is. why the class is not abstract, /// the json serialization attributes are required, and the properties have an internal setter. /// [DebuggerDisplay("{" + nameof(DebuggerDisplay) + "(),nq}")] [HideFromTypeFinder] public class DataEditor : IDataEditor { private IDictionary _defaultConfiguration; /// /// Initializes a new instance of the class. /// public DataEditor(ILogger logger, EditorType type = EditorType.PropertyValue) { Logger = logger ?? throw new ArgumentNullException(nameof(logger)); // defaults Type = type; Icon = Constants.Icons.PropertyEditor; Group = "common"; // assign properties based on the attribute, if it is found Attribute = GetType().GetCustomAttribute(false); if (Attribute == null) return; Alias = Attribute.Alias; Type = Attribute.Type; Name = Attribute.Name; Icon = Attribute.Icon; Group = Attribute.Group; IsDeprecated = Attribute.IsDeprecated; } /// /// Gets the editor attribute. /// protected DataEditorAttribute Attribute { get; } /// /// Gets a logger. /// protected ILogger Logger { get; } /// [JsonProperty("alias", Required = Required.Always)] public string Alias { get; internal set; } /// [JsonIgnore] public EditorType Type { get; } /// [JsonProperty("name", Required = Required.Always)] public string Name { get; internal set; } /// [JsonProperty("icon")] public string Icon { get; internal set; } /// [JsonProperty("group")] public string Group { get; internal set; } /// [JsonIgnore] public bool IsDeprecated { get; } /// /// /// If an explicit value editor has been assigned, then this explicit /// instance is returned. Otherwise, a new instance is created by CreateValueEditor. /// The instance created by CreateValueEditor is not cached, i.e. /// a new instance is created each time the property value is retrieved. The /// property editor is a singleton, and the value editor cannot be a singleton /// since it depends on the datatype configuration. /// Technically, it could be cached by datatype but let's keep things /// simple enough for now. /// // TODO: point of that one? shouldn't we always configure? public IDataValueEditor GetValueEditor() => ExplicitValueEditor ?? CreateValueEditor(); /// /// /// If an explicit value editor has been assigned, then this explicit /// instance is returned. Otherwise, a new instance is created by CreateValueEditor, /// and configured with the configuration. /// The instance created by CreateValueEditor is not cached, i.e. /// a new instance is created each time the property value is retrieved. The /// property editor is a singleton, and the value editor cannot be a singleton /// since it depends on the datatype configuration. /// Technically, it could be cached by datatype but let's keep things /// simple enough for now. /// public IDataValueEditor GetValueEditor(object configuration) { // if an explicit value editor has been set (by the manifest parser) // then return it, and ignore the configuration, which is going to be // empty anyways if (ExplicitValueEditor != null) return ExplicitValueEditor; var editor = CreateValueEditor(); ((DataValueEditor) editor).Configuration = configuration; // TODO: casting is bad return editor; } /// /// Gets or sets an explicit value editor. /// /// Used for manifest data editors. [JsonProperty("editor")] public IDataValueEditor ExplicitValueEditor { get; set; } /// /// /// If an explicit configuration editor has been assigned, then this explicit /// instance is returned. Otherwise, a new instance is created by CreateConfigurationEditor. /// The instance created by CreateConfigurationEditor is not cached, i.e. /// a new instance is created each time. The property editor is a singleton, and although the /// configuration editor could technically be a singleton too, we'd rather not keep configuration editor /// cached. /// public IConfigurationEditor GetConfigurationEditor() => ExplicitConfigurationEditor ?? CreateConfigurationEditor(); /// /// Gets or sets an explicit configuration editor. /// /// Used for manifest data editors. [JsonProperty("config")] public IConfigurationEditor ExplicitConfigurationEditor { get; set; } /// [JsonProperty("defaultConfig")] public IDictionary DefaultConfiguration { // for property value editors, get the ConfigurationEditor.DefaultConfiguration // else fallback to a default, empty dictionary get => _defaultConfiguration ?? ((Type & EditorType.PropertyValue) > 0 ? GetConfigurationEditor().DefaultConfiguration : (_defaultConfiguration = new Dictionary())); set => _defaultConfiguration = value; } /// public virtual IPropertyIndexValueFactory PropertyIndexValueFactory => new DefaultPropertyIndexValueFactory(); /// /// Creates a value editor instance. /// /// protected virtual IDataValueEditor CreateValueEditor() { if (Attribute == null) throw new InvalidOperationException("The editor does not specify a view."); return new DataValueEditor(Attribute); } /// /// Creates a configuration editor instance. /// protected virtual IConfigurationEditor CreateConfigurationEditor() { return new ConfigurationEditor(); } /// /// Provides a summary of the PropertyEditor for use with the . /// protected virtual string DebuggerDisplay() { return $"Name: {Name}, Alias: {Alias}"; } } }