public FluidityEntityDisplayModel ToDisplayModel(FluiditySectionConfig section, FluidityCollectionConfig collection, object entity) { var entityId = entity?.GetPropertyValue(collection.IdProperty); var entityCompositeId = entityId != null ? collection.Alias + "!" + entityId : null; var name = ""; if (collection.NameProperty != null) { name = entity.GetPropertyValue(collection.NameProperty).ToString(); } else if (collection.NameFormat != null) { name = collection.NameFormat(entity); } else { name = entity?.ToString(); } var display = new FluidityEntityDisplayModel { Id = entity?.GetPropertyValue(collection.IdProperty), Name = name, Icon = collection.IconSingular + (collection.IconColor != null ? " color-" + collection.IconColor : ""), Section = section.Alias, Tree = section.Tree.Alias, Collection = collection.Alias, CreateDate = entity != null && collection.DateCreatedProperty != null ? (DateTime)entity.GetPropertyValue(collection.DateCreatedProperty) : DateTime.MinValue, UpdateDate = entity != null && collection.DateModifiedProperty != null ? (DateTime)entity.GetPropertyValue(collection.DateModifiedProperty) : DateTime.MinValue, EditPath = $"{section.Alias}/fluidity/edit/{entityCompositeId}", }; if (collection.ListView != null) { var properties = new List <ContentPropertyBasic>(); foreach (var field in collection.ListView.Fields) { var value = entity?.GetPropertyValue(field.Property); var encryptedProp = collection.EncryptedProperties?.FirstOrDefault(x => x.Name == field.Property.Name); if (encryptedProp != null) { value = SecurityHelper.Decrypt(value.ToString()); } if (field.Format != null) { value = field.Format(value, entity); } var propertyScaffold = new ContentPropertyBasic { Id = properties.Count, Alias = field.Property.Name, Value = value?.ToString() }; properties.Add(propertyScaffold); } display.Properties = properties; } return(display); }
private void Map(Property source, ContentPropertyBasic target, MapperContext context) { // assume this is mapping everything and no MapAll is required _contentPropertyBasicConverter.Map(source, target, context); }
/// <summary> /// Update existing member data /// </summary> /// <param name="contentItem">The member to save</param> /// <remarks> /// We need to use both IMemberService and ASP.NET Identity to do our updates because Identity is responsible for passwords/security. /// When this method is called, the IMember will already have updated/mapped values from the http POST. /// So then we do this in order: /// 1. Deal with sensitive property values on IMember /// 2. Use IMemberService to persist all changes /// 3. Use ASP.NET and MemberUserManager to deal with lockouts /// 4. Use ASP.NET, MemberUserManager and password changer to deal with passwords /// 5. Deal with groups/roles /// </remarks> private async Task <ActionResult <bool> > UpdateMemberAsync(MemberSave contentItem) { contentItem.PersistedContent.WriterId = _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id; // If the user doesn't have access to sensitive values, then we need to check if any of the built in member property types // have been marked as sensitive. If that is the case we cannot change these persisted values no matter what value has been posted. // There's only 3 special ones we need to deal with that are part of the MemberSave instance: Comments, IsApproved, IsLockedOut // but we will take care of this in a generic way below so that it works for all props. if (!_backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser.HasAccessToSensitiveData()) { IMemberType memberType = _memberTypeService.Get(contentItem.PersistedContent.ContentTypeId); var sensitiveProperties = memberType .PropertyTypes.Where(x => memberType.IsSensitiveProperty(x.Alias)) .ToList(); foreach (IPropertyType sensitiveProperty in sensitiveProperties) { // TODO: This logic seems to deviate from the logic that is in v8 where we are explitly checking // against 3 properties: Comments, IsApproved, IsLockedOut, is the v8 version incorrect? ContentPropertyBasic destProp = contentItem.Properties.FirstOrDefault(x => x.Alias == sensitiveProperty.Alias); if (destProp != null) { // if found, change the value of the contentItem model to the persisted value so it remains unchanged object origValue = contentItem.PersistedContent.GetValue(sensitiveProperty.Alias); destProp.Value = origValue; } } } // First save the IMember with mapped values before we start updating data with aspnet identity _memberService.Save(contentItem.PersistedContent); bool needsResync = false; MemberIdentityUser identityMember = await _memberManager.FindByIdAsync(contentItem.Id.ToString()); if (identityMember == null) { return(ValidationProblem("Member was not found")); } // Handle unlocking with the member manager (takes care of other nuances) if (identityMember.IsLockedOut && contentItem.IsLockedOut == false) { IdentityResult unlockResult = await _memberManager.SetLockoutEndDateAsync(identityMember, DateTimeOffset.Now.AddMinutes(-1)); if (unlockResult.Succeeded == false) { return(ValidationProblem( $"Could not unlock for member {contentItem.Id} - error {unlockResult.Errors.ToErrorMessage()}")); } needsResync = true; } else if (identityMember.IsLockedOut == false && contentItem.IsLockedOut) { // NOTE: This should not ever happen unless someone is mucking around with the request data. // An admin cannot simply lock a user, they get locked out by password attempts, but an admin can unlock them return(ValidationProblem("An admin cannot lock a member")); } // If we're changing the password... // Handle changing with the member manager & password changer (takes care of other nuances) if (contentItem.Password != null) { IdentityResult validatePassword = await _memberManager.ValidatePasswordAsync(contentItem.Password.NewPassword); if (validatePassword.Succeeded == false) { return(ValidationProblem(validatePassword.Errors.ToErrorMessage())); } if (!int.TryParse(identityMember.Id, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intId)) { return(ValidationProblem("Member ID was not valid")); } var changingPasswordModel = new ChangingPasswordModel { Id = intId, OldPassword = contentItem.Password.OldPassword, NewPassword = contentItem.Password.NewPassword, }; // Change and persist the password Attempt <PasswordChangedModel> passwordChangeResult = await _passwordChanger.ChangePasswordWithIdentityAsync(changingPasswordModel, _memberManager); if (!passwordChangeResult.Success) { foreach (string memberName in passwordChangeResult.Result?.ChangeError?.MemberNames ?? Enumerable.Empty <string>()) { ModelState.AddModelError(memberName, passwordChangeResult.Result.ChangeError?.ErrorMessage ?? string.Empty); } return(ValidationProblem(ModelState)); } needsResync = true; } // Update the roles and check for changes ActionResult <bool> rolesChanged = await AddOrUpdateRoles(contentItem.Groups, identityMember); if (!rolesChanged.Value && rolesChanged.Result != null) { return(rolesChanged.Result); } else { needsResync = true; } // If there have been underlying changes made by ASP.NET Identity, then we need to resync the // IMember on the PersistedContent with what is stored since it will be mapped to display. if (needsResync) { contentItem.PersistedContent = _memberService.GetById(contentItem.PersistedContent.Id); } return(true); }