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();