using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web.Mvc; namespace Umbraco.Web { internal static class ModelStateExtensions { /// /// Merges ModelState that has names matching the prefix /// /// /// /// public static void Merge(this ModelStateDictionary state, ModelStateDictionary dictionary, string prefix) { if (dictionary == null) return; foreach (var keyValuePair in dictionary //It can either equal the prefix exactly (model level errors) or start with the prefix. (property level errors) .Where(keyValuePair => keyValuePair.Key == prefix || keyValuePair.Key.StartsWith(prefix + "."))) { state[keyValuePair.Key] = keyValuePair.Value; } } /// /// Checks if there are any model errors on any fields containing the prefix /// /// /// /// public static bool IsValid(this ModelStateDictionary state, string prefix) { return state.Where(v => v.Key.StartsWith(prefix + ".")).All(v => !v.Value.Errors.Any()); } //NOTE: we used this alot in v5 when we had editors in MVC, this was really handy for knockout editors using JS ///// ///// Adds an error to the model state that has to do with data validation, this is generally used for JSON responses ///// ///// ///// //public static void AddDataValidationError(this ModelStateDictionary state, string errorMessage) //{ // state.AddModelError("DataValidation", errorMessage); //} /// /// Adds the error to model state correctly for a property so we can use it on the client side. /// /// /// /// internal static void AddPropertyError(this System.Web.Http.ModelBinding.ModelStateDictionary modelState, ValidationResult result, string propertyAlias) { modelState.AddValidationError(result, "_Properties", propertyAlias); } internal static void AddValidationError(this System.Web.Http.ModelBinding.ModelStateDictionary modelState, ValidationResult result, string prefix, string owner) { // if there are assigned member names, we combine the member name with the owner name // so that we can try to match it up to a real field. otherwise, we assume that the // validation message is for the overall owner. var withNames = false; foreach (var memberName in result.MemberNames) { modelState.AddModelError($"{prefix}.{owner}.{memberName}", result.ErrorMessage); withNames = true; } if (!withNames) modelState.AddModelError($"{prefix}.{owner}", result.ErrorMessage); } public static IDictionary ToErrorDictionary(this System.Web.Http.ModelBinding.ModelStateDictionary modelState) { var modelStateError = new Dictionary(); foreach (var keyModelStatePair in modelState) { var key = keyModelStatePair.Key; var errors = keyModelStatePair.Value.Errors; if (errors != null && errors.Count > 0) { modelStateError.Add(key, errors.Select(error => error.ErrorMessage)); } } return modelStateError; } /// /// Serializes the ModelState to JSON for JavaScript to interrogate the errors /// /// /// public static JsonResult ToJsonErrors(this ModelStateDictionary state) { return new JsonResult { Data = new { success = state.IsValid.ToString().ToLower(), failureType = "ValidationError", validationErrors = from e in state where e.Value.Errors.Count > 0 select new { name = e.Key, errors = e.Value.Errors.Select(x => x.ErrorMessage) .Concat( e.Value.Errors.Where(x => x.Exception != null).Select(x => x.Exception.Message)) } } }; } } }