diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/template-helper.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/template-helper.spec.js
index 0ccc9ab1dc..08a69b7e80 100644
--- a/src/Umbraco.Web.UI.Client/test/unit/common/services/template-helper.spec.js
+++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/template-helper.spec.js
@@ -58,13 +58,14 @@ describe('service: templateHelper', function () {
it('should return the snippet for a query', function () {
var queryExpression = "queryExpression";
var snippet = "\n@{\n" + "\tvar selection = " + queryExpression + ";\n}\n";
- snippet += "
\n" +
- "\t@foreach(var item in selection){\n" +
- "\t\t- \n" +
- "\t\t\t@item.Name\n" +
- "\t\t
\n" +
- "\t}\n" +
- "
\n\n";
+ snippet += "
\n" +
+ "\t@foreach (var item in selection)\n" +
+ "\t{\n" +
+ "\t\t- \n" +
+ "\t\t\t@item.Name\n" +
+ "\t\t
\n" +
+ "\t}\n" +
+ "
\n\n";
expect(templateHelper.getQuerySnippet(queryExpression)).toBe(snippet);
});
diff --git a/src/Umbraco.Web.UI/umbraco_client/Dialogs/AssignDomain2.js b/src/Umbraco.Web.UI/umbraco_client/Dialogs/AssignDomain2.js
index ddf38733e1..b5921b6576 100644
--- a/src/Umbraco.Web.UI/umbraco_client/Dialogs/AssignDomain2.js
+++ b/src/Umbraco.Web.UI/umbraco_client/Dialogs/AssignDomain2.js
@@ -1,145 +1,145 @@
-Umbraco.Sys.registerNamespace("Umbraco.Dialogs");
-
-(function ($) {
-
- // register AssignDomain dialog
- Umbraco.Dialogs.AssignDomain2 = base2.Base.extend({
-
- _opts: null,
-
- _isRepeated: function (element) {
- var inputs = $('form input.domain');
- var elementName = element.attr('name');
- var repeated = false;
- inputs.each(function() {
- var input = $(this);
- if (input.attr('name') != elementName && input.val() == element.val())
- repeated = true;
- });
- return repeated;
- },
-
- // constructor
- constructor: function (opts) {
- // merge options with default
- this._opts = $.extend({
- invalidDomain: 'Invalid domain.',
- duplicateDomain: 'Domain has already been assigned.'
- }, opts);
- },
-
- // public methods/variables
-
- languages: null,
- language: null,
- domains: null,
-
- addDomain: function () {
- this.domains.push({
- Name: "",
- Lang: ""
- });
- },
-
- init: function () {
- var self = this;
-
- self.domains = ko.observableArray(self._opts.domains);
- self.languages = self._opts.languages;
- self.language = self._opts.language;
- self.removeDomain = function() { self.domains.remove(this); };
-
- ko.applyBindings(self);
-
- $.validator.addMethod("domain", function (value, element, param) {
- // beware! encode('test') == 'test-'
- // read eg https://rt.cpan.org/Public/Bug/Display.html?id=94347
- value = punycode.encode(value);
- // that regex is best-effort and certainly not exact
- var re = /^(http[s]?:\/\/)?([-\w]+(\.[-\w]+)*)(:\d+)?(\/[-\w]*|-)?$/gi;
- var isopt = this.optional(element);
- var retest = re.test(value);
- var ret = isopt || retest;
- return ret;
- }, self._opts.invalidDomain);
-
- function getDuplicateMessage(val, el) {
- var other = $(el).nextAll('input').val();
- var msg = self._opts.duplicateDomain
- if (other != "" && other != "!!!")
- msg = msg + ' (' + other + ')';
- return msg;
- }
-
- $.validator.addMethod("duplicate", function (value, element, param) {
- return $(element).nextAll('input').val() == "" && !self._isRepeated($(element));
- }, getDuplicateMessage);
-
- $.validator.addClassRules({
- domain: { domain: true },
- duplicate: { duplicate: true }
- });
-
- $('form').validate({
- debug: true,
- focusCleanup: true,
- onkeyup: false
- });
-
- $('form input.domain').on('focus', function(event) {
- if (event.type != 'focusin') return;
- $(this).nextAll('input').val("");
- });
-
- // force validation *now*
- $('form').valid();
-
- $('#btnSave').click(function () {
- if (!$('form').valid())
- return false;
-
- var mask = $('#komask');
- var masked = mask.parent();
- mask.height(masked.height());
- mask.width(masked.width());
- mask.show();
-
- var data = { nodeId: self._opts.nodeId, language: self.language ? self.language : 0, domains: self.domains };
- $.post(self._opts.restServiceLocation + 'PostSaveLanguageAndDomains', ko.toJSON(data), function (json) {
- mask.hide();
-
- if (json.Valid) {
- UmbClientMgr.closeModalWindow();
- }
- else {
- var inputs = $('form input.domain');
- inputs.each(function() { $(this).nextAll('input').val(""); });
- for (var i = 0; i < json.Domains.length; i++) {
- var d = json.Domains[i];
- if (d.Duplicate)
- inputs.each(function() {
- var input = $(this);
- if (input.val() == d.Name)
- input.nextAll('input').val(d.Other ? d.Other : "!!!");
- });
- }
- $('form').valid();
- }
- })
- .fail(function (xhr, textStatus, errorThrown) {
- mask.css('opacity', 1).css('color', "#ff0000").html(xhr.responseText);
- });
- return false;
- });
- }
-
- });
-
- // set defaults for jQuery ajax calls
- $.ajaxSetup({
- dataType: 'json',
- cache: false,
- contentType: 'application/json; charset=utf-8'
- });
-
-})(jQuery);
+Umbraco.Sys.registerNamespace("Umbraco.Dialogs");
+
+(function ($) {
+
+ // register AssignDomain dialog
+ Umbraco.Dialogs.AssignDomain2 = base2.Base.extend({
+
+ _opts: null,
+
+ _isRepeated: function (element) {
+ var inputs = $('form input.domain');
+ var elementName = element.attr('name');
+ var repeated = false;
+ inputs.each(function() {
+ var input = $(this);
+ if (input.attr('name') != elementName && input.val() == element.val())
+ repeated = true;
+ });
+ return repeated;
+ },
+
+ // constructor
+ constructor: function (opts) {
+ // merge options with default
+ this._opts = $.extend({
+ invalidDomain: 'Invalid domain.',
+ duplicateDomain: 'Domain has already been assigned.'
+ }, opts);
+ },
+
+ // public methods/variables
+
+ languages: null,
+ language: null,
+ domains: null,
+
+ addDomain: function () {
+ this.domains.push({
+ Name: "",
+ Lang: ""
+ });
+ },
+
+ init: function () {
+ var self = this;
+
+ self.domains = ko.observableArray(self._opts.domains);
+ self.languages = self._opts.languages;
+ self.language = self._opts.language;
+ self.removeDomain = function() { self.domains.remove(this); };
+
+ ko.applyBindings(self);
+
+ $.validator.addMethod("domain", function (value, element, param) {
+ // beware! encode('test') == 'test-'
+ // read eg https://rt.cpan.org/Public/Bug/Display.html?id=94347
+ value = punycode.encode(value);
+ // that regex is best-effort and certainly not exact
+ var re = /^(http[s]?:\/\/)?([-\w]+(\.[-\w]+)*)(:\d+)?(\/[-\w]*|-)?$/gi;
+ var isopt = this.optional(element);
+ var retest = re.test(value);
+ var ret = isopt || retest;
+ return ret;
+ }, self._opts.invalidDomain);
+
+ function getDuplicateMessage(val, el) {
+ var other = $(el).nextAll('input').val();
+ var msg = self._opts.duplicateDomain
+ if (other != "" && other != "!!!")
+ msg = msg + ' (' + other + ')';
+ return msg;
+ }
+
+ $.validator.addMethod("duplicate", function (value, element, param) {
+ return $(element).nextAll('input').val() == "" && !self._isRepeated($(element));
+ }, getDuplicateMessage);
+
+ $.validator.addClassRules({
+ domain: { domain: true },
+ duplicate: { duplicate: true }
+ });
+
+ $('form').validate({
+ debug: true,
+ focusCleanup: true,
+ onkeyup: false
+ });
+
+ $('form input.domain').on('focus', function(event) {
+ if (event.type != 'focusin') return;
+ $(this).nextAll('input').val("");
+ });
+
+ // force validation *now*
+ $('form').valid();
+
+ $('#btnSave').click(function () {
+ if (!$('form').valid())
+ return false;
+
+ var mask = $('#komask');
+ var masked = mask.parent();
+ mask.height(masked.height());
+ mask.width(masked.width());
+ mask.show();
+
+ var data = { nodeId: self._opts.nodeId, language: self.language ? self.language : 0, domains: self.domains };
+ $.post(self._opts.restServiceLocation + 'PostSaveLanguageAndDomains', ko.toJSON(data), function (json) {
+ mask.hide();
+
+ if (json.Valid) {
+ UmbClientMgr.closeModalWindow();
+ }
+ else {
+ var inputs = $('form input.domain');
+ inputs.each(function() { $(this).nextAll('input').val(""); });
+ for (var i = 0; i < json.Domains.length; i++) {
+ var d = json.Domains[i];
+ if (d.Duplicate)
+ inputs.each(function() {
+ var input = $(this);
+ if (input.val() == d.Name)
+ input.nextAll('input').val(d.Other ? d.Other : "!!!");
+ });
+ }
+ $('form').valid();
+ }
+ })
+ .fail(function (xhr, textStatus, errorThrown) {
+ mask.css('opacity', 1).css('color', "#ff0000").html(xhr.responseText);
+ });
+ return false;
+ });
+ }
+
+ });
+
+ // set defaults for jQuery ajax calls
+ $.ajaxSetup({
+ dataType: 'json',
+ cache: false,
+ contentType: 'application/json; charset=utf-8'
+ });
+
+})(jQuery);
diff --git a/src/Umbraco.Web/Mvc/UrlHelperExtensions.cs b/src/Umbraco.Web/Mvc/UrlHelperExtensions.cs
index 3e29d88d5f..6eb568c6af 100644
--- a/src/Umbraco.Web/Mvc/UrlHelperExtensions.cs
+++ b/src/Umbraco.Web/Mvc/UrlHelperExtensions.cs
@@ -1,79 +1,79 @@
-using System;
-using System.Web.Mvc;
-using Umbraco.Core;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.IO;
-using Umbraco.Core.Xml;
-
-namespace Umbraco.Web.Mvc
-{
- ///
- /// Extension methods for UrlHelper
- ///
- public static class UrlHelperExtensions
- {
- ///
- /// Utility method for checking for valid proxy urls or redirect urls to prevent Open Redirect security issues
- ///
- ///
- ///
The url to validate
- ///
The url of the current local domain (to ensure we can validate if the requested url is local without dependency on the request)
- ///
True if it's an allowed url
- public static bool ValidateProxyUrl(this UrlHelper urlHelper, string url, string callerUrl)
- {
- if (Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute) == false)
- {
- return false;
- }
-
- if (url.StartsWith("//"))
- return false;
-
- Uri requestUri;
- if (Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out requestUri))
- {
- if (string.IsNullOrEmpty(callerUrl) == false)
- {
- Uri localUri;
- if (Uri.TryCreate(callerUrl, UriKind.RelativeOrAbsolute, out localUri))
- {
- // check for local urls
-
- //Cannot start with // since that is not a local url
- if (requestUri.OriginalString.StartsWith("//") == false
- //cannot be non-absolute and also contain the char : since that will indicate a protocol
- && (requestUri.IsAbsoluteUri == false && requestUri.OriginalString.Contains(":") == false)
- //needs to be non-absolute or the hosts must match the current request
- && (requestUri.IsAbsoluteUri == false || requestUri.Host == localUri.Host))
- {
- return true;
- }
- }
- else
- {
- return false;
- }
- }
-
- //we cannot continue if the url is not absolute
- if (requestUri.IsAbsoluteUri == false)
- {
- return false;
- }
-
- // check for valid proxy urls
- var feedProxyXml = XmlHelper.OpenAsXmlDocument(IOHelper.MapPath(SystemFiles.FeedProxyConfig));
- if (feedProxyXml != null &&
- feedProxyXml.SelectSingleNode(string.Concat("//allow[@host = '", requestUri.Host, "']")) != null)
- {
- return true;
- }
- }
- else
- {
- return false;
- }
- return false;
- }
- }
-}
+using System;
+using System.Web.Mvc;
+using Umbraco.Core;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.IO;
+using Umbraco.Core.Xml;
+
+namespace Umbraco.Web.Mvc
+{
+ ///
+ /// Extension methods for UrlHelper
+ ///
+ public static class UrlHelperExtensions
+ {
+ ///
+ /// Utility method for checking for valid proxy urls or redirect urls to prevent Open Redirect security issues
+ ///
+ ///
+ ///
The url to validate
+ ///
The url of the current local domain (to ensure we can validate if the requested url is local without dependency on the request)
+ ///
True if it's an allowed url
+ public static bool ValidateProxyUrl(this UrlHelper urlHelper, string url, string callerUrl)
+ {
+ if (Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute) == false)
+ {
+ return false;
+ }
+
+ if (url.StartsWith("//"))
+ return false;
+
+ Uri requestUri;
+ if (Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out requestUri))
+ {
+ if (string.IsNullOrEmpty(callerUrl) == false)
+ {
+ Uri localUri;
+ if (Uri.TryCreate(callerUrl, UriKind.RelativeOrAbsolute, out localUri))
+ {
+ // check for local urls
+
+ //Cannot start with // since that is not a local url
+ if (requestUri.OriginalString.StartsWith("//") == false
+ //cannot be non-absolute and also contain the char : since that will indicate a protocol
+ && (requestUri.IsAbsoluteUri == false && requestUri.OriginalString.Contains(":") == false)
+ //needs to be non-absolute or the hosts must match the current request
+ && (requestUri.IsAbsoluteUri == false || requestUri.Host == localUri.Host))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //we cannot continue if the url is not absolute
+ if (requestUri.IsAbsoluteUri == false)
+ {
+ return false;
+ }
+
+ // check for valid proxy urls
+ var feedProxyXml = XmlHelper.OpenAsXmlDocument(IOHelper.MapPath(SystemFiles.FeedProxyConfig));
+ if (feedProxyXml != null &&
+ feedProxyXml.SelectSingleNode(string.Concat("//allow[@host = '", requestUri.Host, "']")) != null)
+ {
+ return true;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ return false;
+ }
+ }
+}
diff --git a/src/Umbraco.Web/Properties/Settings1.Designer.cs b/src/Umbraco.Web/Properties/Settings.Designer.cs
similarity index 98%
rename from src/Umbraco.Web/Properties/Settings1.Designer.cs
rename to src/Umbraco.Web/Properties/Settings.Designer.cs
index 2f68c9774c..5a5a863f4f 100644
--- a/src/Umbraco.Web/Properties/Settings1.Designer.cs
+++ b/src/Umbraco.Web/Properties/Settings.Designer.cs
@@ -12,7 +12,7 @@ namespace Umbraco.Web.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.6.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -44,7 +44,7 @@ namespace Umbraco.Web.Properties {
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Configuration.DefaultSettingValueAttribute("Somthing")]
+ [global::System.Configuration.DefaultSettingValueAttribute("Something")]
public string test {
get {
return ((string)(this["test"]));
diff --git a/src/Umbraco.Web/Properties/Settings.settings b/src/Umbraco.Web/Properties/Settings.settings
index d6489e3251..757b363da2 100644
--- a/src/Umbraco.Web/Properties/Settings.settings
+++ b/src/Umbraco.Web/Properties/Settings.settings
@@ -9,7 +9,7 @@
http://update.umbraco.org/checkforupgrade.asmx
- Somthing
+ Something
https://our.umbraco.com/umbraco/webservices/api/repository.asmx
diff --git a/src/Umbraco.Web/PropertyEditors/ColorListPreValueEditor.cs b/src/Umbraco.Web/PropertyEditors/ColorListPreValueEditor.cs
deleted file mode 100644
index 60785d283e..0000000000
--- a/src/Umbraco.Web/PropertyEditors/ColorListPreValueEditor.cs
+++ /dev/null
@@ -1,183 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.Linq;
-using System.Text.RegularExpressions;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using Umbraco.Core;
-using Umbraco.Core.Logging;
-using Umbraco.Core.Models;
-using Umbraco.Core.PropertyEditors;
-
-namespace Umbraco.Web.PropertyEditors
-{
- internal class ColorListPreValueEditor : ValueListPreValueEditor
- {
-
- public ColorListPreValueEditor()
- {
- var field = Fields.First();
-
- //use a custom editor too
- field.View = "views/propertyeditors/colorpicker/colorpicker.prevalues.html";
- //change the description
- field.Description = "Add, remove or sort colors.";
- //change the label
- field.Name = "Colors";
- //need to have some custom validation happening here
- field.Validators.Add(new ColorListValidator());
-
- Fields.Insert(0, new PreValueField
- {
- Name = "Include labels?",
- View = "boolean",
- Key = "useLabel",
- Description = "Stores colors as a Json object containing both the color hex string and label, rather than just the hex string."
- });
- }
-
- public override IDictionary ConvertDbToEditor(IDictionary defaultPreVals, PreValueCollection persistedPreVals)
- {
- var dictionary = persistedPreVals.FormatAsDictionary();
- var items = dictionary.Where(x => x.Key != "useLabel")
- .OrderBy(x => x.Value.SortOrder);
-
- var items2 = new Dictionary();
- foreach (var item in items)
- {
- var valueItem = new ColorPickerColor
- {
- Color = item.Value.Value,
- Label = item.Value.Value,
- SortOrder = item.Value.SortOrder
- };
-
- if (item.Value.Value.DetectIsJson())
- {
- try
- {
- var valueObject = JsonConvert.DeserializeObject(item.Value.Value);
- valueItem = new ColorPickerColor
- {
- Color = valueObject.Color,
- Label = valueObject.Label,
- SortOrder = valueObject.SortOrder
- };
- }
- catch
- {
- // let's say parsing Json failed, we'll not do anything,
- // we'll just use the valueItem we built in the first place
- }
- }
-
- items2[item.Value.Id] = new JObject
- {
- { "value", valueItem.Color },
- { "label", valueItem.Label },
- { "sortOrder", valueItem.SortOrder }
- };
- }
-
- var result = new Dictionary { { "items", items2 } };
- var useLabel = dictionary.ContainsKey("useLabel") && dictionary["useLabel"].Value == "1";
- if (useLabel)
- result["useLabel"] = dictionary["useLabel"].Value;
-
- return result;
- }
-
- public override IDictionary ConvertEditorToDb(IDictionary editorValue, PreValueCollection currentValue)
- {
- var val = editorValue["items"] as JArray;
- var result = new Dictionary();
- if (val == null) return result;
-
- try
- {
- var useLabel = false;
- if (editorValue.TryGetValue("useLabel", out var useLabelObj))
- {
- useLabel = useLabelObj is string && (string) useLabelObj == "1";
- result["useLabel"] = new PreValue(useLabel ? "1" : "0");
- }
-
- // get all non-empty values
- var index = 0;
- // items get submitted in the sorted order, so just count them up
- var sortOrder = -1;
- foreach (var preValue in val.OfType()
- .Where(x => x["value"] != null)
- .Select(x =>
- {
- var idString = x["id"] == null ? "0" : x["id"].ToString();
- int.TryParse(idString, out var id);
-
- var color = x["value"].ToString();
- if (string.IsNullOrWhiteSpace(color)) return null;
-
- var label = x["label"].ToString();
-
- sortOrder++;
- var value = useLabel
- ? JsonConvert.SerializeObject(new { value = color, label = label, sortOrder = sortOrder })
- : color;
-
- return new PreValue(id, value, sortOrder);
- })
- .WhereNotNull())
- {
- result.Add(index.ToInvariantString(), preValue);
- index++;
- }
- }
- catch (Exception ex)
- {
- LogHelper.Error("Could not deserialize the posted value: " + val, ex);
- }
-
- return result;
- }
-
- internal class ColorListValidator : IPropertyValidator
- {
- public IEnumerable Validate(object value, PreValueCollection preValues, PropertyEditor editor)
- {
- var json = value as JArray;
- if (json == null) yield break;
-
- //validate each item which is a json object
- for (var index = 0; index < json.Count; index++)
- {
- var i = json[index];
- var jItem = i as JObject;
- if (jItem == null || jItem["value"] == null) continue;
-
- //NOTE: we will be removing empty values when persisting so no need to validate
- var asString = jItem["value"].ToString();
- if (asString.IsNullOrWhiteSpace()) continue;
-
- if (Regex.IsMatch(asString, "^([0-9a-f]{3}|[0-9a-f]{6})$", RegexOptions.IgnoreCase) == false)
- {
- yield return new ValidationResult("The value " + asString + " is not a valid hex color", new[]
- {
- //we'll make the server field the index number of the value so it can be wired up to the view
- "item_" + index.ToInvariantString()
- });
- }
- }
- }
- }
- }
-
- internal class ColorPickerColor
- {
- [JsonProperty("value")]
- public string Color { get; set; }
- [JsonProperty("label")]
- public string Label { get; set; }
- [JsonProperty("sortOrder")]
- public int SortOrder { get; set; }
- }
-}
diff --git a/src/Umbraco.Web/PropertyEditors/ColorPickerConfigurationEditor.cs b/src/Umbraco.Web/PropertyEditors/ColorPickerConfigurationEditor.cs
index ddc4b50858..cbd4e69a9e 100644
--- a/src/Umbraco.Web/PropertyEditors/ColorPickerConfigurationEditor.cs
+++ b/src/Umbraco.Web/PropertyEditors/ColorPickerConfigurationEditor.cs
@@ -17,39 +17,85 @@ namespace Umbraco.Web.PropertyEditors
// customize the items field
items.View = "views/propertyeditors/colorpicker/colorpicker.prevalues.html";
- items.Description = "Add and remove colors";
- items.Name = "Add color";
+ items.Description = "Add, remove or sort colors";
+ items.Name = "Colors";
items.Validators.Add(new ColorListValidator());
}
public override Dictionary ToConfigurationEditor(ColorPickerConfiguration configuration)
{
- var items = configuration?.Items.ToDictionary(x => x.Id.ToString(), x => GetItemValue(x, configuration.UseLabel)) ?? new object();
+ var configuredItems = configuration?.Items; // ordered
+ object editorItems;
+
+ if (configuredItems == null)
+ {
+ editorItems = new object();
+ }
+ else
+ {
+ var d = new Dictionary();
+ editorItems = d;
+ var sortOrder = 0;
+ foreach (var item in configuredItems)
+ d[item.Id.ToString()] = GetItemValue(item, configuration.UseLabel, sortOrder++);
+ }
+
var useLabel = configuration?.UseLabel ?? false;
return new Dictionary
{
- { "items", items },
+ { "items", editorItems },
{ "useLabel", useLabel }
};
}
- private object GetItemValue(ValueListConfiguration.ValueListItem item, bool useLabel)
+ private object GetItemValue(ValueListConfiguration.ValueListItem item, bool useLabel, int sortOrder)
{
- if (useLabel)
+ // in: ValueListItem, Id = , Value = | { "value": "", "label": "