diff --git a/src/Umbraco.Core/EnumerableExtensions.cs b/src/Umbraco.Core/EnumerableExtensions.cs
index 58d4d453b7..83eb080049 100644
--- a/src/Umbraco.Core/EnumerableExtensions.cs
+++ b/src/Umbraco.Core/EnumerableExtensions.cs
@@ -301,5 +301,21 @@ namespace Umbraco.Core
&& list1Groups.All(g => g.Count() == list2Groups[g.Key].Count());
}
+ ///
+ /// Returns the items of the given enumerable as a pure enumerable.
+ ///
+ /// When quering lists using methods such as , the result, despite appearing to look like and quack like an
+ /// the type is actually an instance of
+ ///
+ ///
+ ///The item to find.
+ ///The index of the first matching item, or -1 if the item was not found.
+ internal static IEnumerable Yield(this IEnumerable source)
+ {
+ foreach (var element in source)
+ {
+ yield return element;
+ }
+ }
}
}
diff --git a/src/Umbraco.Web/Models/RelatedLink.cs b/src/Umbraco.Web/Models/RelatedLink.cs
new file mode 100644
index 0000000000..145ca6ea7d
--- /dev/null
+++ b/src/Umbraco.Web/Models/RelatedLink.cs
@@ -0,0 +1,184 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Umbraco
+//
+//
+// Defines the RelatedLink type.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+using System;
+using Newtonsoft.Json.Linq;
+
+namespace Umbraco.Web.Models
+{
+ ///
+ /// The related link model
+ ///
+ public class RelatedLink
+ {
+ // ReSharper disable InconsistentNaming
+
+ ///
+ /// The _link item.
+ ///
+ private readonly JToken _linkItem;
+
+ ///
+ /// The _caption.
+ ///
+ private string _caption;
+
+ ///
+ /// The _new window.
+ ///
+ private bool? _newWindow;
+
+ ///
+ /// The _is internal.
+ ///
+ private bool? _isInternal;
+
+ ///
+ /// The _link.
+ ///
+ private string _link;
+
+ ///
+ /// The _linkDeleted.
+ ///
+ private bool? _linkDeleted;
+
+ ///
+ /// The _type.
+ ///
+ private RelatedLinkType _type;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The link item.
+ ///
+ public RelatedLink(JToken linkItem)
+ {
+ this._linkItem = linkItem;
+
+ // get the current Link to set the _linkDeleted is a internal link
+ var currentLink = this.Link;
+ }
+
+ ///
+ /// Gets the caption.
+ ///
+ public string Caption
+ {
+ get
+ {
+ if (string.IsNullOrEmpty(this._caption))
+ {
+ this._caption = this._linkItem.Value("caption");
+ }
+
+ return this._caption;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether new window.
+ ///
+ public bool NewWindow
+ {
+ get
+ {
+ if (this._newWindow == null)
+ {
+ this._newWindow = this._linkItem.Value("newWindow");
+ }
+
+ return this._newWindow.GetValueOrDefault();
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the link is internal.
+ ///
+ public bool IsInternal
+ {
+ get
+ {
+ if (this._isInternal == null)
+ {
+ this._isInternal = this._linkItem.Value("isInternal");
+ }
+
+ return this._isInternal.GetValueOrDefault();
+ }
+ }
+
+ ///
+ /// Gets the type.
+ ///
+ public RelatedLinkType? Type
+ {
+ get
+ {
+ if (Enum.TryParse(this._linkItem.Value("type"), true, out this._type))
+ {
+ return this._type;
+ }
+
+ return null;
+ }
+ }
+
+ ///
+ /// Gets the link.
+ ///
+ public string Link
+ {
+ get
+ {
+ if (string.IsNullOrEmpty(this._link))
+ {
+ if (this.IsInternal)
+ {
+ if (UmbracoContext.Current == null)
+ {
+ return null;
+ }
+
+ this._link = UmbracoContext.Current.UrlProvider.GetUrl(this._linkItem.Value("internal"));
+ if (this._link.Equals("#"))
+ {
+ this._linkDeleted = true;
+ this._link = this._linkItem.Value("internal");
+ }
+ else
+ {
+ this._linkDeleted = false;
+ }
+ }
+ else
+ {
+ this._link = this._linkItem.Value("link");
+ }
+ }
+
+ return this._link;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether deleted.
+ ///
+ internal bool InternalLinkDeleted
+ {
+ get
+ {
+ var linkDeleted = this._linkDeleted;
+ return linkDeleted != null && (bool)linkDeleted;
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Web/Models/RelatedLinkType.cs b/src/Umbraco.Web/Models/RelatedLinkType.cs
new file mode 100644
index 0000000000..8de88d5520
--- /dev/null
+++ b/src/Umbraco.Web/Models/RelatedLinkType.cs
@@ -0,0 +1,27 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Umbraco
+//
+//
+// Defines the RelatedLinkType type.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace Umbraco.Web.Models
+{
+ ///
+ /// The related link type.
+ ///
+ public enum RelatedLinkType
+ {
+ ///
+ /// Internal link type
+ ///
+ Internal,
+
+ ///
+ /// External link type
+ ///
+ External
+ }
+}
diff --git a/src/Umbraco.Web/Models/RelatedLinks.cs b/src/Umbraco.Web/Models/RelatedLinks.cs
new file mode 100644
index 0000000000..60d2ef03b5
--- /dev/null
+++ b/src/Umbraco.Web/Models/RelatedLinks.cs
@@ -0,0 +1,110 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Umbraco
+//
+//
+// Defines the RelatedLinks type.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Umbraco.Core.Logging;
+
+namespace Umbraco.Web.Models
+{
+ ///
+ /// The related links model
+ ///
+ public class RelatedLinks : IEnumerable
+ {
+ // ReSharper disable InconsistentNaming
+
+ ///
+ /// The _property data.
+ ///
+ private readonly string _propertyData;
+
+ ///
+ /// The _related links.
+ ///
+ private readonly List _relatedLinks = new List();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The property data.
+ ///
+ public RelatedLinks(string propertyData)
+ {
+ this._propertyData = propertyData;
+
+ if (!string.IsNullOrEmpty(propertyData))
+ {
+ var relatedLinks = JsonConvert.DeserializeObject(propertyData);
+
+ foreach (var item in relatedLinks)
+ {
+ var relatedLink = new RelatedLink(item);
+ if (!relatedLink.InternalLinkDeleted)
+ {
+ this._relatedLinks.Add(relatedLink);
+ }
+ else
+ {
+ LogHelper.Warn(
+ string.Format("Related Links value converter skipped a link as the node has been unpublished/deleted (Internal Link NodeId: {0}, Link Caption: \"{1}\")", relatedLink.Link, relatedLink.Caption));
+ }
+ }
+ }
+ }
+
+ ///
+ /// Gets the property data.
+ ///
+ public string PropertyData
+ {
+ get
+ {
+ return this._propertyData;
+ }
+ }
+
+ ///
+ /// The any.
+ ///
+ ///
+ /// The .
+ ///
+ public bool Any()
+ {
+ return Enumerable.Any(this);
+ }
+
+ ///
+ /// The get enumerator.
+ ///
+ ///
+ /// The .
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ return this._relatedLinks.GetEnumerator();
+ }
+
+ ///
+ /// The get enumerator.
+ ///
+ ///
+ /// The .
+ ///
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+ }
+}
diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerPropertyConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerPropertyConverter.cs
new file mode 100644
index 0000000000..643e936c06
--- /dev/null
+++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerPropertyConverter.cs
@@ -0,0 +1,177 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Umbraco
+//
+//
+// The content picker property editor converter.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+
+using Umbraco.Core;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Core.PropertyEditors;
+
+namespace Umbraco.Web.PropertyEditors.ValueConverters
+{
+ ///
+ /// The content picker property value converter.
+ ///
+ public class ContentPickerPropertyConverter : PropertyValueConverterBase, IPropertyValueConverterMeta
+ {
+ ///
+ /// The properties to exclude.
+ ///
+ private static readonly List PropertiesToExclude = new List()
+ {
+ Constants.Conventions.Content.InternalRedirectId.ToLower(CultureInfo.InvariantCulture),
+ Constants.Conventions.Content.Redirect.ToLower(CultureInfo.InvariantCulture)
+ };
+
+ ///
+ /// Checks if this converter can convert the property editor and registers if it can.
+ ///
+ ///
+ /// The published property type.
+ ///
+ ///
+ /// The .
+ ///
+ public override bool IsConverter(PublishedPropertyType propertyType)
+ {
+ return propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.ContentPickerAlias);
+ }
+
+ ///
+ /// Convert the raw string into a nodeId integer
+ ///
+ ///
+ /// The published property type.
+ ///
+ ///
+ /// The value of the property
+ ///
+ ///
+ /// The preview.
+ ///
+ ///
+ /// The .
+ ///
+ public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ var attemptConvertInt = source.TryConvertTo();
+ if (attemptConvertInt.Success)
+ {
+ return attemptConvertInt.Result;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Convert the source nodeId into a IPublishedContent (or DynamicPublishedContent)
+ ///
+ ///
+ /// The published property type.
+ ///
+ ///
+ /// The value of the property
+ ///
+ ///
+ /// The preview.
+ ///
+ ///
+ /// The .
+ ///
+ public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ if (source == null)
+ {
+ return null;
+ }
+
+ if (UmbracoContext.Current != null)
+ {
+ if ((propertyType.PropertyTypeAlias != null && PropertiesToExclude.Contains(propertyType.PropertyTypeAlias.ToLower(CultureInfo.InvariantCulture))) == false)
+ {
+ var content = UmbracoContext.Current.ContentCache.GetById((int)source);
+ return content;
+ }
+ }
+
+ return source;
+ }
+
+ ///
+ /// The convert source to xPath.
+ ///
+ ///
+ /// The property type.
+ ///
+ ///
+ /// The source.
+ ///
+ ///
+ /// The preview.
+ ///
+ ///
+ /// The .
+ ///
+ public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ return source.ToString();
+ }
+
+ ///
+ /// The CLR type that the value converter returns.
+ ///
+ ///
+ /// The property type.
+ ///
+ ///
+ /// The .
+ ///
+ public virtual Type GetPropertyValueType(PublishedPropertyType propertyType)
+ {
+ return typeof(IPublishedContent);
+ }
+
+ ///
+ /// The get property cache level.
+ ///
+ ///
+ /// The property type.
+ ///
+ ///
+ /// The cache value.
+ ///
+ ///
+ /// The .
+ ///
+ public PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType, PropertyCacheValue cacheValue)
+ {
+ PropertyCacheLevel returnLevel;
+ switch (cacheValue)
+ {
+ case PropertyCacheValue.Object:
+ returnLevel = PropertyCacheLevel.ContentCache;
+ break;
+ case PropertyCacheValue.Source:
+ returnLevel = PropertyCacheLevel.Content;
+ break;
+ case PropertyCacheValue.XPath:
+ returnLevel = PropertyCacheLevel.Content;
+ break;
+ default:
+ returnLevel = PropertyCacheLevel.None;
+ break;
+ }
+
+ return returnLevel;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerPropertyConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerPropertyConverter.cs
new file mode 100644
index 0000000000..3bdba5d07b
--- /dev/null
+++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerPropertyConverter.cs
@@ -0,0 +1,145 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Umbraco
+//
+//
+// The legacy media picker value converter
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+using System;
+
+using Umbraco.Core;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Core.PropertyEditors;
+
+namespace Umbraco.Web.PropertyEditors.ValueConverters
+{
+ ///
+ /// The media picker property value converter.
+ ///
+ public class MediaPickerPropertyConverter : PropertyValueConverterBase, IPropertyValueConverterMeta
+ {
+ ///
+ /// Checks if this converter can convert the property editor and registers if it can.
+ ///
+ ///
+ /// The published property type.
+ ///
+ ///
+ /// The .
+ ///
+ public override bool IsConverter(PublishedPropertyType propertyType)
+ {
+ // ** Value converter disabled as not sure if we want to convert the legacy media picker or not **
+ return false;
+
+ //return propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MediaPickerAlias);
+ }
+
+ ///
+ /// Convert the raw string into a nodeId integer
+ ///
+ ///
+ /// The published property type.
+ ///
+ ///
+ /// The value of the property
+ ///
+ ///
+ /// The preview.
+ ///
+ ///
+ /// The .
+ ///
+ public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ var attemptConvertInt = source.TryConvertTo();
+ if (attemptConvertInt.Success)
+ {
+ return attemptConvertInt.Result;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Convert the source nodeId into a IPublishedContent (or DynamicPublishedContent)
+ ///
+ ///
+ /// The published property type.
+ ///
+ ///
+ /// The value of the property
+ ///
+ ///
+ /// The preview.
+ ///
+ ///
+ /// The .
+ ///
+ public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ if (source == null)
+ {
+ return null;
+ }
+
+ if (UmbracoContext.Current != null)
+ {
+ return UmbracoContext.Current.MediaCache.GetById((int)source);
+ }
+
+ return null;
+ }
+
+ ///
+ /// The get property cache level.
+ ///
+ ///
+ /// The property type.
+ ///
+ ///
+ /// The cache value.
+ ///
+ ///
+ /// The .
+ ///
+ public PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType, PropertyCacheValue cacheValue)
+ {
+ PropertyCacheLevel returnLevel;
+ switch (cacheValue)
+ {
+ case PropertyCacheValue.Object:
+ returnLevel = PropertyCacheLevel.ContentCache;
+ break;
+ case PropertyCacheValue.Source:
+ returnLevel = PropertyCacheLevel.Content;
+ break;
+ case PropertyCacheValue.XPath:
+ returnLevel = PropertyCacheLevel.Content;
+ break;
+ default:
+ returnLevel = PropertyCacheLevel.None;
+ break;
+ }
+
+ return returnLevel;
+ }
+
+ ///
+ /// The CLR type that the value converter returns.
+ ///
+ ///
+ /// The property type.
+ ///
+ ///
+ /// The .
+ ///
+ public virtual Type GetPropertyValueType(PublishedPropertyType propertyType)
+ {
+ return typeof(IPublishedContent);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerPropertyConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerPropertyConverter.cs
new file mode 100644
index 0000000000..af72de0cb2
--- /dev/null
+++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerPropertyConverter.cs
@@ -0,0 +1,194 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Umbraco
+//
+//
+// The multi node tree picker property editor value converter.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Umbraco.Core;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Core.PropertyEditors;
+
+namespace Umbraco.Web.PropertyEditors.ValueConverters
+{
+
+ ///
+ /// The multi node tree picker property editor value converter.
+ ///
+ public class MultiNodeTreePickerPropertyConverter : PropertyValueConverterBase, IPropertyValueConverterMeta
+ {
+ ///
+ /// Checks if this converter can convert the property editor and registers if it can.
+ ///
+ ///
+ /// The published property type.
+ ///
+ ///
+ /// The .
+ ///
+ public override bool IsConverter(PublishedPropertyType propertyType)
+ {
+ return propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MultiNodeTreePickerAlias);
+ }
+
+ ///
+ /// Convert the raw string into a nodeId integer array
+ ///
+ ///
+ /// The published property type.
+ ///
+ ///
+ /// The value of the property
+ ///
+ ///
+ /// The preview.
+ ///
+ ///
+ /// The .
+ ///
+ public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ var nodeIds =
+ source.ToString()
+ .Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
+ .Select(int.Parse)
+ .ToArray();
+
+ return nodeIds;
+ }
+
+ ///
+ /// Convert the source nodeId into a IEnumerable of IPublishedContent (or DynamicPublishedContent)
+ ///
+ ///
+ /// The published property type.
+ ///
+ ///
+ /// The value of the property
+ ///
+ ///
+ /// The preview.
+ ///
+ ///
+ /// The .
+ ///
+ public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ if (source == null)
+ {
+ return null;
+ }
+
+ var nodeIds = (int[])source;
+
+ var multiNodeTreePicker = new List();
+
+ if (UmbracoContext.Current != null)
+ {
+ var umbHelper = new UmbracoHelper(UmbracoContext.Current);
+
+ if (nodeIds.Length > 0)
+ {
+ var objectType = UmbracoObjectTypes.Unknown;
+
+ foreach (var nodeId in nodeIds)
+ {
+ var multiNodeTreePickerItem = GetPublishedContent(nodeId, ref objectType, UmbracoObjectTypes.Document, umbHelper.TypedContent)
+ ?? GetPublishedContent(nodeId, ref objectType, UmbracoObjectTypes.Media, umbHelper.TypedMedia)
+ ?? GetPublishedContent(nodeId, ref objectType, UmbracoObjectTypes.Member, umbHelper.TypedMember);
+
+ if (multiNodeTreePickerItem != null)
+ {
+ multiNodeTreePicker.Add(multiNodeTreePickerItem);
+ }
+ }
+ }
+
+ return multiNodeTreePicker.Yield().Where(x => x != null);
+ }
+
+ return null;
+ }
+
+ ///
+ /// The get property cache level.
+ ///
+ ///
+ /// The property type.
+ ///
+ ///
+ /// The cache value.
+ ///
+ ///
+ /// The .
+ ///
+ public PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType, PropertyCacheValue cacheValue)
+ {
+ PropertyCacheLevel returnLevel;
+ switch (cacheValue)
+ {
+ case PropertyCacheValue.Object:
+ returnLevel = PropertyCacheLevel.ContentCache;
+ break;
+ case PropertyCacheValue.Source:
+ returnLevel = PropertyCacheLevel.Content;
+ break;
+ case PropertyCacheValue.XPath:
+ returnLevel = PropertyCacheLevel.Content;
+ break;
+ default:
+ returnLevel = PropertyCacheLevel.None;
+ break;
+ }
+
+ return returnLevel;
+ }
+
+ ///
+ /// The CLR type that the value converter returns.
+ ///
+ ///
+ /// The property type.
+ ///
+ ///
+ /// The .
+ ///
+ public virtual Type GetPropertyValueType(PublishedPropertyType propertyType)
+ {
+ return typeof(IEnumerable);
+ }
+
+ ///
+ /// Attempt to get an IPublishedContent instance based on ID and content type
+ ///
+ /// The content node ID
+ /// The type of content being requested
+ /// The type of content expected/supported by
+ /// A function to fetch content of type
+ /// The requested content, or null if either it does not exist or does not match
+ private IPublishedContent GetPublishedContent(int nodeId, ref UmbracoObjectTypes actualType, UmbracoObjectTypes expectedType, Func contentFetcher)
+ {
+ // is the actual type supported by the content fetcher?
+ if (actualType != UmbracoObjectTypes.Unknown && actualType != expectedType)
+ {
+ // no, return null
+ return null;
+ }
+
+ // attempt to get the content
+ var content = contentFetcher(nodeId);
+ if (content != null)
+ {
+ // if we found the content, assign the expected type to the actual type so we don't have to keep looking for other types of content
+ actualType = expectedType;
+ }
+ return content;
+ }
+ }
+}
diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultipleMediaPickerPropertyConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultipleMediaPickerPropertyConverter.cs
new file mode 100644
index 0000000000..a31e6e2ef4
--- /dev/null
+++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultipleMediaPickerPropertyConverter.cs
@@ -0,0 +1,212 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Umbraco
+//
+//
+// The multiple media picker property editor converter.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Umbraco.Core;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Core.PropertyEditors;
+
+namespace Umbraco.Web.PropertyEditors.ValueConverters
+{
+ ///
+ /// The multiple media picker property value converter.
+ ///
+ public class MultipleMediaPickerPropertyConverter : PropertyValueConverterBase, IPropertyValueConverterMeta
+ {
+ ///
+ /// Checks if this converter can convert the property editor and registers if it can.
+ ///
+ ///
+ /// The property type.
+ ///
+ ///
+ /// The .
+ ///
+ public override bool IsConverter(PublishedPropertyType propertyType)
+ {
+ return propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MultipleMediaPickerAlias);
+ }
+
+ ///
+ /// Convert the raw string into a nodeId integer array or a single integer
+ ///
+ ///
+ /// The published property type.
+ ///
+ ///
+ /// The value of the property
+ ///
+ ///
+ /// The preview.
+ ///
+ ///
+ /// The .
+ ///
+ public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ if (IsMultipleDataType(propertyType.DataTypeId))
+ {
+ var nodeIds =
+ source.ToString()
+ .Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
+ .Select(int.Parse)
+ .ToArray();
+ return nodeIds;
+ }
+
+ var attemptConvertInt = source.TryConvertTo();
+ if (attemptConvertInt.Success)
+ {
+ return attemptConvertInt.Result;
+ }
+ else
+ {
+ var nodeIds =
+ source.ToString()
+ .Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
+ .Select(int.Parse)
+ .ToArray();
+
+ if (nodeIds.Length > 0)
+ {
+ var error =
+ string.Format(
+ "Data type \"{0}\" is not set to allow multiple items but appears to contain multiple items, check the setting and save the data type again",
+ ApplicationContext.Current.Services.DataTypeService.GetDataTypeDefinitionById(
+ propertyType.DataTypeId).Name);
+
+ LogHelper.Warn(error);
+ throw new Exception(error);
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Convert the source nodeId into a IPublishedContent or IEnumerable of IPublishedContent (or DynamicPublishedContent) depending on data type setting
+ ///
+ ///
+ /// The published property type.
+ ///
+ ///
+ /// The value of the property
+ ///
+ ///
+ /// The preview.
+ ///
+ ///
+ /// The .
+ ///
+ public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ if (source == null)
+ {
+ return null;
+ }
+
+ if (UmbracoContext.Current == null)
+ {
+ return null;
+ }
+
+ var umbHelper = new UmbracoHelper(UmbracoContext.Current);
+
+ if (IsMultipleDataType(propertyType.DataTypeId))
+ {
+ var nodeIds = (int[])source;
+ var multiMediaPicker = Enumerable.Empty();
+ if (nodeIds.Length > 0)
+ {
+ multiMediaPicker = umbHelper.TypedMedia(nodeIds).Where(x => x != null);
+ }
+
+ return multiMediaPicker;
+ }
+
+ // single value picker
+ var nodeId = (int)source;
+
+ return umbHelper.TypedMedia(nodeId);
+ }
+
+ ///
+ /// The get property cache level.
+ ///
+ ///
+ /// The property type.
+ ///
+ ///
+ /// The cache value.
+ ///
+ ///
+ /// The .
+ ///
+ public PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType, PropertyCacheValue cacheValue)
+ {
+ PropertyCacheLevel returnLevel;
+ switch (cacheValue)
+ {
+ case PropertyCacheValue.Object:
+ returnLevel = PropertyCacheLevel.ContentCache;
+ break;
+ case PropertyCacheValue.Source:
+ returnLevel = PropertyCacheLevel.Content;
+ break;
+ case PropertyCacheValue.XPath:
+ returnLevel = PropertyCacheLevel.Content;
+ break;
+ default:
+ returnLevel = PropertyCacheLevel.None;
+ break;
+ }
+
+ return returnLevel;
+ }
+
+ ///
+ /// The get property value type.
+ ///
+ ///
+ /// The property type.
+ ///
+ ///
+ /// The .
+ ///
+ public virtual Type GetPropertyValueType(PublishedPropertyType propertyType)
+ {
+ return IsMultipleDataType(propertyType.DataTypeId) ? typeof(IEnumerable) : typeof(IPublishedContent);
+ }
+
+ ///
+ /// The is multiple data type.
+ ///
+ ///
+ /// The data type id.
+ ///
+ ///
+ /// The .
+ ///
+ public bool IsMultipleDataType(int dataTypeId)
+ {
+ var dts = ApplicationContext.Current.Services.DataTypeService;
+ var multiPickerPreValue =
+ dts.GetPreValuesCollectionByDataTypeId(dataTypeId)
+ .PreValuesAsDictionary.FirstOrDefault(
+ x => string.Equals(x.Key, "multiPicker", StringComparison.InvariantCultureIgnoreCase)).Value;
+
+ return multiPickerPreValue != null && multiPickerPreValue.Value.TryConvertTo().Result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksEditorValueConvertor.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksEditorValueConvertor.cs
index a0368db769..5df37ebfb9 100644
--- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksEditorValueConvertor.cs
+++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksEditorValueConvertor.cs
@@ -1,106 +1,65 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Xml;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Umbraco
+//
+//
+// Defines the RelatedLinksPropertyConverter type.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
using Umbraco.Core;
-using Umbraco.Core.Logging;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
-using Umbraco.Core.PropertyEditors.ValueConverters;
+using Umbraco.Web.Models;
namespace Umbraco.Web.PropertyEditors.ValueConverters
{
- [PropertyValueType(typeof(JArray))]
- [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)]
- [DefaultPropertyValueConverter(typeof(JsonValueConverter))] //this shadows the JsonValueConverter
- public class RelatedLinksEditorValueConvertor : PropertyValueConverterBase
+ ///
+ /// The related links property value converter.
+ ///
+ [PropertyValueType(typeof(RelatedLinks))]
+ [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.ContentCache)]
+ public class RelatedLinksPropertyConverter : PropertyValueConverterBase
{
+ ///
+ /// Checks if this converter can convert the property editor and registers if it can.
+ ///
+ ///
+ /// The property type.
+ ///
+ ///
+ /// The .
+ ///
public override bool IsConverter(PublishedPropertyType propertyType)
{
- return Constants.PropertyEditors.RelatedLinksAlias.Equals(propertyType.PropertyEditorAlias);
+ return propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.RelatedLinksAlias);
}
- public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
+ ///
+ /// Convert the source nodeId into a RelatedLinks object
+ ///
+ ///
+ /// The published property type.
+ ///
+ ///
+ /// The value of the property
+ ///
+ ///
+ /// The preview.
+ ///
+ ///
+ /// The .
+ ///
+ public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview)
{
- if (source == null) return null;
- var sourceString = source.ToString();
-
- if (sourceString.DetectIsJson())
+ if (source == null)
{
- try
- {
- var obj = JsonConvert.DeserializeObject(sourceString);
- //update the internal links if we have a context
- if (UmbracoContext.Current != null)
- {
- var helper = new UmbracoHelper(UmbracoContext.Current);
- foreach (var a in obj)
- {
- var type = a.Value("type");
- if (type.IsNullOrWhiteSpace() == false)
- {
- if (type == "internal")
- {
- var linkId = a.Value("link");
- var link = helper.NiceUrl(linkId);
- a["link"] = link;
- }
- }
- }
- }
- return obj;
- }
- catch (Exception ex)
- {
- LogHelper.Error("Could not parse the string " + sourceString + " to a json object", ex);
- }
+ return null;
}
- //it's not json, just return the string
- return sourceString;
- }
-
- public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview)
- {
- if (source == null) return null;
var sourceString = source.ToString();
- if (sourceString.DetectIsJson())
- {
- try
- {
- var obj = JsonConvert.DeserializeObject(sourceString);
-
- var d = new XmlDocument();
- var e = d.CreateElement("links");
- d.AppendChild(e);
-
- var values = (IEnumerable)source;
- foreach (dynamic link in obj)
- {
- var ee = d.CreateElement("link");
- ee.SetAttribute("title", link.title);
- ee.SetAttribute("link", link.link);
- ee.SetAttribute("type", link.type);
- ee.SetAttribute("newwindow", link.newWindow);
-
- e.AppendChild(ee);
- }
-
- return d.CreateNavigator();
- }
- catch (Exception ex)
- {
- LogHelper.Error("Could not parse the string " + sourceString + " to a json object", ex);
- }
- }
-
- //it's not json, just return the string
- return sourceString;
+ return UmbracoContext.Current != null ? new RelatedLinks(sourceString) : null;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index de4b49bc77..6d44805856 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -342,6 +342,9 @@
+
+
+
@@ -356,8 +359,12 @@
+
+
+
+