diff --git a/src/Umbraco.Core/Models/EntityBase/TracksChangesEntityBase.cs b/src/Umbraco.Core/Models/EntityBase/TracksChangesEntityBase.cs index 74562bc250..dc65c583bc 100644 --- a/src/Umbraco.Core/Models/EntityBase/TracksChangesEntityBase.cs +++ b/src/Umbraco.Core/Models/EntityBase/TracksChangesEntityBase.cs @@ -108,7 +108,7 @@ namespace Umbraco.Core.Models.EntityBase /// Please note that resetting the dirty properties could potentially /// obstruct the saving of a new or updated entity. /// - internal void ResetDirtyProperties(bool rememberPreviouslyChangedProperties) + internal virtual void ResetDirtyProperties(bool rememberPreviouslyChangedProperties) { if (rememberPreviouslyChangedProperties) { diff --git a/src/Umbraco.Core/Models/Membership/UserProfile.cs b/src/Umbraco.Core/Models/Membership/UserProfile.cs index a75d89d870..822692546d 100644 --- a/src/Umbraco.Core/Models/Membership/UserProfile.cs +++ b/src/Umbraco.Core/Models/Membership/UserProfile.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Collections.Specialized; using System.Linq; using System.Reflection; using System.Runtime.Serialization; @@ -22,9 +23,13 @@ namespace Umbraco.Core.Models.Membership { SessionTimeout = 60; _sectionCollection = new ObservableCollection(); + _addedSections = new List(); + _removedSections = new List(); _sectionCollection.CollectionChanged += SectionCollectionChanged; } + private readonly List _addedSections; + private readonly List _removedSections; private readonly ObservableCollection _sectionCollection; private int _sessionTimeout; private int _startContentId; @@ -116,17 +121,68 @@ namespace Umbraco.Core.Models.Membership public void AddAllowedSection(string sectionAlias) { - _sectionCollection.Add(sectionAlias); + if (!_sectionCollection.Contains(sectionAlias)) + { + _sectionCollection.Add(sectionAlias); + } } + /// + /// Whenever resetting occurs, clear the remembered add/removed collections, even if + /// rememberPreviouslyChangedProperties is true, the AllowedSections property will still + /// be flagged as dirty. + /// + /// + internal override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties) + { + _addedSections.Clear(); + _removedSections.Clear(); + base.ResetDirtyProperties(rememberPreviouslyChangedProperties); + } + + /// + /// Used internally to check if we need to add a section in the repository to the db + /// + internal IEnumerable AddedSections + { + get { return _addedSections; } + } + + /// + /// Used internally to check if we need to remove a section in the repository to the db + /// + internal IEnumerable RemovedSections + { + get { return _removedSections; } + } + /// /// Handles the collection changed event in order for us to flag the AllowedSections property as changed /// /// /// - void SectionCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) - { + void SectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { OnPropertyChanged(AllowedSectionsSelector); + + if (e.Action == NotifyCollectionChangedAction.Add) + { + //remove from the removed/added sections (since people could add/remove all they want in one request) + _removedSections.RemoveAll(s => s == e.NewItems.Cast().First()); + _addedSections.RemoveAll(s => s == e.NewItems.Cast().First()); + + //add to the added sections + _addedSections.Add(e.NewItems.Cast().First()); + } + else if (e.Action == NotifyCollectionChangedAction.Remove) + { + //remove from the removed/added sections (since people could add/remove all they want in one request) + _removedSections.RemoveAll(s => s == e.OldItems.Cast().First()); + _addedSections.RemoveAll(s => s == e.OldItems.Cast().First()); + + //add to the added sections + _removedSections.Add(e.OldItems.Cast().First()); + } } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/UserDto.cs b/src/Umbraco.Core/Models/Rdbms/UserDto.cs index 091838749f..6958bd5d37 100644 --- a/src/Umbraco.Core/Models/Rdbms/UserDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/UserDto.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Persistence; +using System.Collections.Generic; +using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseAnnotations; namespace Umbraco.Core.Models.Rdbms @@ -59,5 +60,8 @@ namespace Umbraco.Core.Models.Rdbms [Column("defaultToLiveEditing")] [Constraint(Default = "0")] public bool DefaultToLiveEditing { get; set; } + + [ResultColumn] + public List User2AppDtos { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Factories/UserFactory.cs b/src/Umbraco.Core/Persistence/Factories/UserFactory.cs index 8f1ab387ca..6d38db9739 100644 --- a/src/Umbraco.Core/Persistence/Factories/UserFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/UserFactory.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Models.Membership; +using System.Collections.Generic; +using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; namespace Umbraco.Core.Persistence.Factories @@ -16,8 +17,7 @@ namespace Umbraco.Core.Persistence.Factories public IUser BuildEntity(UserDto dto) { - //TODO Add list of applications for user - return new User(_userType) + var user = new User(_userType) { Id = dto.Id, ProfileId = dto.Id, @@ -34,6 +34,17 @@ namespace Umbraco.Core.Persistence.Factories NoConsole = dto.NoConsole, Permissions = dto.DefaultPermissions }; + + foreach (var app in dto.User2AppDtos) + { + user.AddAllowedSection(app.AppAlias); + } + + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + user.ResetDirtyProperties(false); + + return user; } public UserDto BuildDto(IUser entity) @@ -51,9 +62,24 @@ namespace Umbraco.Core.Persistence.Factories UserLanguage = entity.Language, UserName = entity.Name, Type = short.Parse(entity.UserType.Id.ToString()), - DefaultPermissions = entity.Permissions + DefaultPermissions = entity.Permissions, + User2AppDtos = new List() }; + foreach (var app in entity.AllowedSections) + { + var appDto = new User2AppDto + { + AppAlias = app + }; + if (entity.Id != null) + { + appDto.UserId = (int) entity.Id; + } + + dto.User2AppDtos.Add(appDto); + } + if (entity.HasIdentity) dto.Id = entity.Id.SafeCast(); diff --git a/src/Umbraco.Core/Persistence/Relators/UserSectionRelator.cs b/src/Umbraco.Core/Persistence/Relators/UserSectionRelator.cs new file mode 100644 index 0000000000..0b79bfde26 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Relators/UserSectionRelator.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Relators +{ + internal class UserSectionRelator + { + internal UserDto Current; + + internal UserDto Map(UserDto a, User2AppDto p) + { + // Terminating call. Since we can return null from this function + // we need to be ready for PetaPoco to callback later with null + // parameters + if (a == null) + return Current; + + // Is this the same DictionaryItem as the current one we're processing + if (Current != null && Current.Id == a.Id) + { + // Yes, just add this User2AppDto to the current item's collection + Current.User2AppDtos.Add(p); + + // Return null to indicate we're not done with this User yet + return null; + } + + // This is a different User to the current one, or this is the + // first time through and we don't have one yet + + // Save the current User + var prev = Current; + + // Setup the new current User + Current = a; + Current.User2AppDtos = new List(); + //this can be null since we are doing a left join + if (p.AppAlias != null) + { + Current.User2AppDtos.Add(p); + } + + // Return the now populated previous User (or null if first time through) + return prev; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs index 2832ed73c6..98f8434047 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs @@ -103,7 +103,7 @@ namespace Umbraco.Core.Persistence.Repositories { //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 - Entity asEntity = entity as Entity; + TracksChangesEntityBase asEntity = entity as TracksChangesEntityBase; if (asEntity != null) { asEntity.ResetDirtyProperties(false); diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index 93ea6ca377..ef5e884662 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -8,6 +8,8 @@ using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Caching; using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.Relators; +using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories @@ -38,8 +40,8 @@ namespace Umbraco.Core.Persistence.Repositories var sql = GetBaseQuery(false); sql.Where(GetBaseWhereClause(), new { Id = id }); - var dto = Database.FirstOrDefault(sql); - + var dto = Database.Fetch(new UserSectionRelator().Map, sql).FirstOrDefault(); + if (dto == null) return null; @@ -54,18 +56,30 @@ namespace Umbraco.Core.Persistence.Repositories { if (ids.Any()) { - foreach (var id in ids) - { - yield return Get(id); - } + return PerformGetAllOnIds(ids); } - else + + var sql = GetBaseQuery(false); + var foundUserTypes = new Dictionary(); + return Database.Fetch(new UserSectionRelator().Map, sql) + .Select(dto => + { + //first we need to get the user type + var userType = foundUserTypes.ContainsKey(dto.Type) + ? foundUserTypes[dto.Type] + : _userTypeRepository.Get(dto.Type); + + var userFactory = new UserFactory(userType); + return userFactory.BuildEntity(dto); + }); + } + + private IEnumerable PerformGetAllOnIds(params int[] ids) + { + if (ids.Any() == false) yield break; + foreach (var id in ids) { - var userDtos = Database.Fetch("WHERE id >= 0"); - foreach (var userDto in userDtos) - { - yield return Get(userDto.Id); - } + yield return Get(id); } } @@ -75,24 +89,33 @@ namespace Umbraco.Core.Persistence.Repositories var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate(); - var dtos = Database.Fetch(sql); + var dtos = Database.Fetch(new UserSectionRelator().Map, sql); foreach (var dto in dtos.DistinctBy(x => x.Id)) { yield return Get(dto.Id); } } - + #endregion #region Overrides of PetaPocoRepositoryBase - + protected override Sql GetBaseQuery(bool isCount) { var sql = new Sql(); - sql.Select(isCount ? "COUNT(*)" : "*") - .From(); - return sql; + if (isCount) + { + sql.Select("COUNT(*)").From(); + } + else + { + sql.Select("*") + .From() + .LeftJoin() + .On(left => left.Id, right => right.UserId); + } + return sql; } protected override string GetBaseWhereClause() @@ -104,6 +127,7 @@ namespace Umbraco.Core.Persistence.Repositories { var list = new List { + "DELETE FROM umbracoUser2app WHERE " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("user") + "=@Id", "DELETE FROM umbracoUser WHERE id = @Id" }; return list; @@ -113,7 +137,7 @@ namespace Umbraco.Core.Persistence.Repositories { get { throw new NotImplementedException(); } } - + protected override void PersistNewItem(IUser entity) { var userFactory = new UserFactory(entity.UserType); @@ -122,12 +146,10 @@ namespace Umbraco.Core.Persistence.Repositories var id = Convert.ToInt32(Database.Insert(userDto)); entity.Id = id; - var sectionFactory = new UserSectionFactory(entity); - var sectionDtos = sectionFactory.BuildDto(entity.AllowedSections); - foreach (var sectionDto in sectionDtos) + foreach (var sectionDto in userDto.User2AppDtos) { - //just insert the record, we don't need to return a primary key since we already - //know what it is since it is a composite key (and not an identity) + //need to set the id explicitly here + sectionDto.UserId = id; Database.Insert(sectionDto); } @@ -140,6 +162,37 @@ namespace Umbraco.Core.Persistence.Repositories var userDto = userFactory.BuildDto(entity); Database.Update(userDto); + + //update the sections if they've changed + var user = (User) entity; + if (user.IsPropertyDirty("AllowedSections")) + { + //for any that exist on the object, we need to determine if we need to update or insert + foreach (var sectionDto in userDto.User2AppDtos) + { + if (user.AddedSections.Contains(sectionDto.AppAlias)) + { + //we need to insert since this was added + Database.Insert(sectionDto); + } + else + { + //we need to manually update this record because it has a composite key + Database.Update("SET app=@Section WHERE app=@Section AND " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("user") + "=@UserId", + new { Section = sectionDto.AppAlias, UserId = sectionDto.UserId }); + } + } + + //now we need to delete any applications that have been removed + foreach (var section in user.RemovedSections) + { + //we need to manually delete thsi record because it has a composite key + Database.Delete("WHERE app=@Section AND " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("user") + "=@UserId", + new { Section = section, UserId = (int) user.Id }); + } + } + + ((ICanBeDirty)entity).ResetDirtyProperties(); } #endregion diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index fd689c6dfa..2b6a23e1cd 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -481,6 +481,7 @@ + diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs index 1c09e3f25c..b7717c6a23 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs @@ -15,7 +15,7 @@ using umbraco.interfaces; namespace Umbraco.Tests.Persistence.Repositories { - [TestFixture] + [TestFixture] public class ContentRepositoryTest : BaseDatabaseFactoryTest { [SetUp] diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs new file mode 100644 index 0000000000..333c051413 --- /dev/null +++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs @@ -0,0 +1,390 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Entities; + +namespace Umbraco.Tests.Persistence.Repositories +{ + [TestFixture] + public class UserRepositoryTest : BaseDatabaseFactoryTest + { + [SetUp] + public override void Initialize() + { + base.Initialize(); + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); + } + + [Test] + public void Can_Instantiate_Repository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + + // Act + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + + // Assert + Assert.That(repository, Is.Not.Null); + } + + [Test] + public void Can_Perform_Add_On_UserRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + + var user = MockedUser.CreateUser(CreateAndCommitUserType()); + + // Act + repository.AddOrUpdate(user); + unitOfWork.Commit(); + + // Assert + Assert.That(user.HasIdentity, Is.True); + } + + [Test] + public void Can_Perform_Multiple_Adds_On_UserRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + + var user1 = MockedUser.CreateUser(CreateAndCommitUserType(), "1"); + var use2 = MockedUser.CreateUser(CreateAndCommitUserType(), "2"); + + // Act + repository.AddOrUpdate(user1); + unitOfWork.Commit(); + repository.AddOrUpdate(use2); + unitOfWork.Commit(); + + // Assert + Assert.That(user1.HasIdentity, Is.True); + Assert.That(use2.HasIdentity, Is.True); + } + + [Test] + public void Can_Verify_Fresh_Entity_Is_Not_Dirty() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var user = MockedUser.CreateUser(CreateAndCommitUserType()); + repository.AddOrUpdate(user); + unitOfWork.Commit(); + + // Act + var resolved = repository.Get((int)user.Id); + bool dirty = ((User)resolved).IsDirty(); + + // Assert + Assert.That(dirty, Is.False); + } + + [Test] + public void Can_Perform_Update_On_UserRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var user = MockedUser.CreateUser(CreateAndCommitUserType()); + repository.AddOrUpdate(user); + unitOfWork.Commit(); + + // Act + var resolved = repository.Get((int)user.Id); + + resolved.Name = "New Name"; + resolved.Permissions = "ZYX"; + resolved.Language = "fr"; + resolved.IsApproved = false; + resolved.Password = "new"; + resolved.NoConsole = true; + resolved.StartContentId = 10; + resolved.StartMediaId = 11; + resolved.DefaultToLiveEditing = true; + resolved.Email = "new@new.com"; + resolved.Username = "newName"; + resolved.RemoveAllowedSection("content"); + + repository.AddOrUpdate(resolved); + unitOfWork.Commit(); + var updatedItem = repository.Get((int)user.Id); + + // Assert + Assert.That(updatedItem.Id, Is.EqualTo(resolved.Id)); + Assert.That(updatedItem.Name, Is.EqualTo(resolved.Name)); + Assert.That(updatedItem.Permissions, Is.EqualTo(resolved.Permissions)); + Assert.That(updatedItem.Language, Is.EqualTo(resolved.Language)); + Assert.That(updatedItem.IsApproved, Is.EqualTo(resolved.IsApproved)); + Assert.That(updatedItem.Password, Is.EqualTo(resolved.Password)); + Assert.That(updatedItem.NoConsole, Is.EqualTo(resolved.NoConsole)); + Assert.That(updatedItem.StartContentId, Is.EqualTo(resolved.StartContentId)); + Assert.That(updatedItem.StartMediaId, Is.EqualTo(resolved.StartMediaId)); + Assert.That(updatedItem.DefaultToLiveEditing, Is.EqualTo(resolved.DefaultToLiveEditing)); + Assert.That(updatedItem.Email, Is.EqualTo(resolved.Email)); + Assert.That(updatedItem.Username, Is.EqualTo(resolved.Username)); + Assert.That(updatedItem.AllowedSections.Count(), Is.EqualTo(1)); + Assert.IsTrue(updatedItem.AllowedSections.Contains("media")); + } + + [Test] + public void Can_Perform_Delete_On_UserRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + + var user = MockedUser.CreateUser(CreateAndCommitUserType()); + + // Act + repository.AddOrUpdate(user); + unitOfWork.Commit(); + var id = user.Id; + + var repository2 = RepositoryResolver.Current.ResolveByType(unitOfWork); + repository2.Delete(user); + unitOfWork.Commit(); + + var resolved = repository2.Get((int)id); + + // Assert + Assert.That(resolved, Is.Null); + } + + [Test] + public void Can_Perform_Get_On_UserRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var user = MockedUser.CreateUser(CreateAndCommitUserType()); + repository.AddOrUpdate(user); + unitOfWork.Commit(); + + // Act + var updatedItem = repository.Get((int)user.Id); + + // Assert + Assert.That(updatedItem.Id, Is.EqualTo(user.Id)); + Assert.That(updatedItem.Name, Is.EqualTo(user.Name)); + Assert.That(updatedItem.Permissions, Is.EqualTo(user.Permissions)); + Assert.That(updatedItem.Language, Is.EqualTo(user.Language)); + Assert.That(updatedItem.IsApproved, Is.EqualTo(user.IsApproved)); + Assert.That(updatedItem.Password, Is.EqualTo(user.Password)); + Assert.That(updatedItem.NoConsole, Is.EqualTo(user.NoConsole)); + Assert.That(updatedItem.StartContentId, Is.EqualTo(user.StartContentId)); + Assert.That(updatedItem.StartMediaId, Is.EqualTo(user.StartMediaId)); + Assert.That(updatedItem.DefaultToLiveEditing, Is.EqualTo(user.DefaultToLiveEditing)); + Assert.That(updatedItem.Email, Is.EqualTo(user.Email)); + Assert.That(updatedItem.Username, Is.EqualTo(user.Username)); + Assert.That(updatedItem.AllowedSections.Count(), Is.EqualTo(2)); + Assert.IsTrue(updatedItem.AllowedSections.Contains("media")); + Assert.IsTrue(updatedItem.AllowedSections.Contains("content")); + } + + [Test] + public void Can_Perform_GetByQuery_On_UserRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + CreateAndCommitMultipleUsers(repository, unitOfWork); + + // Act + var query = Query.Builder.Where(x => x.Username == "TestUser1"); + var result = repository.GetByQuery(query); + + // Assert + Assert.That(result.Count(), Is.GreaterThanOrEqualTo(1)); + } + + [Test] + public void Can_Perform_GetAll_By_Param_Ids_On_UserRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var users = CreateAndCommitMultipleUsers(repository, unitOfWork); + + // Act + var result = repository.GetAll((int)users[0].Id, (int)users[1].Id); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.Any(), Is.True); + Assert.That(result.Count(), Is.EqualTo(2)); + } + + [Test] + public void Can_Perform_GetAll_On_UserRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + CreateAndCommitMultipleUsers(repository, unitOfWork); + + // Act + var result = repository.GetAll(); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.Any(), Is.True); + Assert.That(result.Count(), Is.GreaterThanOrEqualTo(3)); + } + + [Test] + public void Can_Perform_Exists_On_UserRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var users = CreateAndCommitMultipleUsers(repository, unitOfWork); + + // Act + var exists = repository.Exists((int)users[0].Id); + + // Assert + Assert.That(exists, Is.True); + } + + [Test] + public void Can_Perform_Count_On_UserRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var users = CreateAndCommitMultipleUsers(repository, unitOfWork); + + // Act + var query = Query.Builder.Where(x => x.Username == "TestUser1" || x.Username == "TestUser2"); + var result = repository.Count(query); + + // Assert + Assert.That(result, Is.GreaterThanOrEqualTo(2)); + } + + [Test] + public void Can_Remove_Section_For_User() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var users = CreateAndCommitMultipleUsers(repository, unitOfWork); + + // Act + + //add and remove a few times, this tests the internal collection + users[0].RemoveAllowedSection("content"); + users[0].RemoveAllowedSection("content"); + users[0].AddAllowedSection("content"); + users[0].RemoveAllowedSection("content"); + + users[1].RemoveAllowedSection("media"); + users[1].RemoveAllowedSection("media"); + + repository.AddOrUpdate(users[0]); + repository.AddOrUpdate(users[1]); + unitOfWork.Commit(); + + // Assert + var result = repository.GetAll((int) users[0].Id, (int) users[1].Id).ToArray(); + Assert.AreEqual(1, result[0].AllowedSections.Count()); + Assert.AreEqual("media", result[0].AllowedSections.First()); + Assert.AreEqual(1, result[1].AllowedSections.Count()); + Assert.AreEqual("content", result[1].AllowedSections.First()); + } + + [Test] + public void Can_Add_Section_For_User() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var users = CreateAndCommitMultipleUsers(repository, unitOfWork); + + // Act + + //add and remove a few times, this tests the internal collection + users[0].AddAllowedSection("settings"); + users[0].AddAllowedSection("settings"); + users[0].RemoveAllowedSection("settings"); + users[0].AddAllowedSection("settings"); + + users[1].AddAllowedSection("developer"); + + //add the same even though it's already there + users[2].AddAllowedSection("content"); + + repository.AddOrUpdate(users[0]); + repository.AddOrUpdate(users[1]); + unitOfWork.Commit(); + + // Assert + var result = repository.GetAll((int)users[0].Id, (int)users[1].Id, (int)users[2].Id).ToArray(); + Assert.AreEqual(3, result[0].AllowedSections.Count()); + Assert.IsTrue(result[0].AllowedSections.Contains("content")); + Assert.IsTrue(result[0].AllowedSections.Contains("media")); + Assert.IsTrue(result[0].AllowedSections.Contains("settings")); + Assert.AreEqual(3, result[1].AllowedSections.Count()); + Assert.IsTrue(result[1].AllowedSections.Contains("content")); + Assert.IsTrue(result[1].AllowedSections.Contains("media")); + Assert.IsTrue(result[1].AllowedSections.Contains("developer")); + Assert.AreEqual(2, result[2].AllowedSections.Count()); + Assert.IsTrue(result[1].AllowedSections.Contains("content")); + Assert.IsTrue(result[1].AllowedSections.Contains("media")); + } + + private IUser[] CreateAndCommitMultipleUsers(IUserRepository repository, IUnitOfWork unitOfWork) + { + var user1 = MockedUser.CreateUser(CreateAndCommitUserType(), "1"); + var user2 = MockedUser.CreateUser(CreateAndCommitUserType(), "2"); + var user3 = MockedUser.CreateUser(CreateAndCommitUserType(), "3"); + repository.AddOrUpdate(user1); + repository.AddOrUpdate(user2); + repository.AddOrUpdate(user3); + unitOfWork.Commit(); + return new IUser[] {user1, user2, user3}; + } + + private IUserType CreateAndCommitUserType() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var userType = MockedUserType.CreateUserType(); + repository.AddOrUpdate(userType); + unitOfWork.Commit(); + return userType; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserTypeRepositoryTest.cs new file mode 100644 index 0000000000..d22f52f9f1 --- /dev/null +++ b/src/Umbraco.Tests/Persistence/Repositories/UserTypeRepositoryTest.cs @@ -0,0 +1,272 @@ +using System.Linq; +using NUnit.Framework; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Entities; + +namespace Umbraco.Tests.Persistence.Repositories +{ + [TestFixture] + public class UserTypeRepositoryTest : BaseDatabaseFactoryTest + { + [SetUp] + public override void Initialize() + { + base.Initialize(); + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); + } + + [Test] + public void Can_Instantiate_Repository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + + // Act + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + + // Assert + Assert.That(repository, Is.Not.Null); + } + + [Test] + public void Can_Perform_Add_On_UserTypeRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + + var userType = MockedUserType.CreateUserType(); + + // Act + repository.AddOrUpdate(userType); + unitOfWork.Commit(); + + // Assert + Assert.That(userType.HasIdentity, Is.True); + } + + [Test] + public void Can_Perform_Multiple_Adds_On_UserTypeRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + + var userType1 = MockedUserType.CreateUserType("1"); + var userType2 = MockedUserType.CreateUserType("2"); + + // Act + repository.AddOrUpdate(userType1); + unitOfWork.Commit(); + repository.AddOrUpdate(userType2); + unitOfWork.Commit(); + + // Assert + Assert.That(userType1.HasIdentity, Is.True); + Assert.That(userType2.HasIdentity, Is.True); + } + + [Test] + public void Can_Verify_Fresh_Entity_Is_Not_Dirty() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var userType = MockedUserType.CreateUserType(); + repository.AddOrUpdate(userType); + unitOfWork.Commit(); + + // Act + var resolved = repository.Get(userType.Id); + bool dirty = ((UserType)resolved).IsDirty(); + + // Assert + Assert.That(dirty, Is.False); + } + + [Test] + public void Can_Perform_Update_On_UserTypeRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var userType = MockedUserType.CreateUserType(); + repository.AddOrUpdate(userType); + unitOfWork.Commit(); + + // Act + var resolved = repository.Get(userType.Id); + resolved.Name = "New Name"; + resolved.Permissions = "ZYX"; + repository.AddOrUpdate(resolved); + unitOfWork.Commit(); + var updatedItem = repository.Get(userType.Id); + + // Assert + Assert.That(updatedItem.Id, Is.EqualTo(resolved.Id)); + Assert.That(updatedItem.Name, Is.EqualTo(resolved.Name)); + Assert.That(updatedItem.Permissions, Is.EqualTo(resolved.Permissions)); + } + + [Test] + public void Can_Perform_Delete_On_UserTypeRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + + var userType = MockedUserType.CreateUserType(); + + // Act + repository.AddOrUpdate(userType); + unitOfWork.Commit(); + var id = userType.Id; + + var repository2 = RepositoryResolver.Current.ResolveByType(unitOfWork); + repository2.Delete(userType); + unitOfWork.Commit(); + + var resolved = repository2.Get(id); + + // Assert + Assert.That(resolved, Is.Null); + } + + [Test] + public void Can_Perform_Get_On_UserTypeRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var userType = MockedUserType.CreateUserType(); + repository.AddOrUpdate(userType); + unitOfWork.Commit(); + + // Act + var resolved = repository.Get(userType.Id); + + // Assert + Assert.That(resolved.Id, Is.EqualTo(userType.Id)); + //Assert.That(resolved.CreateDate, Is.GreaterThan(DateTime.MinValue)); + //Assert.That(resolved.UpdateDate, Is.GreaterThan(DateTime.MinValue)); + Assert.That(resolved.Name, Is.EqualTo(userType.Name)); + Assert.That(resolved.Alias, Is.EqualTo(userType.Alias)); + Assert.That(resolved.Permissions, Is.EqualTo(userType.Permissions)); + } + + [Test] + public void Can_Perform_GetByQuery_On_UserTypeRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + CreateAndCommitMultipleUserTypes(repository, unitOfWork); + + // Act + var query = Query.Builder.Where(x => x.Alias == "testUserType1"); + var result = repository.GetByQuery(query); + + // Assert + Assert.That(result.Count(), Is.GreaterThanOrEqualTo(1)); + } + + [Test] + public void Can_Perform_GetAll_By_Param_Ids_On_UserTypeRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var userTypes = CreateAndCommitMultipleUserTypes(repository, unitOfWork); + + // Act + var result = repository.GetAll(userTypes[0].Id, userTypes[1].Id); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.Any(), Is.True); + Assert.That(result.Count(), Is.EqualTo(2)); + } + + [Test] + public void Can_Perform_GetAll_On_UserTypeRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + CreateAndCommitMultipleUserTypes(repository, unitOfWork); + + // Act + var result = repository.GetAll(); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.Any(), Is.True); + Assert.That(result.Count(), Is.GreaterThanOrEqualTo(3)); + } + + [Test] + public void Can_Perform_Exists_On_UserTypeRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var userTypes = CreateAndCommitMultipleUserTypes(repository, unitOfWork); + + // Act + var exists = repository.Exists(userTypes[0].Id); + + // Assert + Assert.That(exists, Is.True); + } + + [Test] + public void Can_Perform_Count_On_UserTypeRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var userTypes = CreateAndCommitMultipleUserTypes(repository, unitOfWork); + + // Act + var query = Query.Builder.Where(x => x.Alias == "testUserType1" || x.Alias == "testUserType2"); + var result = repository.Count(query); + + // Assert + Assert.That(result, Is.GreaterThanOrEqualTo(2)); + } + + private IUserType[] CreateAndCommitMultipleUserTypes(IUserTypeRepository repository, IUnitOfWork unitOfWork) + { + var userType1 = MockedUserType.CreateUserType("1"); + var userType2 = MockedUserType.CreateUserType("2"); + var userType3 = MockedUserType.CreateUserType("3"); + repository.AddOrUpdate(userType1); + repository.AddOrUpdate(userType2); + repository.AddOrUpdate(userType3); + unitOfWork.Commit(); + return new IUserType[] {userType1, userType2, userType3}; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs new file mode 100644 index 0000000000..be40dd2736 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs @@ -0,0 +1,35 @@ +using Umbraco.Core.Models.Membership; + +namespace Umbraco.Tests.TestHelpers.Entities +{ + public class MockedUser + { + internal static User CreateUser(IUserType userType = null, string suffix = "") + { + if (userType == null) + { + userType = MockedUserType.CreateUserType(); + } + + var user = new User(userType) + { + Language = "en", + IsApproved = true, + Name = "TestUser" + suffix, + Password = "testing", + NoConsole = false, + Permissions = "ABC", + StartContentId = -1, + StartMediaId = -1, + DefaultToLiveEditing = false, + Email = "test" + suffix + "@test.com", + Username = "TestUser" + suffix + }; + + user.AddAllowedSection("content"); + user.AddAllowedSection("media"); + + return user; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedUserType.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedUserType.cs new file mode 100644 index 0000000000..47ec3442b6 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedUserType.cs @@ -0,0 +1,17 @@ +using Umbraco.Core.Models.Membership; + +namespace Umbraco.Tests.TestHelpers.Entities +{ + public class MockedUserType + { + internal static UserType CreateUserType(string suffix = "") + { + return new UserType() + { + Alias = "testUserType" + suffix, + Name = "TestUserType" + suffix, + Permissions = "ABC" + }; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index e8f5f09701..17426c2e2b 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -196,6 +196,8 @@ + + @@ -292,6 +294,8 @@ + +