using System; using System.Linq; using Newtonsoft.Json.Linq; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Editors; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; namespace Umbraco.Web.PropertyEditors { [ValueEditor(Constants.PropertyEditors.Aliases.MultipleTextstring, "Repeatable textstrings", "multipletextbox", ValueType = ValueTypes.Text, Icon="icon-ordered-list", Group="lists")] public class MultipleTextStringPropertyEditor : PropertyEditor { /// /// The constructor will setup the property editor based on the attribute if one is found /// public MultipleTextStringPropertyEditor(ILogger logger) : base(logger) { } protected override IPropertyValueEditor CreateValueEditor() => new MultipleTextStringPropertyValueEditor(Attribute); protected override ConfigurationEditor CreateConfigurationEditor() => new MultipleTextStringConfigurationEditor(); /// /// Custom value editor so we can format the value for the editor and the database /// internal class MultipleTextStringPropertyValueEditor : ValueEditor { public MultipleTextStringPropertyValueEditor(ValueEditorAttribute attribute) : base(attribute) { } /// /// The value passed in from the editor will be an array of simple objects so we'll need to parse them to get the string /// /// /// /// /// /// We will also check the pre-values here, if there are more items than what is allowed we'll just trim the end /// public override object ConvertEditorToDb(ContentPropertyData editorValue, object currentValue) { var asArray = editorValue.Value as JArray; if (asArray == null) { return null; } if (!(editorValue.DataTypeConfiguration is MultipleTestStringConfiguration config)) throw new Exception("panic"); var max = config.Maximum; //The legacy property editor saved this data as new line delimited! strange but we have to maintain that. var array = asArray.OfType() .Where(x => x["value"] != null) .Select(x => x["value"].Value()); //only allow the max if over 0 if (max > 0) { return string.Join(Environment.NewLine, array.Take(max)); } return string.Join(Environment.NewLine, array); } /// /// We are actually passing back an array of simple objects instead of an array of strings because in angular a primitive (string) value /// cannot have 2 way binding, so to get around that each item in the array needs to be an object with a string. /// /// /// /// /// /// /// The legacy property editor saved this data as new line delimited! strange but we have to maintain that. /// public override object ConvertDbToEditor(Property property, PropertyType propertyType, IDataTypeService dataTypeService) { return property.GetValue() == null ? new JObject[] {} : property.GetValue().ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries) .Select(x => JObject.FromObject(new {value = x})); } } } }