private async Task OnActionExecutingAsync(ActionExecutingContext context) { var model = (ContentItemSave?)context.ActionArguments["contentItem"]; var contentItemValidator = new ContentSaveModelValidator(_loggerFactory.CreateLogger <ContentSaveModelValidator>(), _propertyValidationService); if (context.ModelState.ContainsKey("contentItem")) { // if the entire model is marked as error, remove it, we handle everything separately context.ModelState.Remove("contentItem"); } if (!ValidateAtLeastOneVariantIsBeingSaved(model, context)) { return; } if (!contentItemValidator.ValidateExistingContent(model, context)) { return; } if (!await ValidateUserAccessAsync(model, context)) { return; } if (model is not null) { //validate for each variant that is being updated foreach (ContentVariantSave variant in model.Variants.Where(x => x.Save)) { if (contentItemValidator.ValidateProperties(model, variant, context)) { contentItemValidator.ValidatePropertiesData(model, variant, variant.PropertyCollectionDto, context.ModelState); } } } }
public void Validating_ContentItemSave() { var validator = new ContentSaveModelValidator( Factory.GetInstance <ILogger>(), Mock.Of <IUmbracoContextAccessor>(), Factory.GetInstance <ILocalizedTextService>()); var content = MockedContent.CreateTextpageContent(_contentType, "test", -1); var id1 = new Guid("c8df5136-d606-41f0-9134-dea6ae0c2fd9"); var id2 = new Guid("f916104a-4082-48b2-a515-5c4bf2230f38"); var id3 = new Guid("77E15DE9-1C79-47B2-BC60-4913BC4D4C6A"); // TODO: Ok now test with a 4th level complex nested editor var complexValue = @"[{ ""key"": """ + id1.ToString() + @""", ""name"": ""Hello world"", ""ncContentTypeAlias"": """ + ContentTypeAlias + @""", ""title"": ""Hello world"", ""bodyText"": ""The world is round"" }, { ""key"": """ + id2.ToString() + @""", ""name"": ""Super nested"", ""ncContentTypeAlias"": """ + ContentTypeAlias + @""", ""title"": ""Hi there!"", ""bodyText"": ""Well hello there"", ""complex"" : [{ ""key"": """ + id3.ToString() + @""", ""name"": ""I am a sub nested content"", ""ncContentTypeAlias"": """ + ContentTypeAlias + @""", ""title"": ""Hello up there :)"", ""bodyText"": ""Hello way up there on a different level"" }] } ]"; content.SetValue("complex", complexValue); // map the persisted properties to a model representing properties to save //var saveProperties = content.Properties.Select(x => Mapper.Map<ContentPropertyBasic>(x)).ToList(); var saveProperties = content.Properties.Select(x => { return(new ContentPropertyBasic { Alias = x.Alias, Id = x.Id, Value = x.GetValue() }); }).ToList(); var saveVariants = new List <ContentVariantSave> { new ContentVariantSave { Culture = string.Empty, Segment = string.Empty, Name = content.Name, Save = true, Properties = saveProperties } }; var save = new ContentItemSave { Id = content.Id, Action = ContentSaveAction.Save, ContentTypeAlias = _contentType.Alias, ParentId = -1, PersistedContent = content, TemplateAlias = null, Variants = saveVariants }; // This will map the ContentItemSave.Variants.PropertyCollectionDto and then map the values in the saved model // back onto the persisted IContent model. ContentItemBinder.BindModel(save, content); var modelState = new ModelStateDictionary(); var isValid = validator.ValidatePropertiesData(save, saveVariants[0], saveVariants[0].PropertyCollectionDto, modelState); // list results for debugging foreach (var state in modelState) { Console.WriteLine(state.Key); foreach (var error in state.Value.Errors) { Console.WriteLine("\t" + error.ErrorMessage); } } // assert Assert.IsFalse(isValid); Assert.AreEqual(11, modelState.Keys.Count); const string complexPropertyKey = "_Properties.complex.invariant.null"; Assert.IsTrue(modelState.Keys.Contains(complexPropertyKey)); foreach (var state in modelState.Where(x => x.Key != complexPropertyKey)) { foreach (var error in state.Value.Errors) { Assert.IsFalse(error.ErrorMessage.DetectIsJson()); // non complex is just an error message } } var complexEditorErrors = modelState.Single(x => x.Key == complexPropertyKey).Value.Errors; Assert.AreEqual(1, complexEditorErrors.Count); var nestedError = complexEditorErrors[0]; var jsonError = JsonConvert.DeserializeObject <JArray>(nestedError.ErrorMessage); var modelStateKeys = new[] { "_Properties.title.invariant.null.innerFieldId", "_Properties.title.invariant.null.value", "_Properties.bodyText.invariant.null.innerFieldId", "_Properties.bodyText.invariant.null.value" }; AssertNestedValidation(jsonError, 0, id1, modelStateKeys); AssertNestedValidation(jsonError, 1, id2, modelStateKeys.Concat(new[] { "_Properties.complex.invariant.null.innerFieldId", "_Properties.complex.invariant.null.value" }).ToArray()); var nestedJsonError = jsonError.SelectToken("$[1].complex") as JArray; Assert.IsNotNull(nestedJsonError); AssertNestedValidation(nestedJsonError, 0, id3, modelStateKeys); }