diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs index 05189acc3e..912582f1ef 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs @@ -177,6 +177,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings get { return GetOptionalTextElement("EnableInheritedMediaTypes", true); } } + [ConfigurationProperty("EnablePropertyValueConverters")] + internal InnerTextConfigurationElement EnablePropertyValueConverters + { + get { return GetOptionalTextElement("EnablePropertyValueConverters", false); } + } + string IContentSection.NotificationEmailAddress { get { return Notifications.NotificationEmailAddress; } @@ -326,5 +332,9 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { get { return EnableInheritedMediaTypes; } } + bool IContentSection.EnablePropertyValueConverters + { + get { return EnablePropertyValueConverters; } + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs index a388126948..5dbf9f1a33 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs @@ -69,5 +69,8 @@ namespace Umbraco.Core.Configuration.UmbracoSettings bool EnableInheritedDocumentTypes { get; } bool EnableInheritedMediaTypes { get; } + + bool EnablePropertyValueConverters { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/EnumerableExtensions.cs b/src/Umbraco.Core/EnumerableExtensions.cs index d708dfbf82..177e82bbf1 100644 --- a/src/Umbraco.Core/EnumerableExtensions.cs +++ b/src/Umbraco.Core/EnumerableExtensions.cs @@ -296,5 +296,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.Core/Models/PublishedContent/PublishedContentEnumerable.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentEnumerable.cs new file mode 100644 index 0000000000..7891253188 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentEnumerable.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// The published content enumerable, this model is to allow ToString to be overriden for value converters to support legacy requests for string values + /// + public class PublishedContentEnumerable : IEnumerable + { + /// + /// The items in the collection + /// + private readonly IEnumerable _items; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The published content items + /// + public PublishedContentEnumerable(IEnumerable publishedContent) + { + if (publishedContent == null) throw new ArgumentNullException("publishedContent"); + _items = publishedContent; + } + + /// + /// The ToString method to convert the objects back to CSV + /// + /// + /// The . + /// + public override string ToString() + { + return string.Join(",", _items.Select(x => x.Id)); + } + + /// + /// The get enumerator. + /// + /// + /// The . + /// + public IEnumerator GetEnumerator() + { + return _items.GetEnumerator(); + } + + /// + /// The get enumerator. + /// + /// + /// The . + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs index fef066e0b1..56745ece66 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs @@ -1,11 +1,12 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel; using System.Linq; namespace Umbraco.Core.Models.PublishedContent { - + [TypeConverter(typeof(PublishedContentTypeConverter))] public class PublishedContentExtended : PublishedContentWrapped, IPublishedContentExtended { #region Constructor @@ -186,6 +187,11 @@ namespace Umbraco.Core.Models.PublishedContent } #endregion + + public override string ToString() + { + return Id.ToString(); + } } } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeConverter.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeConverter.cs new file mode 100644 index 0000000000..4141a19b35 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeConverter.cs @@ -0,0 +1,39 @@ +using System; +using System.ComponentModel; +using System.Globalization; +using System.Linq; + +namespace Umbraco.Core.Models.PublishedContent +{ + internal class PublishedContentTypeConverter : TypeConverter + { + private static readonly Type[] ConvertableTypes = new[] + { + typeof(int) + }; + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + return ConvertableTypes.Any(x => TypeHelper.IsTypeAssignableFrom(x, destinationType)) + || base.CanConvertFrom(context, destinationType); + } + + public override object ConvertTo( + ITypeDescriptorContext context, + CultureInfo culture, + object value, + Type destinationType) + { + var publishedContent = value as IPublishedContent; + if (publishedContent == null) + return null; + + if (TypeHelper.IsTypeAssignableFrom(destinationType)) + { + return publishedContent.Id; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Range.cs b/src/Umbraco.Core/Models/Range.cs new file mode 100644 index 0000000000..3095d6342d --- /dev/null +++ b/src/Umbraco.Core/Models/Range.cs @@ -0,0 +1,52 @@ +using System; +namespace Umbraco.Core.Models +{ + // The Range class. Adapted from http://stackoverflow.com/questions/5343006/is-there-a-c-sharp-type-for-representing-an-integer-range + /// Generic parameter. + public class Range where T : IComparable + { + /// Minimum value of the range. + public T Minimum { get; set; } + + /// Maximum value of the range. + public T Maximum { get; set; } + + /// Presents the Range in readable format. + /// String representation of the Range + public override string ToString() + { + return string.Format("{0},{1}", this.Minimum, this.Maximum); + } + + /// Determines if the range is valid. + /// True if range is valid, else false + public bool IsValid() + { + return this.Minimum.CompareTo(this.Maximum) <= 0; + } + + /// Determines if the provided value is inside the range. + /// The value to test + /// True if the value is inside Range, else false + public bool ContainsValue(T value) + { + return (this.Minimum.CompareTo(value) <= 0) && (value.CompareTo(this.Maximum) <= 0); + } + + /// Determines if this Range is inside the bounds of another range. + /// The parent range to test on + /// True if range is inclusive, else false + public bool IsInsideRange(Range range) + { + return this.IsValid() && range.IsValid() && range.ContainsValue(this.Minimum) && range.ContainsValue(this.Maximum); + } + + /// Determines if another range is inside the bounds of this range. + /// The child range to test + /// True if range is inside, else false + public bool ContainsRange(Range range) + { + return this.IsValid() && range.IsValid() && this.ContainsValue(range.Minimum) && this.ContainsValue(range.Maximum); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/CheckboxListValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/CheckboxListValueConverter.cs new file mode 100644 index 0000000000..3b97b5e3e6 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/CheckboxListValueConverter.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + [DefaultPropertyValueConverter] + [PropertyValueType(typeof(IEnumerable))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class CheckboxListValueConverter : PropertyValueConverterBase + { + public override bool IsConverter(PublishedPropertyType propertyType) + { + if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters) + { + return propertyType.PropertyEditorAlias.InvariantEquals(Constants.PropertyEditors.CheckBoxListAlias); + } + return false; + } + + public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) + { + var sourceString = (source ?? string.Empty).ToString(); + + if (string.IsNullOrEmpty(sourceString)) + return Enumerable.Empty(); + + var values = + sourceString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(v => v.Trim()); + + return values; + } + } +} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/ColorPickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/ColorPickerValueConverter.cs index bc943b7da0..987640716b 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/ColorPickerValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/ColorPickerValueConverter.cs @@ -1,18 +1,28 @@ -using Umbraco.Core.Models.PublishedContent; +using System; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Core.PropertyEditors.ValueConverters { + [DefaultPropertyValueConverter] + [PropertyValueType(typeof(string))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] public class ColorPickerValueConverter : PropertyValueConverterBase { public override bool IsConverter(PublishedPropertyType propertyType) { - return propertyType.PropertyEditorAlias.InvariantEquals(Constants.PropertyEditors.ColorPickerAlias); + if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters) + { + return propertyType.PropertyEditorAlias.InvariantEquals(Constants.PropertyEditors.ColorPickerAlias); + } + return false; } public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) { // make sure it's a string - return source.ToString(); + return source == null ? string.Empty : source.ToString(); } + } } diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/DatePickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/DatePickerValueConverter.cs index 769f3928a7..ce10dd202e 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/DatePickerValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/DatePickerValueConverter.cs @@ -6,6 +6,7 @@ using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Core.PropertyEditors.ValueConverters { + [DefaultPropertyValueConverter] [PropertyValueType(typeof(DateTime))] [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] public class DatePickerValueConverter : PropertyValueConverterBase diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/DecimalValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/DecimalValueConverter.cs index e32ff7af02..89af76ea12 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/DecimalValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/DecimalValueConverter.cs @@ -3,6 +3,7 @@ using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Core.PropertyEditors.ValueConverters { + [DefaultPropertyValueConverter] [PropertyValueType(typeof(decimal))] [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] public class DecimalValueConverter : PropertyValueConverterBase diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListMultipleValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListMultipleValueConverter.cs new file mode 100644 index 0000000000..80a8a18a5d --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListMultipleValueConverter.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + [DefaultPropertyValueConverter] + [PropertyValueType(typeof(IEnumerable))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class DropdownListMultipleValueConverter : PropertyValueConverterBase + { + public override bool IsConverter(PublishedPropertyType propertyType) + { + if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters) + { + return propertyType.PropertyEditorAlias.InvariantEquals(Constants.PropertyEditors.DropDownListMultipleAlias); + } + return false; + } + + public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) + { + var sourceString = (source ?? "").ToString(); + + if (string.IsNullOrEmpty(sourceString)) + return Enumerable.Empty(); + + var values = + sourceString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(v => v.Trim()); + + return values; + } + + } +} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListMultipleWithKeysValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListMultipleWithKeysValueConverter.cs new file mode 100644 index 0000000000..649d50746f --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListMultipleWithKeysValueConverter.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + + [DefaultPropertyValueConverter] + [PropertyValueType(typeof(IEnumerable))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class DropdownListMultipleWithKeysValueConverter : PropertyValueConverterBase + { + public override bool IsConverter(PublishedPropertyType propertyType) + { + if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters) + { + return propertyType.PropertyEditorAlias.InvariantEquals(Constants.PropertyEditors.DropdownlistMultiplePublishKeysAlias); + } + return false; + } + + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + if (source == null) + return new int[] { }; + + var prevalueIds = source.ToString() + .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(p => p.Trim()) + .Select(int.Parse) + .ToArray(); + + return prevalueIds; + } + + } +} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListValueConverter.cs new file mode 100644 index 0000000000..69b2ff1adb --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListValueConverter.cs @@ -0,0 +1,28 @@ +using System; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + + [DefaultPropertyValueConverter] + [PropertyValueType(typeof(string))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class DropdownListValueConverter : PropertyValueConverterBase + { + public override bool IsConverter(PublishedPropertyType propertyType) + { + if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters) + { + return propertyType.PropertyEditorAlias.InvariantEquals(Constants.PropertyEditors.DropDownListAlias); + } + return false; + } + + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + return source == null ? string.Empty : source.ToString(); + } + + } +} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListWithKeysValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListWithKeysValueConverter.cs new file mode 100644 index 0000000000..de95906469 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListWithKeysValueConverter.cs @@ -0,0 +1,31 @@ +using System; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + [DefaultPropertyValueConverter] + [PropertyValueType(typeof(int))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class DropdownListWithKeysValueConverter : PropertyValueConverterBase + { + public override bool IsConverter(PublishedPropertyType propertyType) + { + if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters) + { + return propertyType.PropertyEditorAlias.InvariantEquals(Constants.PropertyEditors.DropdownlistPublishingKeysAlias); + } + return false; + } + + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + var intAttempt = source.TryConvertTo(); + if (intAttempt.Success) + return intAttempt.Result; + + return null; + } + + } +} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/EmailAddressValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/EmailAddressValueConverter.cs new file mode 100644 index 0000000000..7a2a06cf67 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/EmailAddressValueConverter.cs @@ -0,0 +1,27 @@ +using System; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + [DefaultPropertyValueConverter] + [PropertyValueType(typeof(string))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class EmailAddressValueConverter : PropertyValueConverterBase + { + public override bool IsConverter(PublishedPropertyType propertyType) + { + if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters) + { + return propertyType.PropertyEditorAlias.InvariantEquals(Constants.PropertyEditors.EmailAddressAlias); + } + return false; + } + + public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) + { + return source.ToString(); + } + + } +} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/IntegerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/IntegerValueConverter.cs index c9b0306e32..34b1ebffb1 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/IntegerValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/IntegerValueConverter.cs @@ -2,6 +2,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters { + [DefaultPropertyValueConverter] [PropertyValueType(typeof(int))] [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] public class IntegerValueConverter : PropertyValueConverterBase diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs index aae8da4be7..1d6425133e 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs @@ -4,6 +4,7 @@ using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Core.PropertyEditors.ValueConverters { + [DefaultPropertyValueConverter] [PropertyValueType(typeof(IHtmlString))] [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] public class MarkdownEditorValueConverter : PropertyValueConverterBase diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/MemberGroupPickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MemberGroupPickerValueConverter.cs new file mode 100644 index 0000000000..2d56dcba58 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/MemberGroupPickerValueConverter.cs @@ -0,0 +1,27 @@ +using System; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + [DefaultPropertyValueConverter] + [PropertyValueType(typeof(string))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class MemberGroupPickerValueConverter : PropertyValueConverterBase + { + public override bool IsConverter(PublishedPropertyType propertyType) + { + if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters) + { + return propertyType.PropertyEditorAlias.InvariantEquals(Constants.PropertyEditors.MemberGroupPickerAlias); + } + return false; + } + + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + return source == null ? string.Empty : source.ToString(); + } + + } +} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs index a3b12a6688..fcbdb1cdaf 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs @@ -8,6 +8,7 @@ using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Core.PropertyEditors.ValueConverters { + [DefaultPropertyValueConverter] [PropertyValueType(typeof(IEnumerable))] [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] public class MultipleTextStringValueConverter : PropertyValueConverterBase diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/RadioButtonListValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/RadioButtonListValueConverter.cs new file mode 100644 index 0000000000..9427ccecdb --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/RadioButtonListValueConverter.cs @@ -0,0 +1,31 @@ +using System; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + [DefaultPropertyValueConverter] + [PropertyValueType(typeof(int))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class RadioButtonListValueConverter : PropertyValueConverterBase + { + public override bool IsConverter(PublishedPropertyType propertyType) + { + if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters) + { + return propertyType.PropertyEditorAlias.InvariantEquals(Constants.PropertyEditors.RadioButtonListAlias); + } + return false; + } + + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + var intAttempt = source.TryConvertTo(); + if (intAttempt.Success) + return intAttempt.Result; + + return null; + } + + } +} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/SliderValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/SliderValueConverter.cs new file mode 100644 index 0000000000..f3354eb631 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/SliderValueConverter.cs @@ -0,0 +1,100 @@ +using System; +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Services; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + [DefaultPropertyValueConverter] + public class SliderValueConverter : PropertyValueConverterBase, IPropertyValueConverterMeta + { + private readonly IDataTypeService _dataTypeService; + + //TODO: Remove this ctor in v8 since the other one will use IoC + public SliderValueConverter() + : this(ApplicationContext.Current.Services.DataTypeService) + { + } + + public SliderValueConverter(IDataTypeService dataTypeService) + { + if (dataTypeService == null) throw new ArgumentNullException("dataTypeService"); + _dataTypeService = dataTypeService; + } + + public override bool IsConverter(PublishedPropertyType propertyType) + { + if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters) + { + return propertyType.PropertyEditorAlias.InvariantEquals(Constants.PropertyEditors.SliderAlias); + } + return false; + } + + public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) + { + if (source == null) + { + return null; + } + + if (IsRangeDataType(propertyType.DataTypeId)) + { + var rangeRawValues = source.ToString().Split(','); + var minimumAttempt = rangeRawValues[0].TryConvertTo(); + var maximumAttempt = rangeRawValues[1].TryConvertTo(); + + if (minimumAttempt.Success && maximumAttempt.Success) + { + return new Range() { Maximum = maximumAttempt.Result, Minimum = minimumAttempt.Result }; + } + } + + var valueAttempt = source.ToString().TryConvertTo(); + if (valueAttempt.Success) + { + return valueAttempt.Result; + } + + // Something failed in the conversion of the strings to decimals + return null; + + } + + public Type GetPropertyValueType(PublishedPropertyType propertyType) + { + if (IsRangeDataType(propertyType.DataTypeId)) + { + return typeof(Range); + } + return typeof(decimal); + } + + public PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType, PropertyCacheValue cacheValue) + { + return PropertyCacheLevel.Content; + } + + /// + /// Discovers if the slider is set to range mode. + /// + /// + /// The data type id. + /// + /// + /// The . + /// + private bool IsRangeDataType(int dataTypeId) + { + // ** This must be cached (U4-8862) ** + var enableRange = + _dataTypeService.GetPreValuesCollectionByDataTypeId(dataTypeId) + .PreValuesAsDictionary.FirstOrDefault( + x => string.Equals(x.Key, "enableRange", StringComparison.InvariantCultureIgnoreCase)).Value; + + return enableRange != null && enableRange.Value.TryConvertTo().Result; + } + } +} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/TagsValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/TagsValueConverter.cs new file mode 100644 index 0000000000..a6fadcf8d0 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/TagsValueConverter.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Services; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + [DefaultPropertyValueConverter] + [PropertyValueType(typeof(IEnumerable))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class TagsValueConverter : PropertyValueConverterBase + { + private readonly IDataTypeService _dataTypeService; + + //TODO: Remove this ctor in v8 since the other one will use IoC + public TagsValueConverter() + : this(ApplicationContext.Current.Services.DataTypeService) + { + } + + public TagsValueConverter(IDataTypeService dataTypeService) + { + if (dataTypeService == null) throw new ArgumentNullException("dataTypeService"); + _dataTypeService = dataTypeService; + } + + public override bool IsConverter(PublishedPropertyType propertyType) + { + if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters) + { + return propertyType.PropertyEditorAlias.InvariantEquals(Constants.PropertyEditors.TagsAlias); + } + return false; + } + + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + // if Json storage type deserialzie and return as string array + if (JsonStorageType(propertyType.DataTypeId)) + { + var jArray = JsonConvert.DeserializeObject(source.ToString()); + return jArray.ToObject(); + } + + // Otherwise assume CSV storage type and return as string array + var csvTags = + source.ToString() + .Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) + .ToArray(); + return csvTags; + } + + public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) + { + if (source == null) + { + return null; + } + return (string[]) source; + } + + /// + /// Discovers if the tags data type is storing its data in a Json format + /// + /// + /// The data type id. + /// + /// + /// The . + /// + private bool JsonStorageType(int dataTypeId) + { + // ** This must be cached (U4-8862) ** + var storageType = + _dataTypeService.GetPreValuesCollectionByDataTypeId(dataTypeId) + .PreValuesAsDictionary.FirstOrDefault( + x => string.Equals(x.Key, "storageType", StringComparison.InvariantCultureIgnoreCase)).Value; + + if (storageType.Value.InvariantEquals("Json")) + { + return true; + } + + return false; + } + } +} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/TextStringValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/TextStringValueConverter.cs index b64065c801..bffb073df2 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/TextStringValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/TextStringValueConverter.cs @@ -6,11 +6,12 @@ using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Core.PropertyEditors.ValueConverters { + [DefaultPropertyValueConverter] [PropertyValueType(typeof(string))] [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] public class TextStringValueConverter : PropertyValueConverterBase { - private readonly static string[] PropertyTypeAliases = + private static readonly string[] PropertyTypeAliases = { Constants.PropertyEditors.TextboxAlias, Constants.PropertyEditors.TextboxMultipleAlias diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/TinyMceValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/TinyMceValueConverter.cs index 937808b96b..89fc5fb268 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/TinyMceValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/TinyMceValueConverter.cs @@ -4,10 +4,11 @@ using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Core.PropertyEditors.ValueConverters { - /// - /// Value converter for the RTE so that it always returns IHtmlString so that Html.Raw doesn't have to be used. - /// + /// + /// Value converter for the RTE so that it always returns IHtmlString so that Html.Raw doesn't have to be used. + /// // PropertyCacheLevel.Content is ok here because that version of RTE converter does not parse {locallink} nor executes macros + [DefaultPropertyValueConverter] [PropertyValueType(typeof(IHtmlString))] [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] public class TinyMceValueConverter : PropertyValueConverterBase diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/UploadPropertyConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/UploadPropertyConverter.cs new file mode 100644 index 0000000000..4953cf3d5e --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/UploadPropertyConverter.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + /// + /// The upload property value converter. + /// + [DefaultPropertyValueConverter] + [PropertyValueType(typeof(string))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class UploadPropertyConverter : PropertyValueConverterBase + { + /// + /// 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) + { + if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters) + { + return propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.UploadFieldAlias); + } + return false; + } + + /// + /// Convert the source object to a string + /// + /// + /// The published property type. + /// + /// + /// The value of the property + /// + /// + /// The preview. + /// + /// + /// The . + /// + public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) + { + return (source ?? "").ToString(); + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/YesNoValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/YesNoValueConverter.cs index 1e3b684b08..b9e8639d72 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/YesNoValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/YesNoValueConverter.cs @@ -3,6 +3,7 @@ using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Core.PropertyEditors.ValueConverters { + [DefaultPropertyValueConverter] [PropertyValueType(typeof(bool))] [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] public class YesNoValueConverter : PropertyValueConverterBase diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 4300d56cb1..31b14780b3 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -338,6 +338,7 @@ + @@ -446,9 +447,11 @@ + + @@ -579,10 +582,21 @@ + + + + + + + + + + + diff --git a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs index f9bbc04087..8134924c61 100644 --- a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs @@ -1,8 +1,7 @@ using System; +using System.Collections.Generic; using NUnit.Framework; -using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; -using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.PropertyEditors { @@ -58,5 +57,44 @@ namespace Umbraco.Tests.PropertyEditors Assert.AreEqual(expected, result); } - } + + [TestCase("apples", new[] { "apples" })] + [TestCase("apples,oranges", new[] { "apples", "oranges" })] + [TestCase(" apples, oranges, pears ", new[] { "apples", "oranges", "pears" })] + [TestCase("", new string[] { })] + [TestCase(null, new string[] { })] + public void CanConvertCheckboxListPropertyEditor(object value, IEnumerable expected) + { + var converter = new CheckboxListValueConverter(); + var result = converter.ConvertSourceToObject(null, value, false); + + Assert.AreEqual(expected, result); + } + + [TestCase("apples", new[] {"apples"})] + [TestCase("apples,oranges", new[] {"apples", "oranges"})] + [TestCase("apples , oranges, pears ", new[] {"apples", "oranges", "pears"})] + [TestCase("", new string[] {})] + [TestCase(null, new string[] {})] + public void CanConvertDropdownListMultiplePropertyEditor(object value, IEnumerable expected) + { + var converter = new DropdownListMultipleValueConverter(); + var result = converter.ConvertSourceToObject(null, value, false); + + Assert.AreEqual(expected, result); + } + + [TestCase("100", new[] {100})] + [TestCase("100,200", new[] {100, 200})] + [TestCase("100 , 200, 300 ", new[] {100, 200, 300})] + [TestCase("", new int[] {})] + [TestCase(null, new int[] {})] + public void CanConvertDropdownListMultipleWithKeysPropertyEditor(object value, IEnumerable expected) + { + var converter = new DropdownListMultipleWithKeysValueConverter(); + var result = converter.ConvertDataToSource(null, value, false); + + Assert.AreEqual(expected, result); + } + } } diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config index 4d229c7dcc..1dc6f8aae1 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config @@ -55,6 +55,9 @@ Textstring + + + true diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index cc71b26b70..42e21e1b3d 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -106,6 +106,9 @@ Textstring true + + + true @@ -311,10 +314,10 @@ Configure it here if you need anything specific. Needs to be a complete url with scheme and umbraco path, eg http://mysite.com/umbraco. NOT just "mysite.com" or "mysite.com/umbraco" or "http://mysite.com". --> - + \ No newline at end of file diff --git a/src/Umbraco.Web/Models/RelatedLink.cs b/src/Umbraco.Web/Models/RelatedLink.cs new file mode 100644 index 0000000000..884bdebeef --- /dev/null +++ b/src/Umbraco.Web/Models/RelatedLink.cs @@ -0,0 +1,8 @@ +namespace Umbraco.Web.Models +{ + public class RelatedLink : RelatedLinkBase + { + public int? Id { get; internal set; } + internal bool IsDeleted { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/RelatedLinkBase.cs b/src/Umbraco.Web/Models/RelatedLinkBase.cs new file mode 100644 index 0000000000..c2077ce4a9 --- /dev/null +++ b/src/Umbraco.Web/Models/RelatedLinkBase.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace Umbraco.Web.Models +{ + public abstract class RelatedLinkBase + { + [JsonProperty("caption")] + public string Caption { get; set; } + [JsonProperty("link")] + public string Link { get; set; } + [JsonProperty("newWindow")] + public bool NewWindow { get; set; } + [JsonProperty("isInternal")] + public bool IsInternal { get; set; } + [JsonProperty("type")] + public RelatedLinkType Type { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/RelatedLinkType.cs b/src/Umbraco.Web/Models/RelatedLinkType.cs new file mode 100644 index 0000000000..eec7817ab6 --- /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..afccdfb31f --- /dev/null +++ b/src/Umbraco.Web/Models/RelatedLinks.cs @@ -0,0 +1,42 @@ +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +namespace Umbraco.Web.Models +{ + [TypeConverter(typeof(RelatedLinksTypeConverter))] + public class RelatedLinks : IEnumerable + { + private readonly string _propertyData; + + private readonly IEnumerable _relatedLinks; + + public RelatedLinks(IEnumerable relatedLinks, string propertyData) + { + _relatedLinks = relatedLinks; + _propertyData = propertyData; + } + + /// + /// Gets the property data. + /// + internal string PropertyData + { + get + { + return this._propertyData; + } + } + + public IEnumerator GetEnumerator() + { + return _relatedLinks.GetEnumerator(); + } + + 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..815e07612f --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerPropertyConverter.cs @@ -0,0 +1,140 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Umbraco +// +// +// The content picker property editor converter. +// +// -------------------------------------------------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Globalization; + +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors.ValueConverters +{ + /// + /// The content picker property value converter. + /// + [DefaultPropertyValueConverter] + [PropertyValueType(typeof(IPublishedContent))] + [PropertyValueCache(PropertyCacheValue.Object, PropertyCacheLevel.ContentCache)] + [PropertyValueCache(PropertyCacheValue.Source, PropertyCacheLevel.Content)] + [PropertyValueCache(PropertyCacheValue.XPath, PropertyCacheLevel.Content)] + public class ContentPickerPropertyConverter : PropertyValueConverterBase + { + /// + /// 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) + { + if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters) + { + return propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.ContentPickerAlias); + } + return false; + } + + /// + /// 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(); + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/ImageCropDataSetConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/ImageCropDataSetConverter.cs index 82278676cd..19e87f7d23 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/ImageCropDataSetConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/ImageCropDataSetConverter.cs @@ -13,14 +13,14 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters /// public class ImageCropDataSetConverter : TypeConverter { + private static readonly Type[] ConvertableTypes = new[] + { + typeof(JObject) + }; + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { - var convertableTypes = new[] - { - typeof(JObject) - }; - - return convertableTypes.Any(x => TypeHelper.IsTypeAssignableFrom(x, destinationType)) + return ConvertableTypes.Any(x => TypeHelper.IsTypeAssignableFrom(x, destinationType)) || base.CanConvertFrom(context, destinationType); } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/LegacyRelatedLinksEditorValueConvertor.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/LegacyRelatedLinksEditorValueConvertor.cs new file mode 100644 index 0000000000..28a20cfd00 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/LegacyRelatedLinksEditorValueConvertor.cs @@ -0,0 +1,111 @@ +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; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.PropertyEditors.ValueConverters; + +namespace Umbraco.Web.PropertyEditors.ValueConverters +{ + [PropertyValueType(typeof(JArray))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + [DefaultPropertyValueConverter(typeof(JsonValueConverter))] //this shadows the JsonValueConverter + public class LegacyRelatedLinksEditorValueConvertor : PropertyValueConverterBase + { + public override bool IsConverter(PublishedPropertyType propertyType) + { + if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters == false) + { + return Constants.PropertyEditors.RelatedLinksAlias.Equals(propertyType.PropertyEditorAlias); + } + return false; + } + + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + if (source == null) return null; + var sourceString = source.ToString(); + + if (sourceString.DetectIsJson()) + { + 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); + } + } + + //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; + } + } +} diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerPropertyConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerPropertyConverter.cs new file mode 100644 index 0000000000..aee456c9d1 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerPropertyConverter.cs @@ -0,0 +1,104 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// 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. + /// + [DefaultPropertyValueConverter] + [PropertyValueType(typeof(IPublishedContent))] + [PropertyValueCache(PropertyCacheValue.Object, PropertyCacheLevel.ContentCache)] + [PropertyValueCache(PropertyCacheValue.Source, PropertyCacheLevel.Content)] + [PropertyValueCache(PropertyCacheValue.XPath, PropertyCacheLevel.Content)] + public class MediaPickerPropertyConverter : PropertyValueConverterBase + { + /// + /// 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; + } + + + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MemberPickerPropertyConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MemberPickerPropertyConverter.cs new file mode 100644 index 0000000000..5f4c9ff92f --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MemberPickerPropertyConverter.cs @@ -0,0 +1,51 @@ +using System; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; +using Umbraco.Web.Security; + +namespace Umbraco.Web.PropertyEditors.ValueConverters +{ + [DefaultPropertyValueConverter] + [PropertyValueType(typeof(IPublishedContent))] + [PropertyValueCache(PropertyCacheValue.Object, PropertyCacheLevel.ContentCache)] + [PropertyValueCache(PropertyCacheValue.Source, PropertyCacheLevel.Content)] + [PropertyValueCache(PropertyCacheValue.XPath, PropertyCacheLevel.Content)] + public class MemberPickerPropertyConverter : PropertyValueConverterBase + { + public override bool IsConverter(PublishedPropertyType propertyType) + { + if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters) + { + return propertyType.PropertyEditorAlias.InvariantEquals(Constants.PropertyEditors.MemberPickerAlias); + } + return false; + } + + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + var intAttempt = source.TryConvertTo(); + if (intAttempt.Success) + return intAttempt.Result; + + return null; + } + + public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) + { + if (source == null) + return null; + + if (UmbracoContext.Current != null) + { + var membershipHelper = new MembershipHelper(UmbracoContext.Current); + return membershipHelper.GetById((int) source); + } + + return null; + } + + } +} diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerPropertyConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerPropertyConverter.cs new file mode 100644 index 0000000000..431efda8f4 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerPropertyConverter.cs @@ -0,0 +1,175 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Umbraco +// +// +// The multi node tree picker property editor value converter. +// +// -------------------------------------------------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.Configuration; +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. + /// + [DefaultPropertyValueConverter] + [PropertyValueType(typeof(IEnumerable))] + [PropertyValueCache(PropertyCacheValue.Object, PropertyCacheLevel.ContentCache)] + [PropertyValueCache(PropertyCacheValue.Source, PropertyCacheLevel.Content)] + [PropertyValueCache(PropertyCacheValue.XPath, PropertyCacheLevel.Content)] + public class MultiNodeTreePickerPropertyConverter : PropertyValueConverterBase + { + /// + /// 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) + { + if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters) + { + return propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MultiNodeTreePickerAlias); + } + return false; + } + + /// + /// 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; + } + + //TODO: Inject an UmbracoHelper and create a GetUmbracoHelper method based on either injected or singleton + if (UmbracoContext.Current != null) + { + var nodeIds = (int[])source; + + if ((propertyType.PropertyTypeAlias != null && PropertiesToExclude.InvariantContains(propertyType.PropertyTypeAlias)) == false) + { + var multiNodeTreePicker = new List(); + + 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); + } + } + } + //TODO: Get rid of this Yield thing + return multiNodeTreePicker.Yield().Where(x => x != null); + } + + // return the first nodeId as this is one of the excluded properties that expects a single id + return nodeIds.FirstOrDefault(); + } + else + { + return null; + } + } + + /// + /// 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..3b759fc9ff --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultipleMediaPickerPropertyConverter.cs @@ -0,0 +1,234 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Umbraco +// +// +// The multiple media picker property editor converter. +// +// -------------------------------------------------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; + +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; + +namespace Umbraco.Web.PropertyEditors.ValueConverters +{ + /// + /// The multiple media picker property value converter. + /// + [DefaultPropertyValueConverter] + public class MultipleMediaPickerPropertyConverter : PropertyValueConverterBase, IPropertyValueConverterMeta + { + private readonly IDataTypeService _dataTypeService; + + //TODO: Remove this ctor in v8 since the other one will use IoC + public MultipleMediaPickerPropertyConverter() + : this(ApplicationContext.Current.Services.DataTypeService) + { + } + + public MultipleMediaPickerPropertyConverter(IDataTypeService dataTypeService) + { + if (dataTypeService == null) throw new ArgumentNullException("dataTypeService"); + _dataTypeService = dataTypeService; + } + + /// + /// Checks if this converter can convert the property editor and registers if it can. + /// + /// + /// The property type. + /// + /// + /// The . + /// + public override bool IsConverter(PublishedPropertyType propertyType) + { + if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters) + { + return propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MultipleMediaPickerAlias); + } + return false; + } + + /// + /// 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); + } + + // in v8 should return multiNodeTreePickerEnumerable but for v7 need to return as PublishedContentEnumerable so that string can be returned for legacy compatibility + return new PublishedContentEnumerable(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) + { + // ** This must be cached (U4-8862) ** + var multiPickerPreValue = + _dataTypeService.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..ec46889fae 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksEditorValueConvertor.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksEditorValueConvertor.cs @@ -1,106 +1,153 @@ -using System; +// -------------------------------------------------------------------------------------------------------------------- +// +// Umbraco +// +// +// Defines the RelatedLinksPropertyConverter type. +// +// -------------------------------------------------------------------------------------------------------------------- + +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; using Umbraco.Core; +using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; -using Umbraco.Core.PropertyEditors.ValueConverters; +using Umbraco.Web.Models; +using Umbraco.Web.Routing; 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. + /// + [DefaultPropertyValueConverter] + [PropertyValueType(typeof(RelatedLinks))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.ContentCache)] + public class RelatedLinksPropertyConverter : PropertyValueConverterBase { + private readonly UrlProvider _urlProvider; + + public RelatedLinksPropertyConverter() + { + } + + public RelatedLinksPropertyConverter(UrlProvider urlProvider) + { + if (urlProvider == null) throw new ArgumentNullException("urlProvider"); + _urlProvider = urlProvider; + } + + /// + /// 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); + if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters) + { + return propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.RelatedLinksAlias); + } + return false; } - 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; + if (source == null) + { + return null; + } + var sourceString = source.ToString(); - if (sourceString.DetectIsJson()) + var relatedLinksData = JsonConvert.DeserializeObject>(sourceString); + var relatedLinks = new List(); + + foreach (var linkData in relatedLinksData) { - try + var relatedLink = new RelatedLink() { - 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; + Caption = linkData.Caption, + NewWindow = linkData.NewWindow, + IsInternal = linkData.IsInternal, + Type = linkData.Type, + Id = linkData.Internal, + Link = linkData.Link + }; + relatedLink = CreateLink(relatedLink); + + if (relatedLink.IsDeleted == false) + { + relatedLinks.Add(relatedLink); } - catch (Exception ex) + else { - LogHelper.Error("Could not parse the string " + sourceString + " to a json object", ex); + 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)); } } - //it's not json, just return the string - return sourceString; + return new RelatedLinks(relatedLinks, sourceString); } - public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview) + private RelatedLink CreateLink(RelatedLink link) { - if (source == null) return null; - var sourceString = source.ToString(); - - if (sourceString.DetectIsJson()) + if (link.IsInternal && link.Id != null) { - try + if (_urlProvider == null && UmbracoContext.Current == null) { - 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(); + return null; } - catch (Exception ex) + + var urlProvider = _urlProvider ?? UmbracoContext.Current.UrlProvider; + + link.Link = urlProvider.GetUrl((int)link.Id); + if (link.Link.Equals("#")) { - LogHelper.Error("Could not parse the string " + sourceString + " to a json object", ex); + link.IsDeleted = true; + link.Link = link.Id.ToString(); + } + else + { + link.IsDeleted = false; } } - //it's not json, just return the string - return sourceString; + return link; + } + + internal class RelatedLinkData : RelatedLinkBase + { + [JsonProperty("internal")] + public int? Internal { get; set; } + [JsonProperty("edit")] + public bool Edit { get; set; } + [JsonProperty("internalName")] + public string InternalName { get; set; } + [JsonProperty("title")] + public string Title { get; set; } } } -} +} \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs index 2802a4d631..a6ea79d283 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs @@ -15,14 +15,15 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { /// - /// A value converter for TinyMCE that will ensure any macro content is rendered properly even when - /// used dynamically. - /// + /// A value converter for TinyMCE that will ensure any macro content is rendered properly even when + /// used dynamically. + /// // because that version of RTE converter parses {locallink} and executes macros, when going from // data to source, its source value has to be cached at the request level, because we have no idea // what the macros may depend on actually. An so, object and xpath need to follow... request, too. // note: the TinyMceValueConverter is NOT inherited, so the PropertyValueCache attribute here is not // actually required (since Request is default) but leave it here to be absolutely explicit. + [DefaultPropertyValueConverter] [PropertyValueType(typeof(IHtmlString))] [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Request)] public class RteMacroRenderingValueConverter : TinyMceValueConverter diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs index 8938325c35..8354ca629d 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs @@ -10,11 +10,12 @@ using Umbraco.Web.Templates; namespace Umbraco.Web.PropertyEditors.ValueConverters { + [DefaultPropertyValueConverter] [PropertyValueType(typeof(string))] [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Request)] public class TextStringValueConverter : PropertyValueConverterBase { - private readonly static string[] PropertyTypeAliases = + private static readonly string[] PropertyTypeAliases = { Constants.PropertyEditors.TextboxAlias, Constants.PropertyEditors.TextboxMultipleAlias diff --git a/src/Umbraco.Web/RelatedLinksTypeConverter.cs b/src/Umbraco.Web/RelatedLinksTypeConverter.cs new file mode 100644 index 0000000000..2debe5f3ac --- /dev/null +++ b/src/Umbraco.Web/RelatedLinksTypeConverter.cs @@ -0,0 +1,97 @@ +using System; +using System.ComponentModel; +using System.Globalization; +using System.Linq; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Web.Models; + +namespace Umbraco.Web +{ + public class RelatedLinksTypeConverter : TypeConverter + { + private readonly UmbracoHelper _umbracoHelper; + + public RelatedLinksTypeConverter(UmbracoHelper umbracoHelper) + { + _umbracoHelper = umbracoHelper; + } + + public RelatedLinksTypeConverter() + { + + } + + private static readonly Type[] ConvertableTypes = new[] + { + typeof(JArray) + }; + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + return ConvertableTypes.Any(x => TypeHelper.IsTypeAssignableFrom(x, destinationType)) + || base.CanConvertFrom(context, destinationType); + } + + public override object ConvertTo( + ITypeDescriptorContext context, + CultureInfo culture, + object value, + Type destinationType) + { + var relatedLinks = value as RelatedLinks; + if (relatedLinks == null) + return null; + + if (TypeHelper.IsTypeAssignableFrom(destinationType)) + { + // Conversion to JArray taken from old value converter + + var obj = JsonConvert.DeserializeObject(relatedLinks.PropertyData); + + var umbracoHelper = GetUmbracoHelper(); + + //update the internal links if we have a context + if (umbracoHelper != null) + { + foreach (var a in obj) + { + var type = a.Value("type"); + if (type.IsNullOrWhiteSpace() == false) + { + if (type == "internal") + { + var linkId = a.Value("link"); + var link = umbracoHelper.NiceUrl(linkId); + a["link"] = link; + } + } + } + } + return obj; + + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + private UmbracoHelper GetUmbracoHelper() + { + if (_umbracoHelper != null) + return _umbracoHelper; + + if (UmbracoContext.Current == null) + { + LogHelper.Warn("Cannot create an UmbracoHelper the UmbracoContext is null"); + return null; + } + + //DO NOT assign to _umbracoHelper variable, this is a singleton class and we cannot assign this based on an UmbracoHelper which is request based + return new UmbracoHelper(UmbracoContext.Current); + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 13baf9bbd4..433235c6f0 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -378,10 +378,14 @@ + + + + @@ -400,12 +404,19 @@ + + + + + + +