From d3b69e04e1e5f5620e18c805d5cf0b265f140f96 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 3 Jul 2013 19:01:37 +1000 Subject: [PATCH] Updated IUserService and added more unit tests for new methods. Fixed unit tests that were failing due to incorrectly set settings... not sure how this is working on the server but oh well. --- .../Interfaces/IUserRepository.cs | 5 +- .../Repositories/UserRepository.cs | 75 ++++++++++++++----- .../Services/IMembershipUserService.cs | 15 ++++ src/Umbraco.Core/Services/IUserService.cs | 21 +++--- src/Umbraco.Core/Services/UserService.cs | 53 ++++++++++++- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../Repositories/UserRepositoryTest.cs | 26 +++++++ .../LegacyExamineBackedMediaTests.cs | 7 +- .../PublishedContent/PublishedMediaTests.cs | 5 +- .../Services/UserServiceTests.cs | 30 +++++++- .../TestHelpers/BaseUmbracoApplicationTest.cs | 5 ++ .../TestHelpers/Entities/MockedUser.cs | 19 ++++- .../TestHelpers/SettingsForTests.cs | 12 +++ 13 files changed, 230 insertions(+), 44 deletions(-) create mode 100644 src/Umbraco.Core/Services/IMembershipUserService.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs index 33d3f6cab8..9355e1cdc2 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Models.Membership; +using System.Collections.Generic; +using Umbraco.Core.Models.Membership; namespace Umbraco.Core.Persistence.Repositories { @@ -12,6 +13,6 @@ namespace Umbraco.Core.Persistence.Repositories /// This is useful when an entire section is removed from config /// /// - void DeleteSectionFromAllUsers(string sectionAlias); + IEnumerable GetUsersAssignedToSection(string sectionAlias); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index 4c4843a78e..032b0e9bf5 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -60,18 +60,8 @@ namespace Umbraco.Core.Persistence.Repositories } 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); - }); + return ConvertFromDtos(Database.Fetch(new UserSectionRelator().Map, sql)); } private IEnumerable PerformGetAllOnIds(params int[] ids) @@ -110,14 +100,22 @@ namespace Umbraco.Core.Persistence.Repositories } else { - sql.Select("*") - .From() - .LeftJoin() - .On(left => left.Id, right => right.UserId); + return GetBaseQuery("*"); } return sql; } + private static Sql GetBaseQuery(string columns) + { + var sql = new Sql(); + sql.Select(columns) + .From() + .LeftJoin() + .On(left => left.Id, right => right.UserId); + return sql; + } + + protected override string GetBaseWhereClause() { return "umbracoUser.id = @Id"; @@ -230,12 +228,53 @@ namespace Umbraco.Core.Persistence.Repositories var query = Query.Builder.Where(x => x.Username == username); return GetByQuery(query).FirstOrDefault(); } - - public void DeleteSectionFromAllUsers(string sectionAlias) + + public IEnumerable GetUsersAssignedToSection(string sectionAlias) { - throw new NotImplementedException(); + //Here we're building up a query that looks like this, a sub query is required because the resulting structure + // needs to still contain all of the section rows per user. + + //SELECT * + //FROM [umbracoUser] + //LEFT JOIN [umbracoUser2app] + //ON [umbracoUser].[id] = [umbracoUser2app].[user] + //WHERE umbracoUser.id IN (SELECT umbracoUser.id + // FROM [umbracoUser] + // LEFT JOIN [umbracoUser2app] + // ON [umbracoUser].[id] = [umbracoUser2app].[user] + // WHERE umbracoUser2app.app = 'content') + + var sql = GetBaseQuery(false); + var innerSql = GetBaseQuery("umbracoUser.id"); + innerSql.Where("umbracoUser2app.app = " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedValue(sectionAlias)); + sql.Where(string.Format("umbracoUser.id IN ({0})", innerSql.SQL)); + + return ConvertFromDtos(Database.Fetch(new UserSectionRelator().Map, sql)); } #endregion + + private IEnumerable ConvertFromDtos(IEnumerable dtos) + { + var foundUserTypes = new Dictionary(); + return dtos.Select(dto => + { + //first we need to get the user type + IUserType userType; + if (foundUserTypes.ContainsKey(dto.Type)) + { + userType = foundUserTypes[dto.Type]; + } + else + { + userType = _userTypeRepository.Get(dto.Type); + //put it in the local cache + foundUserTypes.Add(dto.Type, userType); + } + + var userFactory = new UserFactory(userType); + return userFactory.BuildEntity(dto); + }); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IMembershipUserService.cs b/src/Umbraco.Core/Services/IMembershipUserService.cs new file mode 100644 index 0000000000..5ae7f9af32 --- /dev/null +++ b/src/Umbraco.Core/Services/IMembershipUserService.cs @@ -0,0 +1,15 @@ +using Umbraco.Core.Models.Membership; + +namespace Umbraco.Core.Services +{ + /// + /// Defines part of the UserService, which is specific to methods used by the membership provider. + /// + /// + /// Idea is to have this is an isolated interface so that it can be easily 'replaced' in the membership provider impl. + /// + internal interface IMembershipUserService : IService + { + IMembershipUser CreateMembershipUser(string name, string login, string password, IUserType userType, string email = ""); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IUserService.cs b/src/Umbraco.Core/Services/IUserService.cs index 678fbf9292..8549699c53 100644 --- a/src/Umbraco.Core/Services/IUserService.cs +++ b/src/Umbraco.Core/Services/IUserService.cs @@ -32,16 +32,17 @@ namespace Umbraco.Core.Services /// Name of the UserType to retrieve /// IUserType GetUserTypeByName(string name); - } - /// - /// Defines part of the UserService, which is specific to methods used by the membership provider. - /// - /// - /// Idea is to have this is an isolated interface so that it can be easily 'replaced' in the membership provider impl. - /// - internal interface IMembershipUserService : IService - { - IMembershipUser CreateUser(string name, string login, string password, IUserType userType, string email = ""); + /// + /// Saves changes to the user object + /// + /// + void SaveUser(IUser user); + + /// + /// This is useful when an entire section is removed from config + /// + /// + void DeleteSectionFromAllUsers(string sectionAlias); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index 7836abbcdc..d8fa8f1672 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -1,6 +1,8 @@ using System; using System.Globalization; using System.Linq; +using Umbraco.Core.Events; +using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; @@ -101,6 +103,45 @@ namespace Umbraco.Core.Services } } + /// + /// Savers changes to a user to the database + /// + /// + public void SaveUser(IUser user) + { + if (UserSaving.IsRaisedEventCancelled(new SaveEventArgs(user), this)) + return; + + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateUserRepository(uow)) + { + repository.AddOrUpdate(user); + uow.Commit(); + } + + UserSaved.RaiseEvent(new SaveEventArgs(user, false), this); + } + + /// + /// This is useful for when a section is removed from config + /// + /// + public void DeleteSectionFromAllUsers(string sectionAlias) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateUserRepository(uow)) + { + var assignedUsers = repository.GetUsersAssignedToSection(sectionAlias); + foreach (var user in assignedUsers) + { + //now remove the section for each user and commit + user.RemoveAllowedSection(sectionAlias); + repository.AddOrUpdate(user); + } + uow.Commit(); + } + } + /// /// Creates a new user for logging into the umbraco backoffice /// @@ -110,7 +151,7 @@ namespace Umbraco.Core.Services /// /// /// - public IMembershipUser CreateUser(string name, string login, string password, IUserType userType, string email = "") + public IMembershipUser CreateMembershipUser(string name, string login, string password, IUserType userType, string email = "") { var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateUserRepository(uow)) @@ -142,5 +183,15 @@ namespace Umbraco.Core.Services } #endregion + + /// + /// Occurs before Save + /// + public static event TypedEventHandler> UserSaving; + + /// + /// Occurs after Save + /// + public static event TypedEventHandler> UserSaved; } } \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index d424140a95..05a9fdcabf 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -681,6 +681,7 @@ + diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs index 0e68e2fcad..9476cc76bf 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs @@ -439,6 +439,32 @@ namespace Umbraco.Tests.Persistence.Repositories AssertPropertyValues(userByUsername, user); } + [Test] + public void Get_Users_Assigned_To_Section() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var user1 = MockedUser.CreateUser(CreateAndCommitUserType(), "1", "test", "media"); + var user2 = MockedUser.CreateUser(CreateAndCommitUserType(), "2", "media", "settings"); + var user3 = MockedUser.CreateUser(CreateAndCommitUserType(), "3", "test", "settings"); + repository.AddOrUpdate(user1); + repository.AddOrUpdate(user2); + repository.AddOrUpdate(user3); + unitOfWork.Commit(); + + // Act + + var users = repository.GetUsersAssignedToSection("test"); + + // Assert + Assert.AreEqual(2, users.Count()); + var names = users.Select(x => x.Username).ToArray(); + Assert.IsTrue(names.Contains("TestUser1")); + Assert.IsTrue(names.Contains("TestUser3")); + } + private void AssertPropertyValues(IUser updatedItem, IUser originalUser) { Assert.That(updatedItem.Id, Is.EqualTo(originalUser.Id)); diff --git a/src/Umbraco.Tests/PublishedContent/LegacyExamineBackedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/LegacyExamineBackedMediaTests.cs index 19f3046211..f0fa1bc84f 100644 --- a/src/Umbraco.Tests/PublishedContent/LegacyExamineBackedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/LegacyExamineBackedMediaTests.cs @@ -5,6 +5,7 @@ using Lucene.Net.Documents; using Lucene.Net.Store; using NUnit.Framework; using Umbraco.Core.Configuration; +using Umbraco.Tests.TestHelpers; using Umbraco.Tests.UmbracoExamine; using umbraco.MacroEngines; @@ -15,13 +16,13 @@ namespace Umbraco.Tests.PublishedContent public override void TestSetup() { base.TestSetup(); - UmbracoSettings.ForceSafeAliases = true; - UmbracoSettings.UmbracoLibraryCacheDuration = 1800; - UmbracoSettings.ForceSafeAliases = true; + SettingsForTests.ForceSafeAliases = true; + SettingsForTests.UmbracoLibraryCacheDuration = 1800; } public override void TestTearDown() { + SettingsForTests.Reset(); base.TestTearDown(); } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index 36cba1ad3e..2086b9b5d0 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -38,10 +38,7 @@ namespace Umbraco.Tests.PublishedContent { base.Initialize(); UmbracoExamineSearcher.DisableInitializationCheck = true; - BaseUmbracoIndexer.DisableInitializationCheck = true; - UmbracoSettings.ForceSafeAliases = true; - UmbracoSettings.UmbracoLibraryCacheDuration = 1800; - UmbracoSettings.ForceSafeAliases = true; + BaseUmbracoIndexer.DisableInitializationCheck = true; } public override void TearDown() diff --git a/src/Umbraco.Tests/Services/UserServiceTests.cs b/src/Umbraco.Tests/Services/UserServiceTests.cs index f91f573ec7..245e404f7d 100644 --- a/src/Umbraco.Tests/Services/UserServiceTests.cs +++ b/src/Umbraco.Tests/Services/UserServiceTests.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Security.Cryptography; using System.Text; using NUnit.Framework; @@ -32,7 +33,7 @@ namespace Umbraco.Tests.Services var userType = userService.GetUserTypeByAlias("admin"); // Act - var membershipUser = userService.CreateUser("John Doe", "john@umbraco.io", "12345", userType, "john@umbraco.io"); + var membershipUser = userService.CreateMembershipUser("John Doe", "john@umbraco.io", "12345", userType, "john@umbraco.io"); // Assert Assert.That(membershipUser.HasIdentity, Is.True); @@ -54,7 +55,7 @@ namespace Umbraco.Tests.Services var hash = new HMACSHA1(); hash.Key = Encoding.Unicode.GetBytes(password); var encodedPassword = Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password))); - var membershipUser = userService.CreateUser("John Doe", "john@umbraco.io", encodedPassword, userType, "john@umbraco.io"); + var membershipUser = userService.CreateMembershipUser("John Doe", "john@umbraco.io", encodedPassword, userType, "john@umbraco.io"); // Assert Assert.That(membershipUser.HasIdentity, Is.True); @@ -64,5 +65,30 @@ namespace Umbraco.Tests.Services Assert.That(user, Is.Not.Null); Assert.That(user.Permissions, Is.EqualTo(userType.Permissions)); } + + [Test] + public void Can_Remove_Section_From_All_Assigned_Users() + { + var userType = ServiceContext.UserService.GetUserTypeByAlias("admin"); + //we know this actually is an IUser so we'll just cast + var user1 = (IUser)ServiceContext.UserService.CreateMembershipUser("test1", "test1", "test1", userType, "test1@test.com"); + var user2 = (IUser)ServiceContext.UserService.CreateMembershipUser("test2", "test2", "test2", userType, "test2@test.com"); + + //adds some allowed sections + user1.AddAllowedSection("test"); + user2.AddAllowedSection("test"); + ServiceContext.UserService.SaveUser(user1); + ServiceContext.UserService.SaveUser(user2); + + //now clear the section from all users + ServiceContext.UserService.DeleteSectionFromAllUsers("test"); + + //assert + var result1 = ServiceContext.UserService.GetUserById((int)user1.Id); + var result2 = ServiceContext.UserService.GetUserById((int)user2.Id); + Assert.IsFalse(result1.AllowedSections.Contains("test")); + Assert.IsFalse(result2.AllowedSections.Contains("test")); + + } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs index c297150f4b..dbf87b4aee 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs @@ -18,6 +18,11 @@ namespace Umbraco.Tests.TestHelpers { TestHelper.SetupLog4NetForTests(); TestHelper.InitializeContentDirectories(); + + SettingsForTests.UseLegacyXmlSchema = false; + SettingsForTests.ForceSafeAliases = true; + SettingsForTests.UmbracoLibraryCacheDuration = 1800; + SetupPluginManager(); FreezeResolution(); diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs index be40dd2736..19e0148383 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs @@ -1,10 +1,11 @@ -using Umbraco.Core.Models.Membership; +using System.Linq; +using Umbraco.Core.Models.Membership; namespace Umbraco.Tests.TestHelpers.Entities { public class MockedUser { - internal static User CreateUser(IUserType userType = null, string suffix = "") + internal static User CreateUser(IUserType userType = null, string suffix = "", params string[] allowedSections) { if (userType == null) { @@ -26,8 +27,18 @@ namespace Umbraco.Tests.TestHelpers.Entities Username = "TestUser" + suffix }; - user.AddAllowedSection("content"); - user.AddAllowedSection("media"); + if (allowedSections.Any()) + { + foreach (var s in allowedSections) + { + user.AddAllowedSection(s); + } + } + else + { + user.AddAllowedSection("content"); + user.AddAllowedSection("media"); + } return user; } diff --git a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs index 7dd9e64e52..e0b8bf0bae 100644 --- a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs +++ b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs @@ -11,6 +11,12 @@ namespace Umbraco.Tests.TestHelpers { // umbracoSettings + public static int UmbracoLibraryCacheDuration + { + get { return UmbracoSettings.UmbracoLibraryCacheDuration; } + set { UmbracoSettings.UmbracoLibraryCacheDuration = value; } + } + public static bool UseLegacyXmlSchema { get { return UmbracoSettings.UseLegacyXmlSchema; } @@ -35,6 +41,12 @@ namespace Umbraco.Tests.TestHelpers set { UmbracoSettings.SettingsFilePath = value; } } + public static bool ForceSafeAliases + { + get { return UmbracoSettings.ForceSafeAliases; } + set { UmbracoSettings.ForceSafeAliases = value; } + } + // from appSettings private static readonly IDictionary SavedAppSettings = new Dictionary();