public override IDictionary <string, object> ConvertDbToEditor(IDictionary <string, object> defaultPreVals, PreValueCollection persistedPreVals)
            {
                // re-format old style (v0.1.1) pre values if necessary
                NestedContentHelper.ConvertPreValueCollectionFromV011(persistedPreVals);

                return(base.ConvertDbToEditor(defaultPreVals, persistedPreVals));
            }
            public override object ConvertEditorToDb(ContentPropertyData editorValue, object currentValue)
            {
                if (editorValue.Value == null)
                {
                    return(null);
                }

                var rawValue = editorValue.Value.ToString();

                if (string.IsNullOrWhiteSpace(rawValue))
                {
                    return(null);
                }

                var value = JsonConvert.DeserializeObject <List <object> >(rawValue);

                if (value == null)
                {
                    return(null);
                }

                // Issue #38 - Keep recursive property lookups working
                if (!value.Any())
                {
                    return(null);
                }

                // Process value
                for (var i = 0; i < value.Count; i++)
                {
                    var o          = value[i];
                    var propValues = ((JObject)o);

                    var contentType = NestedContentHelper.GetContentTypeFromItem(propValues);
                    if (contentType == null)
                    {
                        continue;
                    }

                    var propValueKeys = propValues.Properties().Select(x => x.Name).ToArray();

                    foreach (var propKey in propValueKeys)
                    {
                        var propType = contentType.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == propKey);
                        if (propType == null)
                        {
                            if (IsSystemPropertyKey(propKey) == false)
                            {
                                // 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], 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;
                }

                IDataTypeService dataTypeService = ApplicationContext.Current.Services.DataTypeService;

                for (var i = 0; i < value.Count; i++)
                {
                    var o          = value[i];
                    var propValues = ((JObject)o);

                    var contentType = NestedContentHelper.GetContentTypeFromItem(propValues);
                    if (contentType == null)
                    {
                        continue;
                    }

                    var propValueKeys = propValues.Properties().Select(x => x.Name).ToArray();

                    foreach (var propKey in propValueKeys)
                    {
                        var propType = contentType.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == propKey);
                        if (propType != null)
                        {
                            PreValueCollection propPrevalues  = dataTypeService.GetPreValuesCollectionByDataTypeId(propType.DataTypeDefinitionId);
                            PropertyEditor     propertyEditor = PropertyEditorResolver.Current.GetByAlias(propType.PropertyEditorAlias);

                            foreach (IPropertyValidator validator in propertyEditor.ValueEditor.Validators)
                            {
                                foreach (ValidationResult result in validator.Validate(propValues[propKey], propPrevalues, propertyEditor))
                                {
                                    result.ErrorMessage = "Item " + (i + 1) + " '" + propType.Name + "' " + result.ErrorMessage;
                                    yield return(result);
                                }
                            }

                            // 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() || (propValues[propKey].Type == JTokenType.Array && !propValues[propKey].HasValues))
                                {
                                    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)
                {
                    return(string.Empty);
                }

                var propertyValue = property.Value.ToString();

                if (string.IsNullOrWhiteSpace(propertyValue))
                {
                    return(string.Empty);
                }

                var value = JsonConvert.DeserializeObject <List <object> >(propertyValue);

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

                // Process value
                PreValueCollection preValues = null;

                for (var i = 0; i < value.Count; i++)
                {
                    var o          = value[i];
                    var propValues = ((JObject)o);

                    // convert from old style (v0.1.1) data format if necessary
                    NestedContentHelper.ConvertItemValueFromV011(propValues, propertyType.DataTypeDefinitionId, ref preValues);

                    var contentType = NestedContentHelper.GetContentTypeFromItem(propValues);
                    if (contentType == null)
                    {
                        continue;
                    }

                    var propValueKeys = propValues.Properties().Select(x => x.Name).ToArray();

                    foreach (var propKey in propValueKeys)
                    {
                        var propType = contentType.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == propKey);
                        if (propType == null)
                        {
                            if (IsSystemPropertyKey(propKey) == false)
                            {
                                // Property missing so just delete the value
                                propValues[propKey] = null;
                            }
                        }
                        else
                        {
                            try
                            {
                                // Create a fake property using the property and 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, dataTypeService);

                                // Store the value back
                                propValues[propKey] = (newValue == null) ? null : JToken.FromObject(newValue);
                            }
                            catch (InvalidOperationException)
                            {
                                // https://github.com/umco/umbraco-nested-content/issues/111
                                // Catch any invalid cast operations as likely means courier failed due to missing
                                // or trashed item so couldn't convert a guid back to an int

                                propValues[propKey] = null;
                            }
                        }
                    }
                }

                // Return the strongly-typed object, Umbraco will handle the JSON serializing/parsing, then Angular can handle it directly
                return(value);
            }