/// <summary> /// This will check if any properties of the model are attributed with the RequiredForPersistenceAttribute attribute and if they are it will /// check if that property validates, if it doesn't it means that the current model cannot be persisted because it doesn't have the necessary information /// to be saved. /// </summary> /// <param name="model"></param> /// <returns></returns> /// <remarks> /// This is normally used for things like content creating when the name is empty since we cannot actually create a content item when the name is empty. /// This is similar but different from the standard Required validator since we still allow content to be saved when validation fails but there are some /// content fields that are absolutely mandatory for creating/saving. /// </remarks> internal static bool ModelHasRequiredForPersistenceErrors(object model) { var requiredForPersistenceProperties = TypeDescriptor.GetProperties(model).Cast<PropertyDescriptor>() .Where(x => x.Attributes.Cast<Attribute>().Any(a => a is RequiredForPersistenceAttribute)); var validator = new RequiredForPersistenceAttribute(); return requiredForPersistenceProperties.Any(p => !validator.IsValid(p.GetValue(model))); }
/// <summary> /// This will check if any properties of the model are attributed with the RequiredForPersistenceAttribute attribute and if they are it will /// check if that property validates, if it doesn't it means that the current model cannot be persisted because it doesn't have the necessary information /// to be saved. /// </summary> /// <param name="model"></param> /// <returns></returns> /// <remarks> /// This is normally used for things like content creating when the name is empty since we cannot actually create a content item when the name is empty. /// This is similar but different from the standard Required validator since we still allow content to be saved when validation fails but there are some /// content fields that are absolutely mandatory for creating/saving. /// </remarks> internal static bool ModelHasRequiredForPersistenceErrors(object model) { var requiredForPersistenceProperties = TypeDescriptor.GetProperties(model).Cast <PropertyDescriptor>() .Where(x => x.Attributes.Cast <Attribute>().Any(a => a is RequiredForPersistenceAttribute)); var validator = new RequiredForPersistenceAttribute(); return(requiredForPersistenceProperties.Any(p => !validator.IsValid(p.GetValue(model)))); }
public ActionResult <MediaItemDisplay> PostSave( [ModelBinder(typeof(MediaItemBinder))] MediaItemSave contentItem) { //Recent versions of IE/Edge may send in the full client side file path instead of just the file name. //To ensure similar behavior across all browsers no matter what they do - we strip the FileName property of all //uploaded files to being *only* the actual file name (as it should be). if (contentItem.UploadedFiles != null && contentItem.UploadedFiles.Any()) { foreach (var file in contentItem.UploadedFiles) { file.FileName = Path.GetFileName(file.FileName); } } //If we've reached here it means: // * Our model has been bound // * and validated // * any file attachments have been saved to their temporary location for us to use // * we have a reference to the DTO object and the persisted object // * Permissions are valid //Don't update the name if it is empty if (contentItem.Name.IsNullOrWhiteSpace() == false) { contentItem.PersistedContent.Name = contentItem.Name; } MapPropertyValuesForPersistence <IMedia, MediaItemSave>( contentItem, contentItem.PropertyCollectionDto, (save, property) => property.GetValue(), //get prop val (save, property, v) => property.SetValue(v), //set prop val null); // media are all invariant //we will continue to save if model state is invalid, however we cannot save if critical data is missing. //TODO: Allowing media to be saved when it is invalid is odd - media doesn't have a publish phase so suddenly invalid data is allowed to be 'live' if (!ModelState.IsValid) { //check for critical data validation issues, we can't continue saving if this data is invalid if (!RequiredForPersistenceAttribute.HasRequiredValuesForPersistence(contentItem)) { //ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue! // add the model state to the outgoing object and throw validation response MediaItemDisplay forDisplay = _umbracoMapper.Map <MediaItemDisplay>(contentItem.PersistedContent); return(ValidationProblem(forDisplay, ModelState)); } } //save the item var saveStatus = _mediaService.Save(contentItem.PersistedContent, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(Constants.Security.SuperUserId)); //return the updated model var display = _umbracoMapper.Map <MediaItemDisplay>(contentItem.PersistedContent); //lastly, if it is not valid, add the model state to the outgoing object and throw a 403 if (!ModelState.IsValid) { return(ValidationProblem(display, ModelState, StatusCodes.Status403Forbidden)); } //put the correct msgs in switch (contentItem.Action) { case ContentSaveAction.Save: case ContentSaveAction.SaveNew: if (saveStatus.Success) { display.AddSuccessNotification( _localizedTextService.Localize("speechBubbles", "editMediaSaved"), _localizedTextService.Localize("speechBubbles", "editMediaSavedText")); } else { AddCancelMessage(display); //If the item is new and the operation was cancelled, we need to return a different // status code so the UI can handle it since it won't be able to redirect since there // is no Id to redirect to! if (saveStatus.Result.Result == OperationResultType.FailedCancelledByEvent && IsCreatingAction(contentItem.Action)) { return(ValidationProblem(display)); } } break; } return(display); }
public MediaItemDisplay PostSave( [ModelBinder(typeof(MediaItemBinder))] MediaItemSave contentItem) { //Recent versions of IE/Edge may send in the full clientside file path instead of just the file name. //To ensure similar behavior across all browsers no matter what they do - we strip the FileName property of all //uploaded files to being *only* the actual file name (as it should be). if (contentItem.UploadedFiles != null && contentItem.UploadedFiles.Any()) { foreach (var file in contentItem.UploadedFiles) { file.FileName = Path.GetFileName(file.FileName); } } //If we've reached here it means: // * Our model has been bound // * and validated // * any file attachments have been saved to their temporary location for us to use // * we have a reference to the DTO object and the persisted object // * Permissions are valid //Don't update the name if it is empty if (contentItem.Name.IsNullOrWhiteSpace() == false) { contentItem.PersistedContent.Name = contentItem.Name; } MapPropertyValuesForPersistence <IMedia, MediaItemSave>( contentItem, contentItem.PropertyCollectionDto, (save, property) => property.GetValue(), //get prop val (save, property, v) => property.SetValue(v), //set prop val null); // media are all invariant //We need to manually check the validation results here because: // * We still need to save the entity even if there are validation value errors // * Depending on if the entity is new, and if there are non property validation errors (i.e. the name is null) // then we cannot continue saving, we can only display errors // * If there are validation errors and they were attempting to publish, we can only save, NOT publish and display // a message indicating this if (ModelState.IsValid == false) { if (!RequiredForPersistenceAttribute.HasRequiredValuesForPersistence(contentItem) && (contentItem.Action == ContentSaveAction.SaveNew)) { //ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue! // add the modelstate to the outgoing object and throw validation response var forDisplay = Mapper.Map <MediaItemDisplay>(contentItem.PersistedContent); forDisplay.Errors = ModelState.ToErrorDictionary(); throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay)); } } //save the item var saveStatus = Services.MediaService.Save(contentItem.PersistedContent, (int)Security.CurrentUser.Id); //return the updated model var display = Mapper.Map <MediaItemDisplay>(contentItem.PersistedContent); //lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403 HandleInvalidModelState(display); //put the correct msgs in switch (contentItem.Action) { case ContentSaveAction.Save: case ContentSaveAction.SaveNew: if (saveStatus.Success) { display.AddSuccessNotification( Services.TextService.Localize("speechBubbles/editMediaSaved"), Services.TextService.Localize("speechBubbles/editMediaSavedText")); } else { AddCancelMessage(display); //If the item is new and the operation was cancelled, we need to return a different // status code so the UI can handle it since it won't be able to redirect since there // is no Id to redirect to! if (saveStatus.Result.Result == OperationResultType.FailedCancelledByEvent && IsCreatingAction(contentItem.Action)) { throw new HttpResponseException(Request.CreateValidationErrorResponse(display)); } } break; } return(display); }