public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
        {
            try
            {
                using (var timer = DisposableTimer.DebugDuration <NestedContentValueConverter>(string.Format("ConvertDataToSource ({0})", propertyType.DataTypeId)))
                {
                    if (source != null && !source.ToString().IsNullOrWhiteSpace())
                    {
                        var rawValue       = JsonConvert.DeserializeObject <List <object> >(source.ToString());
                        var processedValue = new List <IPublishedContent>();

                        var preValue    = NestedContentHelper.GetPreValuesDictionaryByDataTypeId(propertyType.DataTypeId);
                        var contentType = NestedContentHelper.GetContentTypeFromPreValue(propertyType.DataTypeId);
                        if (contentType == null)
                        {
                            return(null);
                        }

                        var publishedContentType = PublishedContentType.Get(PublishedItemType.Content, contentType.Alias);
                        if (publishedContentType == null)
                        {
                            return(null);
                        }

                        for (var i = 0; i < rawValue.Count; i++)
                        {
                            var o          = rawValue[i];
                            var propValues = ((JObject)o).ToObject <Dictionary <string, object> >();
                            var properties = new List <IPublishedProperty>();

                            foreach (var jProp in propValues)
                            {
                                var propType = publishedContentType.GetPropertyType(jProp.Key);
                                if (propType != null)
                                {
                                    properties.Add(new DetachedPublishedProperty(propType, jProp.Value));
                                }
                            }

                            // Parse out the name manually
                            object nameObj = null;
                            if (propValues.TryGetValue("name", out nameObj))
                            {
                                // Do nothing, we just want to parse out the name if we can
                            }

                            processedValue.Add(new DetachedPublishedContent(nameObj == null ? null : nameObj.ToString(), publishedContentType, properties.ToArray()));
                        }

                        // Detect min/max items == 1 and just return a single IPublishedContent
                        int minItems, maxItems;
                        if (preValue.ContainsKey("minItems") && int.TryParse(preValue["minItems"], out minItems) && minItems == 1 &&
                            preValue.ContainsKey("maxItems") && int.TryParse(preValue["maxItems"], out maxItems) && maxItems == 1)
                        {
                            return(processedValue.FirstOrDefault());
                        }

                        return(processedValue);
                    }
                }
            }
            catch (Exception e)
            {
                LogHelper.Error <NestedContentValueConverter>("Error converting value", e);
            }

            return(null);
        }
            public override object ConvertEditorToDb(ContentPropertyData editorValue, object currentValue)
            {
                if (editorValue.Value == null || string.IsNullOrWhiteSpace(editorValue.Value.ToString()))
                {
                    return(null);
                }

                var value = JsonConvert.DeserializeObject <List <object> >(editorValue.Value.ToString());

                if (value == null)
                {
                    return(string.Empty);
                }

                var contentType = NestedContentHelper.GetContentTypeFromPreValue(editorValue.PreValues);

                if (contentType == null)
                {
                    return(string.Empty);
                }

                // Process value
                for (var i = 0; i < value.Count; i++)
                {
                    var o             = value[i];
                    var propValues    = ((JObject)o);
                    var propValueKeys = propValues.Properties().Select(x => x.Name).ToArray();

                    foreach (var propKey in propValueKeys)
                    {
                        var propType = contentType.PropertyTypes.FirstOrDefault(x => x.Alias == propKey);
                        if (propType == null)
                        {
                            if (propKey != "name")
                            {
                                // Property missing so just delete the value
                                propValues[propKey] = null;
                            }
                        }
                        else
                        {
                            // Fetch the property types prevalue
                            var propPreValues = Services.DataTypeService.GetPreValuesCollectionByDataTypeId(
                                propType.DataTypeDefinitionId);

                            // Lookup the property editor
                            var propEditor = PropertyEditorResolver.Current.GetByAlias(propType.PropertyEditorAlias);

                            // Create a fake content property data object
                            var contentPropData = new ContentPropertyData(
                                propValues[propKey] == null ? null : propValues[propKey].ToString(), propPreValues,
                                new Dictionary <string, object>());

                            // Get the property editor to do it's conversion
                            var newValue = propEditor.ValueEditor.ConvertEditorToDb(contentPropData, propValues[propKey]);

                            // Store the value back
                            propValues[propKey] = (newValue == null) ? null : JToken.FromObject(newValue);
                        }
                    }
                }

                return(JsonConvert.SerializeObject(value));
            }
            public IEnumerable <ValidationResult> Validate(object rawValue, PreValueCollection preValues, PropertyEditor editor)
            {
                var value = JsonConvert.DeserializeObject <List <object> >(rawValue.ToString());

                if (value == null)
                {
                    yield break;
                }

                var contentType = NestedContentHelper.GetContentTypeFromPreValue(preValues);

                if (contentType == null)
                {
                    yield break;
                }

                for (var i = 0; i < value.Count; i++)
                {
                    var o             = value[i];
                    var propValues    = ((JObject)o);
                    var propValueKeys = propValues.Properties().Select(x => x.Name).ToArray();

                    foreach (var propKey in propValueKeys)
                    {
                        var propType = contentType.PropertyTypes.FirstOrDefault(x => x.Alias == propKey);
                        if (propType != null)
                        {
                            // It would be better to pass this off to the individual property editors
                            // to validate themselves and pass the result down, however a lot of the
                            // validation checking code in core seems to be internal so for now we'll
                            // just replicate the mandatory / regex validation checks ourselves.
                            // This does of course mean we will miss any custom validators a property
                            // editor may have registered by itself, and it also means we can only
                            // validate to a single depth so having a complex property editor in a
                            // doc type could get passed validation if it can't be validated from it's
                            // stored value alone.

                            // Check mandatory
                            if (propType.Mandatory)
                            {
                                if (propValues[propKey] == null)
                                {
                                    yield return(new ValidationResult("Item " + (i + 1) + " '" + propType.Name + "' cannot be null", new[] { propKey }));
                                }
                                else if (propValues[propKey].ToString().IsNullOrWhiteSpace())
                                {
                                    yield return(new ValidationResult("Item " + (i + 1) + " '" + propType.Name + "' cannot be empty", new[] { propKey }));
                                }
                            }

                            // Check regex
                            if (!propType.ValidationRegExp.IsNullOrWhiteSpace() &&
                                propValues[propKey] != null && !propValues[propKey].ToString().IsNullOrWhiteSpace())
                            {
                                var regex = new Regex(propType.ValidationRegExp);
                                if (!regex.IsMatch(propValues[propKey].ToString()))
                                {
                                    yield return(new ValidationResult("Item " + (i + 1) + " '" + propType.Name + "' is invalid, it does not match the correct pattern", new[] { propKey }));
                                }
                            }
                        }
                    }
                }
            }
            public override object ConvertDbToEditor(Property property, PropertyType propertyType, IDataTypeService dataTypeService)
            {
                if (property.Value == null || string.IsNullOrWhiteSpace(property.Value.ToString()))
                {
                    return(string.Empty);
                }

                var value = JsonConvert.DeserializeObject <List <object> >(property.Value.ToString());

                if (value == null)
                {
                    return(string.Empty);
                }

                var contentType = NestedContentHelper.GetContentTypeFromPreValue(propertyType.DataTypeDefinitionId);

                if (contentType == null)
                {
                    return(string.Empty);
                }

                // Process value
                for (var i = 0; i < value.Count; i++)
                {
                    var o             = value[i];
                    var propValues    = ((JObject)o);
                    var propValueKeys = propValues.Properties().Select(x => x.Name).ToArray();

                    foreach (var propKey in propValueKeys)
                    {
                        var propType = contentType.PropertyTypes.FirstOrDefault(x => x.Alias == propKey);
                        if (propType == null)
                        {
                            if (propKey != "name")
                            {
                                // Property missing so just delete the value
                                propValues[propKey] = null;
                            }
                        }
                        else
                        {
                            // Create a fake property using the property abd stored value
                            var prop = new Property(propType, propValues[propKey] == null ? null : propValues[propKey].ToString());

                            // Lookup the property editor
                            var propEditor = PropertyEditorResolver.Current.GetByAlias(propType.PropertyEditorAlias);

                            // Get the editor to do it's conversion
                            var newValue = propEditor.ValueEditor.ConvertDbToEditor(prop, propType,
                                                                                    ApplicationContext.Current.Services.DataTypeService);

                            // Store the value back
                            propValues[propKey] = (newValue == null) ? null : JToken.FromObject(newValue);
                        }
                    }
                }

                // Update the value on the property
                property.Value = JsonConvert.SerializeObject(value);

                // Pass the call down
                return(base.ConvertDbToEditor(property, propertyType, dataTypeService));
            }