+
+
+
+ Add between {{model.config.minNumber}} and {{model.config.maxNumber}} items
+
+ You can only have {{model.config.maxNumber}} items selected
+
+
+
+
+
+ Add {{model.config.minNumber - renderModel.length}} item(s)
+
+ You can only have {{model.config.maxNumber}} items selected
+
+
+
+
+
+ Add up to {{model.config.maxNumber}} items
+
+ You can only have {{model.config.maxNumber}} items selected
+
+
+
+
+
+ Add at least {{model.config.minNumber}} item(s)
+
+
+
+
+
+
+
+
+
+ You need to add at least {{model.config.minNumber}} items
+
+
+
+ You can only have {{model.config.maxNumber}} items selected
+
+
+
+
+
diff --git a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs
index 65d366e2ad..fe85263f3b 100644
--- a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs
+++ b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs
@@ -117,6 +117,7 @@ namespace Umbraco.Web.Cache
LegacyMediaPickerPropertyConverter.ClearCaches();
SliderValueConverter.ClearCaches();
MediaPickerPropertyConverter.ClearCaches();
+ MultiUrlPickerPropertyConverter.ClearCaches();
base.Refresh(jsonPayload);
diff --git a/src/Umbraco.Web/Models/ContentEditing/LinkDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/LinkDisplay.cs
new file mode 100644
index 0000000000..857ae2c318
--- /dev/null
+++ b/src/Umbraco.Web/Models/ContentEditing/LinkDisplay.cs
@@ -0,0 +1,36 @@
+using System.Runtime.Serialization;
+using Umbraco.Core;
+
+namespace Umbraco.Web.Models.ContentEditing
+{
+ [DataContract(Name = "link", Namespace = "")]
+ internal class LinkDisplay
+ {
+ [DataMember(Name = "icon")]
+ public string Icon { get; set; }
+
+ [DataMember(Name = "isMedia")]
+ public bool IsMedia { get; set; }
+
+ [DataMember(Name = "name")]
+ public string Name { get; set; }
+
+ [DataMember(Name = "published")]
+ public bool Published { get; set; }
+
+ [DataMember(Name = "queryString")]
+ public string QueryString { get; set; }
+
+ [DataMember(Name = "target")]
+ public string Target { get; set; }
+
+ [DataMember(Name = "trashed")]
+ public bool Trashed { get; set; }
+
+ [DataMember(Name = "udi")]
+ public GuidUdi Udi { get; set; }
+
+ [DataMember(Name = "url")]
+ public string Url { get; set; }
+ }
+}
diff --git a/src/Umbraco.Web/Models/Link.cs b/src/Umbraco.Web/Models/Link.cs
new file mode 100644
index 0000000000..74ad4ad2af
--- /dev/null
+++ b/src/Umbraco.Web/Models/Link.cs
@@ -0,0 +1,13 @@
+using Umbraco.Core;
+
+namespace Umbraco.Web.Models
+{
+ public class Link
+ {
+ public string Name { get; set; }
+ public string Target { get; set; }
+ public LinkType Type { get; set; }
+ public Udi Udi { get; set; }
+ public string Url { get; set; }
+ }
+}
diff --git a/src/Umbraco.Web/Models/LinkType.cs b/src/Umbraco.Web/Models/LinkType.cs
new file mode 100644
index 0000000000..3db3165d7f
--- /dev/null
+++ b/src/Umbraco.Web/Models/LinkType.cs
@@ -0,0 +1,9 @@
+namespace Umbraco.Web.Models
+{
+ public enum LinkType
+ {
+ Content,
+ Media,
+ External
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/PropertyEditors/MultiUrlPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MultiUrlPickerPropertyEditor.cs
new file mode 100644
index 0000000000..84523f05c4
--- /dev/null
+++ b/src/Umbraco.Web/PropertyEditors/MultiUrlPickerPropertyEditor.cs
@@ -0,0 +1,249 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using Newtonsoft.Json;
+using Umbraco.Core;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.Editors;
+using Umbraco.Core.Models.EntityBase;
+using Umbraco.Core.PropertyEditors;
+using Umbraco.Core.Services;
+using Umbraco.Web.Models.ContentEditing;
+using Umbraco.Web.Routing;
+
+namespace Umbraco.Web.PropertyEditors
+{
+ [PropertyEditor(Constants.PropertyEditors.MultiUrlPickerAlias, "Multi Url Picker", PropertyEditorValueTypes.Json, "multiurlpicker", Group = "pickers", Icon = "icon-link", IsParameterEditor = true)]
+ public class MultiUrlPickerPropertyEditor : PropertyEditor
+ {
+ protected override PreValueEditor CreatePreValueEditor()
+ {
+ return new MultiUrlPickerPreValueEditor();
+ }
+
+ protected override PropertyValueEditor CreateValueEditor()
+ {
+ return new MultiUrlPickerPropertyValueEditor(base.CreateValueEditor());
+ }
+
+ private class MultiUrlPickerPreValueEditor : PreValueEditor
+ {
+ public MultiUrlPickerPreValueEditor()
+ {
+ Fields.Add(new PreValueField
+ {
+ Key = "minNumber",
+ View = "number",
+ Name = "Minimum number of items"
+ });
+ Fields.Add(new PreValueField
+ {
+ Key = "maxNumber",
+ View = "number",
+ Name = "Maximum number of items"
+ });
+ }
+ }
+
+ private class MultiUrlPickerPropertyValueEditor : PropertyValueEditorWrapper
+ {
+ public MultiUrlPickerPropertyValueEditor(PropertyValueEditor wrapped) : base(wrapped)
+ {
+ }
+
+ public override object ConvertDbToEditor(Property property, PropertyType propertyType, IDataTypeService dataTypeService)
+ {
+ if (property.Value == null)
+ return Enumerable.Empty