diff --git a/src/Umbraco.Core/Models/Membership/IUser.cs b/src/Umbraco.Core/Models/Membership/IUser.cs index 0b17fea05b..c679181963 100644 --- a/src/Umbraco.Core/Models/Membership/IUser.cs +++ b/src/Umbraco.Core/Models/Membership/IUser.cs @@ -23,7 +23,7 @@ namespace Umbraco.Core.Models.Membership IEnumerable Groups { get; } void RemoveGroup(string group); - + void ClearGroups(); void AddGroup(string group); IEnumerable AllowedSections { get; } diff --git a/src/Umbraco.Core/Models/Membership/User.cs b/src/Umbraco.Core/Models/Membership/User.cs index f06c373360..9c444b48e4 100644 --- a/src/Umbraco.Core/Models/Membership/User.cs +++ b/src/Umbraco.Core/Models/Membership/User.cs @@ -78,6 +78,7 @@ namespace Umbraco.Core.Models.Membership if (userGroups == null) throw new ArgumentNullException("userGroups"); if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", "name"); if (string.IsNullOrWhiteSpace(username)) throw new ArgumentException("Value cannot be null or whitespace.", "username"); + if (string.IsNullOrWhiteSpace(rawPasswordValue)) throw new ArgumentException("Value cannot be null or whitespace.", "rawPasswordValue"); Id = id; _name = name; @@ -344,6 +345,15 @@ namespace Umbraco.Core.Models.Membership } } + public void ClearGroups() + { + if (_userGroups.Count > 0) + { + _userGroups.Clear(); + OnPropertyChanged(Ps.Value.UserGroupsSelector); + } + } + public void AddGroup(string group) { if (_userGroups.Contains(group) == false) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 57ed19c00f..384198c39b 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -976,6 +976,7 @@ To manage your website, simply open the Umbraco back office and start adding con Validation Validation errors must be fixed before the item can be saved Failed + Saved Insufficient user permissions, could not complete the operation Cancelled Operation was cancelled by a 3rd party add-in diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs index 73757f5427..0e74de8af1 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -12,6 +12,7 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; @@ -40,7 +41,17 @@ namespace Umbraco.Web.Editors : base(umbracoContext) { } - + + public IHttpActionResult SetAvatar(int id) + { + return Ok(); + } + + public IHttpActionResult ClearAvatar(int id) + { + return Ok(); + } + /// /// Gets a user by Id /// @@ -61,8 +72,8 @@ namespace Umbraco.Web.Editors /// /// public IEnumerable GetUserGroups() - { - return Mapper.Map< IEnumerable, IEnumerable>(Services.UserService.GetAllUserGroups()); + { + return Mapper.Map, IEnumerable>(Services.UserService.GetAllUserGroups()); } /// @@ -111,7 +122,7 @@ namespace Umbraco.Web.Editors { throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } - + var existing = Services.UserService.GetByEmail(userSave.Email); if (existing != null) { @@ -177,7 +188,7 @@ namespace Umbraco.Web.Editors { throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } - + var intId = userSave.Id.TryConvertTo(); if (intId.Success == false) throw new HttpResponseException(HttpStatusCode.NotFound); @@ -189,28 +200,32 @@ namespace Umbraco.Web.Editors var hasErrors = false; var existing = Services.UserService.GetByEmail(userSave.Email); - if (existing != null && existing.Id != (int)userSave.Id) + if (existing != null && existing.Id != userSave.Id) { ModelState.AddModelError("Email", "A user with the email already exists"); hasErrors = true; } existing = Services.UserService.GetByUsername(userSave.Name); - if (existing != null && existing.Id != (int)userSave.Id) + if (existing != null && existing.Id != userSave.Id) { ModelState.AddModelError("Email", "A user with the email already exists"); hasErrors = true; - } + } if (hasErrors) throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); //TODO: More validation, password changing logic, persisting - var user = Mapper.Map(userSave); + //merge the save data onto the user + var user = Mapper.Map(userSave, found); Services.UserService.Save(user); - return Mapper.Map(user); + var display = Mapper.Map(user); + + display.AddSuccessNotification(Services.TextService.Localize("speechBubbles/operationSavedHeader"), Services.TextService.Localize("speechBubbles/editUserSaved")); + return display; } /// @@ -222,7 +237,7 @@ namespace Umbraco.Web.Editors var users = Services.UserService.GetUsersById(userIds).ToArray(); foreach (var u in users) { - u.IsApproved = false; + u.IsApproved = false; } Services.UserService.Save(users); @@ -238,7 +253,7 @@ namespace Umbraco.Web.Editors var users = Services.UserService.GetUsersById(userIds).ToArray(); foreach (var u in users) { - u.IsApproved = true; + u.IsApproved = true; } Services.UserService.Save(users); diff --git a/src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs index e4bbe7850b..29b95e75eb 100644 --- a/src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs @@ -19,6 +19,9 @@ namespace Umbraco.Web.Models.ContentEditing Notifications = new List(); } + [DataMember(Name = "username")] + public string Username { get; set; } + /// /// The MD5 lowercase hash of the email which can be used by gravatar /// diff --git a/src/Umbraco.Web/Models/ContentEditing/UserSave.cs b/src/Umbraco.Web/Models/ContentEditing/UserSave.cs index 8f42dbf169..8e7b247b23 100644 --- a/src/Umbraco.Web/Models/ContentEditing/UserSave.cs +++ b/src/Umbraco.Web/Models/ContentEditing/UserSave.cs @@ -17,7 +17,15 @@ namespace Umbraco.Web.Models.ContentEditing { //TODO: There will be more information to save along with the structure for changing passwords - [DataMember(Name = "locale", IsRequired = true)] + [DataMember(Name = "id", IsRequired = true)] + [Required] + public new int Id { get; set; } + + [DataMember(Name = "username", IsRequired = true)] + [Required] + public string Username { get; set; } + + [DataMember(Name = "culture", IsRequired = true)] [Required] public string Culture { get; set; } diff --git a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs b/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs index 96d52f53fd..731d9037f3 100644 --- a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs @@ -15,7 +15,35 @@ namespace Umbraco.Web.Models.Mapping internal class UserModelMapper : MapperConfiguration { public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext) - { + { + //Used for merging existing UserSave to an existing IUser instance - this will not create an IUser instance! + config.CreateMap() + .ForMember(user => user.Language, expression => expression.MapFrom(save => save.Culture)) + .ForMember(user => user.SessionTimeout, expression => expression.Ignore()) + .ForMember(user => user.SecurityStamp, expression => expression.Ignore()) + .ForMember(user => user.ProviderUserKey, expression => expression.Ignore()) + .ForMember(user => user.RawPasswordValue, expression => expression.Ignore()) + .ForMember(user => user.PasswordQuestion, expression => expression.Ignore()) + .ForMember(user => user.RawPasswordAnswerValue, expression => expression.Ignore()) + .ForMember(user => user.Comments, expression => expression.Ignore()) + .ForMember(user => user.IsApproved, expression => expression.Ignore()) + .ForMember(user => user.IsLockedOut, expression => expression.Ignore()) + .ForMember(user => user.LastLoginDate, expression => expression.Ignore()) + .ForMember(user => user.LastPasswordChangeDate, expression => expression.Ignore()) + .ForMember(user => user.LastLockoutDate, expression => expression.Ignore()) + .ForMember(user => user.FailedPasswordAttempts, expression => expression.Ignore()) + .ForMember(user => user.DeletedDate, expression => expression.Ignore()) + .ForMember(user => user.CreateDate, expression => expression.Ignore()) + .ForMember(user => user.UpdateDate, expression => expression.Ignore()) + .AfterMap((save, user) => + { + user.ClearGroups(); + foreach (var group in save.UserGroups) + { + user.AddGroup(group); + } + }); + config.CreateMap() .ConstructUsing(invite => new User(invite.Name, invite.Email, invite.Email, Guid.NewGuid().ToString("N"))) .ForMember(user => user.Id, expression => expression.Ignore()) @@ -57,7 +85,8 @@ namespace Umbraco.Web.Models.Mapping .ForMember(detail => detail.Path, opt => opt.MapFrom(user => "-1," + user.Id)) .ForMember(detail => detail.AdditionalData, opt => opt.Ignore()); - config.CreateMap() + config.CreateMap() + .ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.Username)) .ForMember(detail => detail.UserGroups, opt => opt.MapFrom(user => user.Groups)) .ForMember(detail => detail.StartContentIds, opt => opt.MapFrom(user => user.StartContentIds)) .ForMember(detail => detail.StartMediaIds, opt => opt.MapFrom(user => user.StartMediaIds)) diff --git a/src/umbraco.businesslogic/User.cs b/src/umbraco.businesslogic/User.cs index 114c77e340..54748f7637 100644 --- a/src/umbraco.businesslogic/User.cs +++ b/src/umbraco.businesslogic/User.cs @@ -648,10 +648,7 @@ namespace umbraco.BusinessLogic public void ClearGroups() { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - foreach (var group in UserEntity.Groups.ToArray()) - { - UserEntity.RemoveGroup(group); - } + UserEntity.ClearGroups(); } ///