From 1643eec62c432434ba37ad0581b0a838ed3d73a1 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 13 Jul 2017 18:47:10 +1000 Subject: [PATCH] Fixes cache refreshing for user groups since user cache need to be cleared for that too. Updates cache refreshers to use json.net for serializing which is way faster. adds better support for querying multiple user groups and entity ids for permissions, updates tests --- .../Models/Membership/EntityPermissionSet.cs | 6 +- .../Repositories/PermissionRepository.cs | 3 + .../Repositories/UserGroupRepository.cs | 8 +- src/Umbraco.Core/Services/IUserService.cs | 24 ++-- src/Umbraco.Core/Services/UserService.cs | 133 ++++++++++++------ .../Services/UserServiceExtensions.cs | 33 ++++- .../Services/ContentServiceTests.cs | 17 ++- .../Services/UserServiceTests.cs | 3 +- .../Cache/CacheRefresherEventHandler.cs | 27 +++- .../Cache/ContentTypeCacheRefresher.cs | 7 +- .../Cache/DataTypeCacheRefresher.cs | 7 +- .../Cache/DistributedCacheExtensions.cs | 19 +++ src/Umbraco.Web/Cache/MacroCacheRefresher.cs | 13 +- src/Umbraco.Web/Cache/MediaCacheRefresher.cs | 13 +- .../Cache/MemberGroupCacheRefresher.cs | 7 +- .../Cache/UnpublishedPageCacheRefresher.cs | 8 +- .../Cache/UserGroupCacheRefresher.cs | 12 +- .../Models/Mapping/UserModelMapper.cs | 2 +- src/umbraco.cms/businesslogic/Permission.cs | 132 +---------------- 19 files changed, 233 insertions(+), 241 deletions(-) diff --git a/src/Umbraco.Core/Models/Membership/EntityPermissionSet.cs b/src/Umbraco.Core/Models/Membership/EntityPermissionSet.cs index 0f35fec990..c33c4aa315 100644 --- a/src/Umbraco.Core/Models/Membership/EntityPermissionSet.cs +++ b/src/Umbraco.Core/Models/Membership/EntityPermissionSet.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; namespace Umbraco.Core.Models.Membership @@ -8,13 +9,14 @@ namespace Umbraco.Core.Models.Membership /// public class EntityPermissionSet { + private static readonly Lazy EmptyInstance = new Lazy(() => new EntityPermissionSet(-1, new EntityPermissionCollection())); /// /// Returns an empty permission set /// /// public static EntityPermissionSet Empty() { - return new EntityPermissionSet(-1, new EntityPermissionCollection()); + return EmptyInstance.Value; } public EntityPermissionSet(int entityId, EntityPermissionCollection permissionsSet) diff --git a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs index 7dc6996b57..edf0274832 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs @@ -135,6 +135,9 @@ namespace Umbraco.Core.Persistence.Repositories /// public void ReplacePermissions(int groupId, IEnumerable permissions, params int[] entityIds) { + if (entityIds.Length == 0) + return; + var db = UnitOfWork.Database; //we need to batch these in groups of 2000 so we don't exceed the max 2100 limit diff --git a/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs index 137cc0885b..3d291d5e07 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs @@ -132,6 +132,10 @@ namespace Umbraco.Core.Persistence.Repositories { foreach (var nodeId in nodeIds) { + //TODO: We could/should change the EntityPermissionsCollection into a KeyedCollection and they key could be + // a struct of the nodeid + groupid so then we don't actually allocate this class just to check if it's not + // going to be included in the result! + var defaultPermission = new EntityPermission(group.Id, nodeId, group.Permissions.ToArray(), isDefaultPermissions: true); //Since this is a hashset, this will not add anything that already exists by group/node combination result.Add(defaultPermission); @@ -145,8 +149,8 @@ namespace Umbraco.Core.Persistence.Repositories /// Replaces the same permission set for a single group to any number of entities /// /// Id of group - /// Permissions as enumerable list of - /// Specify the nodes to replace permissions for. If nothing is specified all permissions are removed. + /// Permissions as enumerable list of If nothing is specified all permissions are removed. + /// Specify the nodes to replace permissions for. public void ReplaceGroupPermissions(int groupId, IEnumerable permissions, params int[] entityIds) { _permissionRepository.ReplacePermissions(groupId, permissions, entityIds); diff --git a/src/Umbraco.Core/Services/IUserService.cs b/src/Umbraco.Core/Services/IUserService.cs index 5ae31f8c0d..ea56264075 100644 --- a/src/Umbraco.Core/Services/IUserService.cs +++ b/src/Umbraco.Core/Services/IUserService.cs @@ -95,35 +95,35 @@ namespace Umbraco.Core.Services /// /// This will return the default permissions for the user's groups for node ids that don't have explicitly defined permissions /// - EntityPermissionCollection GetPermissions(IUser user, params int[] nodeIds); - + EntityPermissionCollection GetPermissions(IUser user, params int[] nodeIds); + /// - /// Get explicitly assigned permissions for a group and optional node Ids + /// Get explicitly assigned permissions for groups and optional node Ids /// - /// Group to retrieve permissions for + /// /// - /// Flag indicating if we want to include the default group permissions for each result if there are not explicit permissions set + /// Flag indicating if we want to include the default group permissions for each result if there are not explicit permissions set /// /// Specifiying nothing will return all permissions for all nodes /// An enumerable list of - EntityPermissionCollection GetPermissions(IUserGroup group, bool fallbackToDefaultPermissions, params int[] nodeIds); + EntityPermissionCollection GetPermissions(IUserGroup[] groups, bool fallbackToDefaultPermissions, params int[] nodeIds); /// /// Gets the implicit/inherited permissions for the user for the given path /// /// User to check permissions for /// Path to check permissions for - EntityPermissionSet GetPermissionsForPath(IUser user, string path); - + EntityPermissionSet GetPermissionsForPath(IUser user, string path); + /// - /// Gets the permissions for the provided group and path + /// Gets the permissions for the provided groups and path /// - /// Group to check permissions for + /// /// Path to check permissions for /// - /// Flag indicating if we want to include the default group permissions for each result if there are not explicit permissions set + /// Flag indicating if we want to include the default group permissions for each result if there are not explicit permissions set /// - EntityPermission GetPermissionsForPath(IUserGroup group, string path, bool fallbackToDefaultPermissions = false); + EntityPermissionSet GetPermissionsForPath(IUserGroup[] groups, string path, bool fallbackToDefaultPermissions = false); /// /// Replaces the same permission set for a single group to any number of entities diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index 24ce233cc2..2d5fff536c 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Data.Common; +using System.Globalization; using System.Linq; using System.Linq.Expressions; using Umbraco.Core.Configuration; @@ -680,15 +681,26 @@ namespace Umbraco.Core.Services /// /// If no 'entityIds' are specified all permissions will be removed for the specified group. /// Id of the group - /// Permissions as enumerable list of - /// Specify the nodes to replace permissions for. If nothing is specified all permissions are removed. + /// Permissions as enumerable list of If nothing is specified all permissions are removed. + /// Specify the nodes to replace permissions for. public void ReplaceUserGroupPermissions(int groupId, IEnumerable permissions, params int[] entityIds) { + if (entityIds.Length == 0) + return; + using (var uow = UowProvider.GetUnitOfWork()) { var repository = RepositoryFactory.CreateUserGroupRepository(uow); repository.ReplaceGroupPermissions(groupId, permissions, entityIds); uow.Commit(); + + uow.Events.Dispatch(UserGroupPermissionsAssigned, this, new SaveEventArgs( + entityIds.Select( + x => new EntityPermission( + groupId, + x, + permissions.Select(p => p.ToString(CultureInfo.InvariantCulture)).ToArray())) + .ToArray(), false)); } } @@ -700,11 +712,22 @@ namespace Umbraco.Core.Services /// Specify the nodes to replace permissions for public void AssignUserGroupPermission(int groupId, char permission, params int[] entityIds) { + if (entityIds.Length == 0) + return; + using (var uow = UowProvider.GetUnitOfWork()) { var repository = RepositoryFactory.CreateUserGroupRepository(uow); repository.AssignGroupPermission(groupId, permission, entityIds); uow.Commit(); + + uow.Events.Dispatch(UserGroupPermissionsAssigned, this, new SaveEventArgs( + entityIds.Select( + x => new EntityPermission( + groupId, + x, + new[] {permission.ToString(CultureInfo.InvariantCulture)})) + .ToArray(), false)); } } @@ -853,45 +876,45 @@ namespace Umbraco.Core.Services var repository = RepositoryFactory.CreateUserGroupRepository(uow); return repository.GetPermissions(user.Groups.ToArray(), true, nodeIds); - } + } } /// /// Get explicitly assigned permissions for a group and optional node Ids /// - /// Group to retrieve permissions for - /// - /// Flag indicating if we want to include the default group permissions for each result if there are not explicit permissions set - /// - /// Specifiying nothing will return all permissions for all nodes - /// An enumerable list of - private IEnumerable GetPermissions(IReadOnlyUserGroup group, bool fallbackToDefaultPermissions, params int[] nodeIds) - { - if (group == null) throw new ArgumentNullException("group"); - - using (var uow = UowProvider.GetUnitOfWork(readOnly: true)) - { - var repository = RepositoryFactory.CreateUserGroupRepository(uow); - return repository.GetPermissions(new[] { group }, fallbackToDefaultPermissions, nodeIds); - } - } - - /// - /// Get explicitly assigned permissions for a group and optional node Ids - /// - /// Group to retrieve permissions for + /// Groups to retrieve permissions for /// /// Flag indicating if we want to include the default group permissions for each result if there are not explicit permissions set /// /// Specifiying nothing will return all permissions for all nodes /// An enumerable list of - public EntityPermissionCollection GetPermissions(IUserGroup group, bool fallbackToDefaultPermissions, params int[] nodeIds) + private IEnumerable GetPermissions(IReadOnlyUserGroup[] groups, bool fallbackToDefaultPermissions, params int[] nodeIds) { - if (group == null) throw new ArgumentNullException("group"); + if (groups == null) throw new ArgumentNullException("group"); + using (var uow = UowProvider.GetUnitOfWork(readOnly: true)) { var repository = RepositoryFactory.CreateUserGroupRepository(uow); - return repository.GetPermissions(new[] { group.ToReadOnlyGroup() }, fallbackToDefaultPermissions, nodeIds); + return repository.GetPermissions(groups, fallbackToDefaultPermissions, nodeIds); + } + } + + /// + /// Get explicitly assigned permissions for a group and optional node Ids + /// + /// + /// + /// Flag indicating if we want to include the default group permissions for each result if there are not explicit permissions set + /// + /// Specifiying nothing will return all permissions for all nodes + /// An enumerable list of + public EntityPermissionCollection GetPermissions(IUserGroup[] groups, bool fallbackToDefaultPermissions, params int[] nodeIds) + { + if (groups == null) throw new ArgumentNullException("groups"); + using (var uow = UowProvider.GetUnitOfWork(readOnly: true)) + { + var repository = RepositoryFactory.CreateUserGroupRepository(uow); + return repository.GetPermissions(groups.Select(x => x.ToReadOnlyGroup()).ToArray(), fallbackToDefaultPermissions, nodeIds); } } @@ -905,36 +928,47 @@ namespace Umbraco.Core.Services var nodeIds = path.GetIdsFromPathReversed(); if (nodeIds.Length == 0) - return EntityPermissionSet.Empty(); - - //collect all permissions structures for all nodes for all groups belonging to the user - var groupPermissions = user.Groups - .Select(g => GetPermissionsForPath(g, nodeIds, fallbackToDefaultPermissions: true)) - .ToArray(); - + return EntityPermissionSet.Empty(); + + //collect all permissions structures for all nodes for all groups belonging to the user + var groupPermissions = GetPermissionsForPath(user.Groups.ToArray(), nodeIds, fallbackToDefaultPermissions: true).ToArray(); + return CalculatePermissionsForPathForUser(groupPermissions, nodeIds); - } - + } + /// /// Gets the permissions for the provided group and path /// - /// Group to check permissions for + /// /// Path to check permissions for /// - /// Flag indicating if we want to include the default group permissions for each result if there are not explicit permissions set + /// Flag indicating if we want to include the default group permissions for each result if there are not explicit permissions set /// /// String indicating permissions for provided user and path - public EntityPermission GetPermissionsForPath(IUserGroup group, string path, bool fallbackToDefaultPermissions = false) + public EntityPermissionSet GetPermissionsForPath(IUserGroup[] groups, string path, bool fallbackToDefaultPermissions = false) { var nodeIds = path.GetIdsFromPathReversed(); - return GetPermissionsForPath(group.ToReadOnlyGroup(), nodeIds, fallbackToDefaultPermissions); + + if (nodeIds.Length == 0) + return EntityPermissionSet.Empty(); + + //collect all permissions structures for all nodes for all groups + var groupPermissions = GetPermissionsForPath(groups.Select(x => x.ToReadOnlyGroup()).ToArray(), nodeIds, fallbackToDefaultPermissions: true).ToArray(); + + return CalculatePermissionsForPathForUser(groupPermissions, nodeIds); } - private EntityPermission GetPermissionsForPath(IReadOnlyUserGroup group, int[] pathIds, bool fallbackToDefaultPermissions = false) + private EntityPermissionCollection GetPermissionsForPath(IReadOnlyUserGroup[] groups, int[] pathIds, bool fallbackToDefaultPermissions = false) { - //get permissions for all nodes in the path - var permissions = GetPermissions(group, fallbackToDefaultPermissions, pathIds); - return GetPermissionsForPathForGroup(permissions, pathIds, fallbackToDefaultPermissions); + if (pathIds.Length == 0) + return new EntityPermissionCollection(Enumerable.Empty()); + + //get permissions for all nodes in the path by group + var permissions = GetPermissions(groups, fallbackToDefaultPermissions, pathIds) + .GroupBy(x => x.UserGroupId); + + return new EntityPermissionCollection( + permissions.Select(x => GetPermissionsForPathForGroup(x, pathIds, fallbackToDefaultPermissions))); } /// @@ -1011,7 +1045,10 @@ namespace Umbraco.Core.Services /// /// Returns the resulting permission set for a group for the path based on all permissions provided for the branch /// - /// The collective set of permissions provided to calculate the resulting permissions set for the path + /// + /// The collective set of permissions provided to calculate the resulting permissions set for the path + /// based on a single group + /// /// Must be ordered deepest to shallowest (right to left) /// /// Flag indicating if we want to include the default group permissions for each result if there are not explicit permissions set @@ -1122,6 +1159,10 @@ namespace Umbraco.Core.Services /// /// Occurs after Delete /// - public static event TypedEventHandler> DeletedUserGroup; + public static event TypedEventHandler> DeletedUserGroup; + + //TODO: still don't know if we need this yet unless we start caching permissions, but that also means we'll need another + // event on the ContentService since there's a method there to modify node permissions too, or we can proxy events if needed. + internal static event TypedEventHandler> UserGroupPermissionsAssigned; } } diff --git a/src/Umbraco.Core/Services/UserServiceExtensions.cs b/src/Umbraco.Core/Services/UserServiceExtensions.cs index 0492a59d03..e53a24527f 100644 --- a/src/Umbraco.Core/Services/UserServiceExtensions.cs +++ b/src/Umbraco.Core/Services/UserServiceExtensions.cs @@ -4,8 +4,37 @@ using Umbraco.Core.Models.Membership; namespace Umbraco.Core.Services { - internal static class UserServiceExtensions + public static class UserServiceExtensions { + /// + /// Get explicitly assigned permissions for a group and optional node Ids + /// + /// + /// + /// + /// Flag indicating if we want to include the default group permissions for each result if there are not explicit permissions set + /// + /// Specifiying nothing will return all permissions for all nodes + /// An enumerable list of + public static EntityPermissionCollection GetPermissions(this IUserService service, IUserGroup group, bool fallbackToDefaultPermissions, params int[] nodeIds) + { + return service.GetPermissions(new[] {group}, fallbackToDefaultPermissions, nodeIds); + } + + /// + /// Gets the permissions for the provided group and path + /// + /// + /// + /// Path to check permissions for + /// + /// Flag indicating if we want to include the default group permissions for each result if there are not explicit permissions set + /// + public static EntityPermissionSet GetPermissionsForPath(this IUserService service, IUserGroup group, string path, bool fallbackToDefaultPermissions = false) + { + return service.GetPermissionsForPath(new[] { group }, path, fallbackToDefaultPermissions); + } + /// /// Remove all permissions for this user group for all nodes specified /// @@ -36,7 +65,7 @@ namespace Umbraco.Core.Services /// To maintain compatibility we have to check the login name if the provider key lookup fails but otherwise /// we'll store the provider user key in the login column. /// - public static IUser CreateUserMappingForCustomProvider(this IUserService userService, MembershipUser member) + internal static IUser CreateUserMappingForCustomProvider(this IUserService userService, MembershipUser member) { if (member == null) throw new ArgumentNullException("member"); diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 43f049adf4..69ff951f26 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -1488,8 +1488,9 @@ namespace Umbraco.Tests.Services //get the permissions and verify var permissions = ServiceContext.UserService.GetPermissionsForPath(userGroup, copy.Path, fallbackToDefaultPermissions: true); - Assert.AreEqual(1, permissions.AssignedPermissions.Length); - Assert.AreEqual("A", permissions.AssignedPermissions[0]); + var allPermissions = permissions.GetAllPermissions().ToArray(); + Assert.AreEqual(1, allPermissions.Length); + Assert.AreEqual("A", allPermissions[0]); } [Test] @@ -1523,9 +1524,10 @@ namespace Umbraco.Tests.Services foreach (var descendant in descendants) { - var permissions = ServiceContext.UserService.GetPermissionsForPath(userGroup, descendant.Path, fallbackToDefaultPermissions:true); - Assert.AreEqual(1, permissions.AssignedPermissions.Length); - Assert.AreEqual("A", permissions.AssignedPermissions[0]); + var permissions = ServiceContext.UserService.GetPermissionsForPath(userGroup, descendant.Path, fallbackToDefaultPermissions: true); + var allPermissions = permissions.GetAllPermissions().ToArray(); + Assert.AreEqual(1, allPermissions.Length); + Assert.AreEqual("A", allPermissions[0]); } //create a new parent with a new permission structure @@ -1542,8 +1544,9 @@ namespace Umbraco.Tests.Services foreach (var descendant in descendants) { var permissions = ServiceContext.UserService.GetPermissionsForPath(userGroup, descendant.Path, fallbackToDefaultPermissions: true); - Assert.AreEqual(1, permissions.AssignedPermissions.Length); - Assert.AreEqual("B", permissions.AssignedPermissions[0]); + var allPermissions = permissions.GetAllPermissions().ToArray(); + Assert.AreEqual(1, allPermissions.Length); + Assert.AreEqual("B", allPermissions[0]); } } diff --git a/src/Umbraco.Tests/Services/UserServiceTests.cs b/src/Umbraco.Tests/Services/UserServiceTests.cs index cdfff6ff0d..dcd572fdd7 100644 --- a/src/Umbraco.Tests/Services/UserServiceTests.cs +++ b/src/Umbraco.Tests/Services/UserServiceTests.cs @@ -432,7 +432,8 @@ namespace Umbraco.Tests.Services var permissions = userService.GetPermissionsForPath(userGroup, child2.Path); //assert - Assert.AreEqual(3, permissions.AssignedPermissions.Length); + var allPermissions = permissions.GetAllPermissions().ToArray(); + Assert.AreEqual(3, allPermissions.Length); } [Test] diff --git a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs index daf405a562..b4e28fb95b 100644 --- a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs +++ b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs @@ -68,6 +68,8 @@ namespace Umbraco.Web.Cache () => UserService.SavedUser -= UserService_SavedUser); Bind(() => UserService.DeletedUser += UserService_DeletedUser, () => UserService.DeletedUser -= UserService_DeletedUser); + Bind(() => UserService.UserGroupPermissionsAssigned += UserService_UserGroupPermissionsAssigned, + () => UserService.UserGroupPermissionsAssigned -= UserService_UserGroupPermissionsAssigned); // bind to dictionary events Bind(() => LocalizationService.DeletedDictionaryItem += LocalizationService_DeletedDictionaryItem, @@ -111,8 +113,10 @@ namespace Umbraco.Web.Cache Bind(() => MemberTypeService.Saved += MemberTypeService_Saved, () => MemberTypeService.Saved -= MemberTypeService_Saved); Bind(() => MemberTypeService.Deleted += MemberTypeService_Deleted, - () => MemberTypeService.Deleted -= MemberTypeService_Deleted); - + () => MemberTypeService.Deleted -= MemberTypeService_Deleted); + + + // bind to template events Bind(() => FileService.SavedTemplate += FileService_SavedTemplate, () => FileService.SavedTemplate -= FileService_SavedTemplate); @@ -548,13 +552,22 @@ namespace Umbraco.Web.Cache static void MemberTypeService_Saved(IMemberTypeService sender, SaveEventArgs e) { e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshMemberTypeCache(x)); - } - - + } + + #endregion - + #region User/permissions event handlers - + + static void UserService_UserGroupPermissionsAssigned(IUserService sender, SaveEventArgs e) + { + //TODO: Not sure if we need this yet depends if we start caching permissions + //var groupIds = e.SavedEntities.Select(x => x.UserGroupId).Distinct(); + //foreach (var groupId in groupIds) + //{ + // DistributedCache.Instance.RefreshUserGroupPermissionsCache(groupId); + //} + } static void UserService_SavedUser(IUserService sender, SaveEventArgs e) { diff --git a/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs index e83050dd26..5e527cdfcb 100644 --- a/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Web.Script.Serialization; +using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Models; @@ -34,8 +35,7 @@ namespace Umbraco.Web.Cache /// internal static JsonPayload[] DeserializeFromJsonPayload(string json) { - var serializer = new JavaScriptSerializer(); - var jsonObject = serializer.Deserialize(json); + var jsonObject = JsonConvert.DeserializeObject(json); return jsonObject; } @@ -79,9 +79,8 @@ namespace Umbraco.Web.Cache /// internal static string SerializeToJsonPayload(bool isDeleted, params IContentTypeBase[] contentTypes) { - var serializer = new JavaScriptSerializer(); var items = contentTypes.Select(x => FromContentType(x, isDeleted)).ToArray(); - var json = serializer.Serialize(items); + var json = JsonConvert.SerializeObject(items); return json; } diff --git a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs index 9d8dfc3332..833855a972 100644 --- a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs @@ -3,6 +3,7 @@ using System.Web.Script.Serialization; using Umbraco.Core; using Umbraco.Core.Cache; using System.Linq; +using Newtonsoft.Json; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors.ValueConverters; @@ -27,8 +28,7 @@ namespace Umbraco.Web.Cache /// private static JsonPayload[] DeserializeFromJsonPayload(string json) { - var serializer = new JavaScriptSerializer(); - var jsonObject = serializer.Deserialize(json); + var jsonObject = JsonConvert.DeserializeObject(json); return jsonObject; } @@ -39,9 +39,8 @@ namespace Umbraco.Web.Cache /// internal static string SerializeToJsonPayload(params IDataTypeDefinition[] dataTypes) { - var serializer = new JavaScriptSerializer(); var items = dataTypes.Select(FromDataTypeDefinition).ToArray(); - var json = serializer.Serialize(items); + var json = JsonConvert.SerializeObject(items); return json; } diff --git a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs index 397410dbd5..849cd6c81a 100644 --- a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs +++ b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs @@ -79,6 +79,25 @@ namespace Umbraco.Web.Cache #endregion + #region User group permissions cache + + public static void RemoveUserGroupPermissionsCache(this DistributedCache dc, int groupId) + { + dc.Remove(DistributedCache.UserGroupPermissionsCacheRefresherGuid, groupId); + } + + public static void RefreshUserGroupPermissionsCache(this DistributedCache dc, int groupId) + { + //TODO: Not sure if we need this yet depends if we start caching permissions + //dc.Refresh(DistributedCache.UserGroupPermissionsCacheRefresherGuid, groupId); + } + + public static void RefreshAllUserGroupPermissionsCache(this DistributedCache dc) + { + dc.RefreshAll(DistributedCache.UserGroupPermissionsCacheRefresherGuid); + } + + #endregion #region Template cache diff --git a/src/Umbraco.Web/Cache/MacroCacheRefresher.cs b/src/Umbraco.Web/Cache/MacroCacheRefresher.cs index cb62dbeb39..4292a30589 100644 --- a/src/Umbraco.Web/Cache/MacroCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/MacroCacheRefresher.cs @@ -8,6 +8,7 @@ using umbraco; using Umbraco.Core.Persistence.Repositories; using umbraco.interfaces; using System.Linq; +using Newtonsoft.Json; using Macro = umbraco.cms.businesslogic.macro.Macro; namespace Umbraco.Web.Cache @@ -47,8 +48,7 @@ namespace Umbraco.Web.Cache /// private static JsonPayload[] DeserializeFromJsonPayload(string json) { - var serializer = new JavaScriptSerializer(); - var jsonObject = serializer.Deserialize(json); + var jsonObject = JsonConvert.DeserializeObject(json); return jsonObject; } @@ -59,9 +59,8 @@ namespace Umbraco.Web.Cache /// internal static string SerializeToJsonPayload(params Macro[] macros) { - var serializer = new JavaScriptSerializer(); var items = macros.Select(FromMacro).ToArray(); - var json = serializer.Serialize(items); + var json = JsonConvert.SerializeObject(items); return json; } @@ -72,9 +71,8 @@ namespace Umbraco.Web.Cache /// internal static string SerializeToJsonPayload(params IMacro[] macros) { - var serializer = new JavaScriptSerializer(); var items = macros.Select(FromMacro).ToArray(); - var json = serializer.Serialize(items); + var json = JsonConvert.SerializeObject(items); return json; } @@ -85,9 +83,8 @@ namespace Umbraco.Web.Cache /// internal static string SerializeToJsonPayload(params macro[] macros) { - var serializer = new JavaScriptSerializer(); var items = macros.Select(FromMacro).ToArray(); - var json = serializer.Serialize(items); + var json = JsonConvert.SerializeObject(items); return json; } diff --git a/src/Umbraco.Web/Cache/MediaCacheRefresher.cs b/src/Umbraco.Web/Cache/MediaCacheRefresher.cs index fc589b530b..3613da426d 100644 --- a/src/Umbraco.Web/Cache/MediaCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/MediaCacheRefresher.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Persistence.Repositories; using umbraco.interfaces; using System.Linq; +using Newtonsoft.Json; using Umbraco.Web.PublishedCache.XmlPublishedCache; namespace Umbraco.Web.Cache @@ -32,8 +33,7 @@ namespace Umbraco.Web.Cache /// public static JsonPayload[] DeserializeFromJsonPayload(string json) { - var serializer = new JavaScriptSerializer(); - var jsonObject = serializer.Deserialize(json); + var jsonObject = JsonConvert.DeserializeObject(json); return jsonObject; } @@ -45,34 +45,31 @@ namespace Umbraco.Web.Cache /// internal static string SerializeToJsonPayload(OperationType operation, params IMedia[] media) { - var serializer = new JavaScriptSerializer(); var items = media.Select(x => FromMedia(x, operation)).ToArray(); - var json = serializer.Serialize(items); + var json = JsonConvert.SerializeObject(items); return json; } internal static string SerializeToJsonPayloadForMoving(OperationType operation, MoveEventInfo[] media) { - var serializer = new JavaScriptSerializer(); var items = media.Select(x => new JsonPayload { Id = x.Entity.Id, Operation = operation, Path = x.OriginalPath }).ToArray(); - var json = serializer.Serialize(items); + var json = JsonConvert.SerializeObject(items); return json; } internal static string SerializeToJsonPayloadForPermanentDeletion(params int[] mediaIds) { - var serializer = new JavaScriptSerializer(); var items = mediaIds.Select(x => new JsonPayload { Id = x, Operation = OperationType.Deleted }).ToArray(); - var json = serializer.Serialize(items); + var json = JsonConvert.SerializeObject(items); return json; } diff --git a/src/Umbraco.Web/Cache/MemberGroupCacheRefresher.cs b/src/Umbraco.Web/Cache/MemberGroupCacheRefresher.cs index dc2ba39b9d..2d61b254a0 100644 --- a/src/Umbraco.Web/Cache/MemberGroupCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/MemberGroupCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Web.Script.Serialization; +using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Models; @@ -20,8 +21,7 @@ namespace Umbraco.Web.Cache /// private static JsonPayload[] DeserializeFromJsonPayload(string json) { - var serializer = new JavaScriptSerializer(); - var jsonObject = serializer.Deserialize(json); + var jsonObject = JsonConvert.DeserializeObject(json); return jsonObject; } @@ -32,9 +32,8 @@ namespace Umbraco.Web.Cache /// internal static string SerializeToJsonPayload(params IMemberGroup[] groups) { - var serializer = new JavaScriptSerializer(); var items = groups.Select(FromMemberGroup).ToArray(); - var json = serializer.Serialize(items); + var json = JsonConvert.SerializeObject(items); return json; } diff --git a/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs b/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs index e352c8108a..2941357f19 100644 --- a/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs @@ -5,7 +5,7 @@ using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Models; using System.Linq; - +using Newtonsoft.Json; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Sync; @@ -40,21 +40,19 @@ namespace Umbraco.Web.Cache /// internal static JsonPayload[] DeserializeFromJsonPayload(string json) { - var serializer = new JavaScriptSerializer(); - var jsonObject = serializer.Deserialize(json); + var jsonObject = JsonConvert.DeserializeObject(json); return jsonObject; } internal static string SerializeToJsonPayloadForPermanentDeletion(params int[] contentIds) { - var serializer = new JavaScriptSerializer(); var items = contentIds.Select(x => new JsonPayload { Id = x, Operation = OperationType.Deleted }).ToArray(); - var json = serializer.Serialize(items); + var json = JsonConvert.SerializeObject(items); return json; } diff --git a/src/Umbraco.Web/Cache/UserGroupCacheRefresher.cs b/src/Umbraco.Web/Cache/UserGroupCacheRefresher.cs index 84496b61fd..5fd1e4dfa4 100644 --- a/src/Umbraco.Web/Cache/UserGroupCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/UserGroupCacheRefresher.cs @@ -9,6 +9,9 @@ namespace Umbraco.Web.Cache /// /// Handles User group cache invalidation/refreshing /// + /// + /// This also needs to clear the user cache since IReadOnlyUserGroup's are attached to IUser objects + /// public sealed class UserGroupCacheRefresher : CacheRefresherBase { protected override UserGroupCacheRefresher Instance @@ -35,6 +38,9 @@ namespace Umbraco.Web.Cache userGroupCache.Result.ClearCacheByKeySearch(UserGroupRepository.GetByAliasCacheKeyPrefix); } + //We'll need to clear all user cache too + ClearAllIsolatedCacheByEntityType(); + base.RefreshAll(); } @@ -52,8 +58,12 @@ namespace Umbraco.Web.Cache userGroupCache.Result.ClearCacheItem(RepositoryBase.GetCacheIdKey(id)); userGroupCache.Result.ClearCacheByKeySearch(UserGroupRepository.GetByAliasCacheKeyPrefix); } - + + //we don't know what user's belong to this group without doing a look up so we'll need to just clear them all + ClearAllIsolatedCacheByEntityType(); + base.Remove(id); } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs b/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs index 8056724a68..6525e95d66 100644 --- a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs @@ -193,7 +193,7 @@ namespace Umbraco.Web.Models.Mapping //Deal with assigned permissions: - var allContentPermissions = applicationContext.Services.UserService.GetPermissions(group, true) + var allContentPermissions = applicationContext.Services.UserService.GetPermissions(@group, true) .ToDictionary(x => x.EntityId, x => x); var contentEntities = allContentPermissions.Keys.Count == 0 diff --git a/src/umbraco.cms/businesslogic/Permission.cs b/src/umbraco.cms/businesslogic/Permission.cs index 335535c08d..32daa3df50 100644 --- a/src/umbraco.cms/businesslogic/Permission.cs +++ b/src/umbraco.cms/businesslogic/Permission.cs @@ -32,24 +32,8 @@ namespace umbraco.BusinessLogic public static void MakeNew(IUserGroup userGroup, CMSNode node, char permissionKey) { - MakeNew(userGroup, node, permissionKey, true); - } - - private static void MakeNew(IUserGroup userGroup, IEnumerable nodes, char permissionKey, bool raiseEvents) - { - var asArray = nodes.ToArray(); - - ApplicationContext.Current.Services.UserService.AssignUserGroupPermission(userGroup.Id, permissionKey, asArray.Select(x => x.Id).ToArray()); - - if (raiseEvents) - { - OnNew(new UserGroupPermission(userGroup, asArray, new[] { permissionKey }), new NewEventArgs()); - } - } - - private static void MakeNew(IUserGroup userGroup, CMSNode Node, char PermissionKey, bool raiseEvents) - { - MakeNew(userGroup, new[] {Node}, PermissionKey, raiseEvents); + ApplicationContext.Current.Services.UserService.AssignUserGroupPermission( + userGroup.Id, permissionKey, node.Id); } /// @@ -99,17 +83,8 @@ namespace umbraco.BusinessLogic /// /// public static void DeletePermissions(IUserGroup userGroup, CMSNode node) - { - DeletePermissions(userGroup, node, true); - } - - internal static void DeletePermissions(IUserGroup userGroup, CMSNode node, bool raiseEvents) { ApplicationContext.Current.Services.UserService.RemoveUserGroupPermissions(userGroup.Id, node.Id); - if (raiseEvents) - { - OnDeleted(new UserGroupPermission(userGroup, node, null), new DeleteEventArgs()); - } } /// @@ -119,15 +94,13 @@ namespace umbraco.BusinessLogic public static void DeletePermissions(IUserGroup userGroup) { ApplicationContext.Current.Services.UserService.RemoveUserGroupPermissions(userGroup.Id); - - OnDeleted(new UserGroupPermission(userGroup, Enumerable.Empty(), null), new DeleteEventArgs()); + } public static void DeletePermissions(int userGroupId, int[] iNodeIDs) { ApplicationContext.Current.Services.UserService.RemoveUserGroupPermissions(userGroupId, iNodeIDs); - - OnDeleted(new UserGroupPermission(userGroupId, iNodeIDs), new DeleteEventArgs()); + } public static void DeletePermissions(int userGroupId, int iNodeID) { @@ -142,7 +115,6 @@ namespace umbraco.BusinessLogic { ApplicationContext.Current.Services.ContentService.RemoveContentPermissions(node.Id); - OnDeleted(new UserGroupPermission(null, node, null), new DeleteEventArgs()); } public static void UpdateCruds(IUserGroup userGroup, CMSNode node, string permissions) @@ -151,103 +123,9 @@ namespace umbraco.BusinessLogic userGroup.Id, permissions.ToCharArray(), node.Id); - - OnUpdated(new UserGroupPermission(userGroup, node, permissions.ToCharArray()), new SaveEventArgs()); - } - - internal static event TypedEventHandler Deleted; - private static void OnDeleted(UserGroupPermission permission, DeleteEventArgs args) - { - if (Deleted != null) - { - Deleted(permission, args); - } - } - - internal static event TypedEventHandler Updated; - private static void OnUpdated(UserGroupPermission permission, SaveEventArgs args) - { - if (Updated != null) - { - Updated(permission, args); - } - } - - internal static event TypedEventHandler New; - private static void OnNew(UserGroupPermission permission, NewEventArgs args) - { - if (New != null) - { - New(permission, args); - } + } } - internal class UserGroupPermission - { - private int? _userGroupId; - private readonly int[] _nodeIds; - - internal UserGroupPermission(int userGroupId) - { - _userGroupId = userGroupId; - } - - internal UserGroupPermission(int userGroupId, IEnumerable nodeIds) - { - _userGroupId = userGroupId; - _nodeIds = nodeIds.ToArray(); - } - - internal UserGroupPermission(IUserGroup userGroup, CMSNode node, char[] permissionKeys) - { - UserGroup = userGroup; - Nodes = new[] { node }; - PermissionKeys = permissionKeys; - } - - internal UserGroupPermission(IUserGroup userGroup, IEnumerable nodes, char[] permissionKeys) - { - UserGroup = userGroup; - Nodes = nodes; - PermissionKeys = permissionKeys; - } - - internal int UserId - { - get - { - if (_userGroupId.HasValue) - { - return _userGroupId.Value; - } - if (UserGroup != null) - { - return UserGroup.Id; - } - return -1; - } - } - - internal IEnumerable NodeIds - { - get - { - if (_nodeIds != null) - { - return _nodeIds; - } - if (Nodes != null) - { - return Nodes.Select(x => x.Id); - } - return Enumerable.Empty(); - } - } - - internal IUserGroup UserGroup { get; private set; } - internal IEnumerable Nodes { get; private set; } - internal char[] PermissionKeys { get; private set; } - } } \ No newline at end of file