/// <summary> /// Processes the value. /// </summary> /// <returns> /// The <see cref="object" /> representing the processed value. /// </returns> public override object ProcessValue() { object result = this.Value; var propertyType = this.Context.PropertyDescriptor.PropertyType; var propertyIsEnumerableType = Direction == EnumerableConvertionDirection.Automatic ? propertyType.IsEnumerableType() && !(propertyType == typeof(string)) : Direction == EnumerableConvertionDirection.ToEnumerable; if (this.Value != null) { var valueType = this.Value.GetType(); var valueIsEnumerableType = valueType.IsEnumerableType() && !(this.Value is string); if (propertyIsEnumerableType) { if (!valueIsEnumerableType) { // Property is enumerable, but value isn't, so make enumerable var arr = Array.CreateInstance(valueType, 1); arr.SetValue(this.Value, 0); result = arr; } } else { if (valueIsEnumerableType) { // Property is not enumerable, but value is, so grab first item var enumerator = ((IEnumerable)this.Value).GetEnumerator(); result = enumerator.MoveNext() ? enumerator.Current : null; } } } else { if (propertyIsEnumerableType) { if (propertyType.IsInterface && !propertyType.IsEnumerableOfKeyValueType()) { // Value is null, but property is enumerable interface, so return empty enumerable result = EnumerableInvocations.Empty(propertyType.GenericTypeArguments.First()); } else { // Concrete enumerables cannot be cast from Array so we create an instance and return it // if we know it has an empty constructor. var constructorParams = propertyType.GetConstructorParameters(); if (constructorParams != null && constructorParams.Length == 0) { // Internally this uses Activator.CreateInstance which is heavily optimized. result = propertyType.GetInstance(); } } } } return(result); }
/// <summary> /// Gets a collection of the given type from the given <see cref="IEnumerable{IPublishedContent}"/>. /// </summary> /// <param name="items"> /// The <see cref="IEnumerable{IPublishedContent}"/> to convert. /// </param> /// <param name="type"> /// The <see cref="Type"/> of items to return. /// </param> /// <param name="documentTypeAlias"> /// The document type alias. /// </param> /// <param name="convertingType"> /// The <see cref="Action{ConvertingTypeEventArgs}"/> to fire when converting. /// </param> /// <param name="convertedType"> /// The <see cref="Action{ConvertedTypeEventArgs}"/> to fire when converted. /// </param> /// <param name="culture"> /// The <see cref="CultureInfo"/>. /// </param> /// <returns> /// The resolved <see cref="IEnumerable{T}"/>. /// </returns> public static IEnumerable <object> As( this IEnumerable <IPublishedContent> items, Type type, string documentTypeAlias = null, Action <ConvertingTypeEventArgs> convertingType = null, Action <ConvertedTypeEventArgs> convertedType = null, CultureInfo culture = null) { using (DisposableTimer.DebugDuration <IEnumerable <object> >(string.Format("IEnumerable As ({0})", documentTypeAlias))) { IEnumerable <object> typedItems; if (string.IsNullOrWhiteSpace(documentTypeAlias)) { typedItems = items.Select(x => x.As(type, convertingType, convertedType, culture)); } else { typedItems = items.Where(x => documentTypeAlias.InvariantEquals(x.DocumentTypeAlias)) .Select(x => x.As(type, convertingType, convertedType, culture)); } // We need to cast back here as nothing is strong typed anymore. return((IEnumerable <object>)EnumerableInvocations.Cast(type, typedItems)); } }
/// <summary> /// Gets a collection of the given type from the given <see cref="IEnumerable{IPublishedContent}"/>. /// </summary> /// <param name="items"> /// The <see cref="IEnumerable{IPublishedContent}"/> to convert. /// </param> /// <param name="type"> /// The <see cref="Type"/> of items to return. /// </param> /// <param name="culture"> /// The <see cref="CultureInfo"/>. /// </param> /// <param name="valueResolverContexts"> /// A collection of <see cref="DittoValueResolverContext"/> entities to use whilst resolving values. /// </param> /// <param name="onConverting"> /// The <see cref="Action{ConversionHandlerContext}"/> to fire when converting. /// </param> /// <param name="onConverted"> /// The <see cref="Action{ConversionHandlerContext}"/> to fire when converted. /// </param> /// <returns> /// The resolved <see cref="IEnumerable{T}"/>. /// </returns> public static IEnumerable <object> As( this IEnumerable <IPublishedContent> items, Type type, CultureInfo culture = null, IEnumerable <DittoValueResolverContext> valueResolverContexts = null, Action <DittoConversionHandlerContext> onConverting = null, Action <DittoConversionHandlerContext> onConverted = null) { using (DittoDisposableTimer.DebugDuration <IEnumerable <object> >("IEnumerable As")) { var typedItems = items.Select(x => x.As(type, culture, null, valueResolverContexts, onConverting, onConverted)); // We need to cast back here as nothing is strong typed anymore. return((IEnumerable <object>)EnumerableInvocations.Cast(type, typedItems)); } }
/// <summary> /// Gets a collection of the given type from the given <see cref="IEnumerable{IPublishedContent}"/>. /// </summary> /// <param name="items"> /// The <see cref="IEnumerable{IPublishedContent}"/> to convert. /// </param> /// <param name="type"> /// The <see cref="Type"/> of items to return. /// </param> /// <param name="culture"> /// The <see cref="CultureInfo"/>. /// </param> /// <param name="processorContexts"> /// A collection of <see cref="DittoProcessorContext"/> entities to use whilst processing values. /// </param> /// <param name="onConverting"> /// The <see cref="Action{ConversionHandlerContext}"/> to fire when converting. /// </param> /// <param name="onConverted"> /// The <see cref="Action{ConversionHandlerContext}"/> to fire when converted. /// </param> /// <param name="chainContext"> /// The <see cref="DittoChainContext"/> for the current processor chain. /// </param> /// <returns> /// The converted <see cref="IEnumerable{T}"/>. /// </returns> public static IEnumerable <object> As( this IEnumerable <IPublishedContent> items, Type type, CultureInfo culture = null, IEnumerable <DittoProcessorContext> processorContexts = null, Action <DittoConversionHandlerContext> onConverting = null, Action <DittoConversionHandlerContext> onConverted = null, DittoChainContext chainContext = null) { using (DittoDisposableTimer.DebugDuration <Ditto>($"IEnumerable As<{type.Name}>")) { var typedItems = items.Select(x => x.As(type, culture, null, processorContexts, onConverting, onConverted, chainContext)); // We need to cast back here as nothing is strong typed anymore. return((IEnumerable <object>)EnumerableInvocations.Cast(type, typedItems)); } }
/// <summary>Returns the processed value for the given type and property.</summary> /// <param name="content">The content.</param> /// <param name="mappableProperty">Information about the mappable property.</param> /// <param name="baseProcessorContext">The base processor context.</param> /// <param name="chainContext">The <see cref="DittoChainContext"/> for the current processor chain.</param> /// <returns>Returns the processed value.</returns> private static object DoGetProcessedValue( IPublishedContent content, DittoTypeInfo.DittoTypePropertyInfo mappableProperty, DittoProcessorContext baseProcessorContext, DittoChainContext chainContext) { // Create holder for value as it's processed object currentValue = content; // Process attributes foreach (var processorAttr in mappableProperty.Processors) { using (DittoDisposableTimer.DebugDuration(typeof(Ditto), $"Processor '{processorAttr}' ({content.Id})")) { // Get the right context type var ctx = chainContext.ProcessorContexts.GetOrCreate(baseProcessorContext, processorAttr.ContextType); // Populate UmbracoContext & ApplicationContext processorAttr.UmbracoContext = ContextAccessor.UmbracoContext; processorAttr.ApplicationContext = ContextAccessor.ApplicationContext; // Process value currentValue = processorAttr.ProcessValue(currentValue, ctx, chainContext); } } // The following has to happen after all the processors. if (mappableProperty.IsEnumerable && currentValue != null && currentValue.Equals(Enumerable.Empty <object>())) { if (mappableProperty.PropertyInfo.PropertyType.IsInterface) { // You cannot set an enumerable of type from an empty object array. currentValue = EnumerableInvocations.Cast(mappableProperty.EnumerableType, (IEnumerable)currentValue); } else { // This should allow the casting back of IEnumerable<T> to an empty List<T> Collection<T> etc. // I cant think of any that don't have an empty constructor currentValue = mappableProperty.PropertyInfo.PropertyType.GetInstance(); } } return(currentValue == null && mappableProperty.PropertyInfo.PropertyType.IsValueType ? mappableProperty.PropertyInfo.PropertyType.GetInstance() // Set to default instance of value type : currentValue); }
/// <summary> /// Processes the value. /// </summary> /// <returns> /// The <see cref="object" /> representing the processed value. /// </returns> /// <exception cref="System.NotImplementedException"></exception> public override object ProcessValue() { object result = Value; var propertyIsEnumerableType = Context.PropertyDescriptor.PropertyType.IsEnumerableType() && !Context.PropertyDescriptor.PropertyType.IsEnumerableOfKeyValueType() && !(Context.PropertyDescriptor.PropertyType == typeof(string)); if (Value != null) { var valueIsEnumerableType = Value.GetType().IsEnumerableType() && !Value.GetType().IsEnumerableOfKeyValueType() && !(Value is string); if (propertyIsEnumerableType) { if (!valueIsEnumerableType) { // Property is enumerable, but value isn't, so make enumerable var arr = Array.CreateInstance(Value.GetType(), 1); arr.SetValue(Value, 0); result = arr; } } else { if (valueIsEnumerableType) { // Property is not enumerable, but value is, so grab first item var enumerator = ((IEnumerable)Value).GetEnumerator(); result = enumerator.MoveNext() ? enumerator.Current : null; } } } else { if (propertyIsEnumerableType) { // Value is null, but property is enumerable, so return empty enumerable result = EnumerableInvocations.Empty(Context.PropertyDescriptor.PropertyType.GenericTypeArguments.First()); } } return(result); }
/// <summary> /// Converts the given object to the type of this converter, using the specified context and culture information. /// </summary> /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param> /// <param name="culture">The <see cref="T:System.Globalization.CultureInfo" /> to use as the current culture.</param> /// <param name="value">The <see cref="T:System.Object" /> to convert.</param> /// <returns> /// An <see cref="T:System.Object" /> that represents the converted value. /// </returns> public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (context == null || context.PropertyDescriptor == null) { return(Enumerable.Empty <object>()); } var propertyType = context.PropertyDescriptor.PropertyType; var isGenericType = propertyType.IsGenericType; var targetType = isGenericType ? propertyType.GenericTypeArguments.First() : propertyType; if (value.IsNullOrEmptyString()) { return(EnumerableInvocations.Empty(targetType)); } // DictionaryPublishedContent IPublishedContent content = value as IPublishedContent; if (content != null) { // Use the id so we get folder sanitation. return(this.ConvertMediaFromInt(content.Id, targetType, culture)); } // If a single item is selected, this is passed as an int, not a string. if (value is int) { var id = (int)value; return(this.ConvertMediaFromInt(id, targetType, culture).YieldSingleItem()); } var s = value as string; if (!string.IsNullOrWhiteSpace(s)) { var multiMediaPicker = EnumerableInvocations.Empty(targetType); int n; var nodeIds = XmlHelper.CouldItBeXml(s) ? s.GetXmlIds() : s .ToDelimitedList() .Select(x => int.TryParse(x, NumberStyles.Any, culture, out n) ? n : -1) .Where(x => x > 0) .ToArray(); if (nodeIds.Any()) { multiMediaPicker = nodeIds.ForEach(i => this.ConvertMediaFromInt(i, targetType, culture)); } return(multiMediaPicker); } return(base.ConvertFrom(context, culture, value)); }
/// <summary> /// Set the typed value to the given instance. /// </summary> /// <param name="content">The <see cref="IPublishedContent"/> to convert.</param> /// <param name="culture">The <see cref="CultureInfo"/></param> /// <param name="propertyInfo">The <see cref="PropertyInfo"/> property info associated with the type.</param> /// <param name="propertyValue">The property value.</param> /// <param name="instance">The instance to assign the value to.</param> /// <returns>The strong typed converted value for the given property.</returns> private static object GetConvertedValue( IPublishedContent content, CultureInfo culture, PropertyInfo propertyInfo, object propertyValue, object instance) { // Process the value. object result = null; var propertyType = propertyInfo.PropertyType; var typeInfo = propertyType.GetTypeInfo(); // This should return false against typeof(string) etc also. var propertyIsEnumerableType = propertyType.IsCastableEnumerableType(); // Try any custom type converters first. // 1: Check the property. // 2: Check any type arguments in generic enumerable types. // 3: Check the type itself. var converterAttribute = propertyInfo.GetCustomAttribute <TypeConverterAttribute>() ?? (propertyIsEnumerableType ? typeInfo.GenericTypeArguments.First().GetCustomAttribute <TypeConverterAttribute>(true) : propertyType.GetCustomAttribute <TypeConverterAttribute>(true)); if (converterAttribute != null && converterAttribute.ConverterTypeName != null) { // Time custom conversions. using (DittoDisposableTimer.DebugDuration <object>(string.Format("Custom TypeConverter ({0}, {1})", content.Id, propertyInfo.Name))) { // Get the custom converter from the attribute and attempt to convert. var converterType = Type.GetType(converterAttribute.ConverterTypeName); if (converterType != null) { var converter = converterType.GetDependencyResolvedInstance() as TypeConverter; if (converter != null) { // Create context to pass to converter implementations. // This contains the IPublishedContent and the currently converting property descriptor. var descriptor = TypeDescriptor.GetProperties(instance)[propertyInfo.Name]; var context = new DittoTypeConverterContext { ConversionType = instance.GetType(), Instance = content, PropertyDescriptor = descriptor }; Type propertyValueType = null; if (propertyValue != null) { propertyValueType = propertyValue.GetType(); } // We're deliberately passing null. // ReSharper disable once AssignNullToNotNullAttribute if (converter.CanConvertFrom(context, propertyValueType)) { object converted = converter.ConvertFrom(context, culture, propertyValue); if (converted != null) { // Handle Typeconverters returning single objects when we want an IEnumerable. // Use case: Someone selects a folder of images rather than a single image with the media picker. var convertedType = converted.GetType(); if (propertyIsEnumerableType) { var parameterType = typeInfo.GenericTypeArguments.First(); // Some converters return an IEnumerable so we check again. if (!convertedType.IsEnumerableType()) { // Using 'Cast' to convert the type back to IEnumerable<T>. object enumerablePropertyValue = EnumerableInvocations.Cast( parameterType, converted.YieldSingleItem()); result = enumerablePropertyValue; } else { // Nothing is strong typed anymore. result = EnumerableInvocations.Cast(parameterType, (IEnumerable)converted); } } else { // Return single expected items from converters returning an IEnumerable. // Check for key/value pairs and strings. if (convertedType.IsEnumerableType() && !convertedType.IsEnumerableOfKeyValueType() && !(convertedType == typeof(string) && propertyType == typeof(string))) { // Use 'FirstOrDefault' to convert the type back to T. result = EnumerableInvocations.FirstOrDefault( propertyType, (IEnumerable)converted); } else { result = converted; } } } else { // Ensure we pass back an empty enumerable if the expected output is an enumerable. // and null has been returned by the type converter. if (propertyIsEnumerableType) { result = EnumerableInvocations.Empty(typeInfo.GenericTypeArguments.First()); } } } else if (propertyType.IsInstanceOfType(propertyValue)) { // If the TypeConverter's `CanConvertFrom` has returned false, // then we can check if the value is the same type as the target type. result = propertyValue; } } } } } else if (propertyType == typeof(HtmlString)) { // Handle Html strings so we don't have to set the attribute. var converterType = typeof(DittoHtmlStringConverter); var converter = converterType.GetDependencyResolvedInstance() as TypeConverter; if (converter != null) { // This contains the IPublishedContent and the currently converting property descriptor. var descriptor = TypeDescriptor.GetProperties(instance)[propertyInfo.Name]; var context = new DittoTypeConverterContext { ConversionType = instance.GetType(), Instance = content, PropertyDescriptor = descriptor }; Type propertyValueType = null; if (propertyValue != null) { propertyValueType = propertyValue.GetType(); } // We're deliberately passing null. // ReSharper disable once AssignNullToNotNullAttribute if (converter.CanConvertFrom(context, propertyValueType)) { result = converter.ConvertFrom(context, culture, propertyValue); } } } else if (propertyType.IsInstanceOfType(propertyValue)) { // Simple types result = propertyValue; } else if (propertyValue is IPublishedContent && propertyType.IsClass) { // If the property value is an IPublishedContent, then we can use Ditto to map to the target type. result = ((IPublishedContent)propertyValue).As(propertyType); } else if (propertyValue != null && propertyValue.GetType().IsEnumerableOfType(typeof(IPublishedContent)) && propertyType.IsEnumerable() && propertyType.GetEnumerableType() != null && propertyType.GetEnumerableType().IsClass) { // If the property value is IEnumerable<IPublishedContent>, then we can use Ditto to map to the target type. result = ((IEnumerable <IPublishedContent>)propertyValue).As(propertyType.GetEnumerableType()); } else { using (DittoDisposableTimer.DebugDuration <object>(string.Format("TypeConverter ({0}, {1})", content.Id, propertyInfo.Name))) { var convert = propertyValue.TryConvertTo(propertyType); if (convert.Success) { result = convert.Result; } } } return(result); }
/// <summary> /// Processes the value. /// </summary> /// <returns> /// The <see cref="object" /> representing the processed value. /// </returns> public override object ProcessValue() { var propType = this.Context.PropertyDescriptor.PropertyType; var propTypeIsEnumerable = propType.IsEnumerableType(); var baseType = propTypeIsEnumerable ? propType.GetEnumerableType() : propType; // We have an enumerable processor that runs at the end of every conversion // converting individual instances to IEnumerables and vica versa, so we // won't worry about returning in the right way, rather we'll just ensure // that the IPublishedContent's are converted to the right types // and let the enumerable processor handle the rest. // TODO: Validate the base type more? // Get the list of types to search through // If we have explicitly set a list of allowed types, just use those // otherwise attempt to search through loaded assemblies IEnumerable <Type> types; if (AllowedTypes != null && AllowedTypes.Length > 0) { types = AllowedTypes; } else { types = (IEnumerable <Type>)ApplicationContext.ApplicationCache.StaticCache.GetCacheItem( $"DittoFactoryAttribute_ResolveTypes_{baseType.AssemblyQualifiedName}", () => { // Workaround for http://issues.umbraco.org/issue/U4-9011 if (baseType.Assembly.IsAppCodeAssembly()) { // This logic is taken from the core type finder so it should be performing the same checks. return(baseType.Assembly .GetTypes() .Where(t => baseType.IsAssignableFrom(t) && t.IsClass && !t.IsAbstract && !t.IsSealed && !t.IsNestedPrivate && t.GetCustomAttribute <HideFromTypeFinderAttribute>(true) == null) .ToArray()); } // Find the appropriate types // There is no non generic version of ResolveTypes so we have to // call it via reflection. var method = typeof(PluginManager).GetMethod("ResolveTypes"); var generic = method.MakeGenericMethod(baseType); return(((IEnumerable <Type>)generic.Invoke(PluginManager.Current, new object[] { true, null })).ToArray()); }); } // Check for IEnumerable<IPublishedContent> value if (this.Value is IEnumerable <IPublishedContent> enumerableValue && enumerableValue != null) { var items = enumerableValue.Select(x => { var typeName = this.ResolveTypeName(x); var type = types.FirstOrDefault(y => y.Name.InvariantEquals(typeName)); return(type != null ? x.As(type, chainContext: ChainContext) : null); }); return(EnumerableInvocations.Cast(baseType, items)); } // Check for IPublishedContent value if (this.Value is IPublishedContent ipublishedContentValue && ipublishedContentValue != null) { var typeName = this.ResolveTypeName(ipublishedContentValue); var type = types.FirstOrDefault(y => y.Name.InvariantEquals(typeName)); return(type != null ? ipublishedContentValue.As(type, chainContext : ChainContext) : null); } // No other possible options return(null); }
/// <summary> /// Returns the processed value for the given type and property. /// </summary> /// <param name="content">The content.</param> /// <param name="propertyInfo">The property information.</param> /// <param name="contextAccessor">The context accessor.</param> /// <param name="baseProcessorContext">The base processor context.</param> /// <param name="chainContext">The <see cref="DittoChainContext"/> for the current processor chain.</param> /// <returns>Returns the processed value.</returns> private static object DoGetProcessedValue( IPublishedContent content, PropertyInfo propertyInfo, IDittoContextAccessor contextAccessor, DittoProcessorContext baseProcessorContext, DittoChainContext chainContext) { // Check the property for any explicit processor attributes var processorAttrs = propertyInfo.GetCustomAttributes <DittoProcessorAttribute>(true) .OrderBy(x => x.Order) .ToList(); if (!processorAttrs.Any()) { // Adds the default processor for this conversion processorAttrs.Add(DittoProcessorRegistry.Instance.GetDefaultProcessorFor(baseProcessorContext.TargetType)); } var propertyType = propertyInfo.PropertyType; // Check for type registered processors processorAttrs.AddRange(propertyType .GetCustomAttributes <DittoProcessorAttribute>(true) .OrderBy(x => x.Order)); // Check any type arguments in generic enumerable types. // This should return false against typeof(string) etc also. var typeInfo = propertyType.GetTypeInfo(); bool isEnumerable = false; Type typeArg = null; if (propertyType.IsCastableEnumerableType()) { typeArg = typeInfo.GenericTypeArguments.First(); processorAttrs.AddRange(typeInfo .GenericTypeArguments .First() .GetCustomAttributes <DittoProcessorAttribute>(true) .OrderBy(x => x.Order) .ToList()); isEnumerable = true; } // Check for globally registered processors processorAttrs.AddRange(DittoProcessorRegistry.Instance.GetRegisteredProcessorAttributesFor(propertyInfo.PropertyType)); // Add any core processors onto the end processorAttrs.AddRange(DittoProcessorRegistry.Instance.GetPostProcessorAttributes()); // Create holder for value as it's processed object currentValue = content; // Process attributes foreach (var processorAttr in processorAttrs) { using (DittoDisposableTimer.DebugDuration <Ditto>($"Processor '{processorAttr.GetType().Name}' ({content.Id})")) { // Get the right context type var ctx = chainContext.ProcessorContexts.GetOrCreate(baseProcessorContext, processorAttr.ContextType); // Populate UmbracoContext & ApplicationContext processorAttr.UmbracoContext = contextAccessor.UmbracoContext; processorAttr.ApplicationContext = contextAccessor.ApplicationContext; // Process value currentValue = processorAttr.ProcessValue(currentValue, ctx, chainContext); } } // The following has to happen after all the processors. if (isEnumerable && currentValue != null && currentValue.Equals(Enumerable.Empty <object>())) { if (propertyType.IsInterface) { // You cannot set an enumerable of type from an empty object array. currentValue = EnumerableInvocations.Cast(typeArg, (IEnumerable)currentValue); } else { // This should allow the casting back of IEnumerable<T> to an empty List<T> Collection<T> etc. // I cant think of any that don't have an empty constructor currentValue = propertyType.GetInstance(); } } return((currentValue == null && propertyType.IsValueType) ? propertyInfo.PropertyType.GetInstance() // Set to default instance of value type : currentValue); }
/// <summary> /// Converts the given object to the type of this converter, using the specified context and culture information. /// </summary> /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param> /// <param name="culture">The <see cref="T:System.Globalization.CultureInfo" /> to use as the current culture.</param> /// <param name="value">The <see cref="T:System.Object" /> to convert.</param> /// <returns> /// An <see cref="T:System.Object" /> that represents the converted value. /// </returns> public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (context == null || context.PropertyDescriptor == null) { // There's no way to determine the type here. return(null); } var propertyType = context.PropertyDescriptor.PropertyType; var isGenericType = propertyType.IsGenericType; var targetType = isGenericType ? propertyType.GenericTypeArguments.First() : propertyType; if (value.IsNullOrEmptyString()) { if (isGenericType) { return(EnumerableInvocations.Empty(targetType)); } return(null); } // If a single item is selected, this comes back as an int, not a string. if (value is int) { var id = (int)value; // CheckBoxList, ListBox if (targetType != null) { return(this.ConvertContentFromInt(id, targetType, culture).YieldSingleItem()); } // AutoComplete, DropDownList, RadioButton return(this.ConvertContentFromInt(id, propertyType, culture)); } if (value != null) { string s = value as string ?? value.ToString(); if (!string.IsNullOrWhiteSpace(s)) { int n; var nodeIds = s .ToDelimitedList() .Select(x => int.TryParse(x, NumberStyles.Any, culture, out n) ? n : -1) .Where(x => x > 0) .ToArray(); if (nodeIds.Any()) { var ultimatePicker = new List <IPublishedContent>(); // ReSharper disable once LoopCanBeConvertedToQuery foreach (var nodeId in nodeIds) { var item = UmbracoContext.Current.ContentCache.GetById(nodeId); if (item != null) { ultimatePicker.Add(item); } } // CheckBoxList, ListBox if (isGenericType) { return(ultimatePicker.As(targetType, culture)); } // AutoComplete, DropDownList, RadioButton return(ultimatePicker.As(targetType, culture).FirstOrDefault()); } } } return(base.ConvertFrom(context, culture, value)); }
/// <summary> /// Converts the given object to the type of this converter, using the specified context and culture information. /// </summary> /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param> /// <param name="culture">The <see cref="T:System.Globalization.CultureInfo" /> to use as the current culture.</param> /// <param name="value">The <see cref="T:System.Object" /> to convert.</param> /// <returns> /// An <see cref="T:System.Object" /> that represents the converted value. /// </returns> public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (context == null || context.PropertyDescriptor == null) { return(Enumerable.Empty <object>()); } var propertyType = context.PropertyDescriptor.PropertyType; var isGenericType = propertyType.IsGenericType; var targetType = isGenericType ? propertyType.GenericTypeArguments.First() : propertyType; if (value.IsNullOrEmptyString()) { return(EnumerableInvocations.Empty(targetType)); } // Single IPublishedContent IPublishedContent content = value as IPublishedContent; if (content != null) { return(content.As(targetType, null, null, culture)); } // ReSharper disable once PossibleNullReferenceException var type = value.GetType(); // Multiple IPublishedContent if (type.IsEnumerableOfType(typeof(IPublishedContent))) { return(((IEnumerable <IPublishedContent>)value).As(targetType, null, null, null, culture)); } int[] nodeIds = { }; // First try enumerable strings, ints. if (type.IsGenericType || type.IsArray) { if (type.IsEnumerableOfType(typeof(string))) { int n; nodeIds = ((IEnumerable <string>)value) .Select(x => int.TryParse(x, NumberStyles.Any, culture, out n) ? n : -1) .ToArray(); } if (type.IsEnumerableOfType(typeof(int))) { nodeIds = ((IEnumerable <int>)value).ToArray(); } } // Now csv strings. if (!nodeIds.Any()) { var s = value as string ?? value.ToString(); if (!string.IsNullOrWhiteSpace(s)) { int n; nodeIds = XmlHelper.CouldItBeXml(s) ? s.GetXmlIds() : s .ToDelimitedList() .Select(x => int.TryParse(x, NumberStyles.Any, culture, out n) ? n : -1) .Where(x => x > 0) .ToArray(); } } if (nodeIds.Any()) { var umbracoContext = UmbracoContext.Current; var membershipHelper = new MembershipHelper(umbracoContext); var objectType = UmbracoObjectTypes.Unknown; var multiPicker = new List <IPublishedContent>(); // Oh so ugly if you let Resharper do this. // ReSharper disable once LoopCanBeConvertedToQuery foreach (var nodeId in nodeIds) { var item = this.GetPublishedContent(nodeId, ref objectType, UmbracoObjectTypes.Document, umbracoContext.ContentCache.GetById) ?? this.GetPublishedContent(nodeId, ref objectType, UmbracoObjectTypes.Media, umbracoContext.MediaCache.GetById) ?? this.GetPublishedContent(nodeId, ref objectType, UmbracoObjectTypes.Member, membershipHelper.GetById); if (item != null) { multiPicker.Add(item); } } return(multiPicker.As(targetType, null, null, null, culture)); } return(base.ConvertFrom(context, culture, value)); }