diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.rights.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.rights.controller.js index f7bdcc5610..eace76ac75 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.rights.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.rights.controller.js @@ -7,7 +7,7 @@ vm.availableUserGroups = []; vm.selectedUserGroups = []; - vm.selectUserGroup = {}; + vm.removedUserGroups = []; vm.viewState = "manageGroups"; vm.setViewSate = setViewSate; @@ -20,75 +20,121 @@ function onInit() { vm.loading = true; contentResource.getDetailedPermissions($scope.currentNode.id).then(function (userGroups) { - vm.availableUserGroups = userGroups; + initData(userGroups); vm.loading = false; }); } + /** + * This will initialize the data and set the correct selectedUserGroups based on the default permissions and explicit permissions assigned + * @param {any} userGroups + */ + function initData(userGroups) { + //reset this + vm.selectedUserGroups = []; + vm.availableUserGroups = userGroups; + angular.forEach(vm.availableUserGroups, function (group) { + if (group.permissions) { + //if there's explicit permissions assigned than it's selected + assignGroupPermissions(group); + } + }); + } + function setViewSate(state) { vm.viewState = state; } function editPermissions(group) { - vm.selectedUserGroup = group; - setViewSate("managePermissions"); + vm.selectedUserGroup = group; + if (!vm.selectedUserGroup.permissions) { + //if no permissions are explicitly set this means we need to show the defaults + vm.selectedUserGroup.permissions = vm.selectedUserGroup.defaultPermissions; + } + setViewSate("managePermissions"); + } + + function assignGroupPermissions(group) { + // clear allowed permissions before we make the list so we don't have duplicates + group.allowedPermissions = []; + + // get list of checked permissions + angular.forEach(group.permissions, function (permissionGroup) { + angular.forEach(permissionGroup, function (permission) { + if (permission.checked) { + //the `allowedPermissions` is what will get sent up to the server for saving + group.allowedPermissions.push(permission); + } + }); + }); + + if (!group.selected) { + // set to selected so we can remove from the dropdown easily + group.selected = true; + vm.selectedUserGroups.push(group); + //remove from the removed groups if it's been re-added + vm.removedUserGroups = _.reject(vm.removedUserGroups, function (g) { + return g.id == group.id; + }); + } } function setPermissions(group) { - // clear allowed permissions before we make the list - // so we don't have duplicates - group.allowedPermissions = []; - - // get list of checked permissions - angular.forEach(group.permissions, function (permissionGroup) { - angular.forEach(permissionGroup, function (permission) { - if (permission.checked) { - group.allowedPermissions.push(permission); - } - }); - }); - - if (!group.selected) { - // set to selected so we can remove from the dropdown easily - group.selected = true; - vm.selectedUserGroups.push(group); - } - + assignGroupPermissions(group); setViewSate("manageGroups"); } + /** + * This essentially resets the permissions for a group for this content item, it will remove it from the selected list + * @param {any} index + */ function removePermissions(index) { // remove as selected so we can select it from the dropdown again var group = vm.selectedUserGroups[index]; group.selected = false; + //reset assigned permissions - so it will default back to default permissions + group.permissions = []; + group.allowedPermissions = []; vm.selectedUserGroups.splice(index, 1); + //track it in the removed so this gets pushed to the server + vm.removedUserGroups.push(group); } function cancelManagePermissions() { setViewSate("manageGroups"); } + function formatSaveModel(permissionsSave, groupCollection) { + angular.forEach(groupCollection, function (g) { + permissionsSave[g.id] = []; + angular.forEach(g.allowedPermissions, function (p) { + permissionsSave[g.id].push(p.permissionCode); + }); + }); + } + function save() { vm.saveState = "busy"; vm.saveError = false; vm.saveSuccces = false; - //this is a dictionary that we need to format + //this is a dictionary that we need to populate var permissionsSave = {}; - angular.forEach(vm.selectedUserGroups, function (g) { - permissionsSave[g.id] = []; - angular.forEach(g.allowedPermissions, function (p) { - permissionsSave[g.id].push(p.permissionCode); - }); - }); + //format the selectedUserGroups, then the removedUserGroups since we want to pass data from both collections up + formatSaveModel(permissionsSave, vm.selectedUserGroups); + formatSaveModel(permissionsSave, vm.removedUserGroups); var saveModel = { contentId: $scope.currentNode.id, permissions: permissionsSave }; - contentResource.savePermissions(saveModel).then(function () { + contentResource.savePermissions(saveModel).then(function (userGroups) { + + //re-assign model from server since it could have changed + initData(userGroups); + vm.saveState = "success"; vm.saveSuccces = true; }, function(error){ diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 4bcf47a441..8fd2d8f221 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -114,9 +114,15 @@ namespace Umbraco.Web.Editors { //create a string collection of the assigned letters var groupPermissionCodes = groupPermissions.ToArray(); - + + //check if there are no permissions assigned for this group save model, if that is the case we want to reset the permissions + //for this group/node which will go back to the defaults + if (groupPermissionCodes.Length == 0) + { + Services.UserService.RemoveUserGroupPermissions(userGroup.Id, content.Id); + } //check if they are the defaults, if so we should just remove them if they exist since it's more overhead having them stored - if (userGroup.Permissions.UnsortedSequenceEqual(groupPermissionCodes)) + else if (userGroup.Permissions.UnsortedSequenceEqual(groupPermissionCodes)) { //only remove them if they are actually currently assigned if (contentPermissions.ContainsKey(userGroup.Id)) @@ -125,9 +131,10 @@ namespace Umbraco.Web.Editors Services.UserService.RemoveUserGroupPermissions(userGroup.Id, content.Id); } } + //if they are different we need to update, otherwise there's nothing to update else if (contentPermissions.ContainsKey(userGroup.Id) == false || contentPermissions[userGroup.Id].AssignedPermissions.UnsortedSequenceEqual(groupPermissionCodes) == false) { - //if they are different we need to update, otherwise there's nothing to update + Services.UserService.ReplaceUserGroupPermissions(userGroup.Id, groupPermissionCodes.Select(x => x[0]), content.Id); } } @@ -161,7 +168,9 @@ namespace Umbraco.Web.Editors //get all user groups and map their default permissions to the AssignedUserGroupPermissions model. //we do this because not all groups will have true assigned permissions for this node so if they don't have assigned permissions, we need to show the defaults. - var defaultPermissionsByGroup = Mapper.Map>(allUserGroups) + var defaultPermissionsByGroup = Mapper.Map>(allUserGroups).ToArray(); + + var defaultPermissionsAsDictionary = defaultPermissionsByGroup .ToDictionary(x => Convert.ToInt32(x.Id), x => x); //get the actual assigned permissions @@ -170,7 +179,10 @@ namespace Umbraco.Web.Editors //iterate over assigned and update the defaults with the real values foreach (var assignedGroupPermission in assignedPermissionsByGroup) { - var defaultUserGroupPermissions = defaultPermissionsByGroup[assignedGroupPermission.UserGroupId]; + var defaultUserGroupPermissions = defaultPermissionsAsDictionary[assignedGroupPermission.UserGroupId]; + + //clone the default permissions model to the assigned ones + defaultUserGroupPermissions.AssignedPermissions = AssignedUserGroupPermissions.ClonePermissions(defaultUserGroupPermissions.DefaultPermissions); //since there is custom permissions assigned to this node for this group, we need to clear all of the default permissions //and we'll re-check it if it's one of the explicitly assigned ones @@ -179,9 +191,10 @@ namespace Umbraco.Web.Editors permission.Checked = false; permission.Checked = assignedGroupPermission.AssignedPermissions.Contains(permission.PermissionCode, StringComparer.InvariantCulture); } - } - - return defaultPermissionsByGroup.Values; + + } + + return defaultPermissionsByGroup; } /// diff --git a/src/Umbraco.Web/Models/ContentEditing/AssignedUserGroupPermissions.cs b/src/Umbraco.Web/Models/ContentEditing/AssignedUserGroupPermissions.cs index 8875550454..0b8f28ce06 100644 --- a/src/Umbraco.Web/Models/ContentEditing/AssignedUserGroupPermissions.cs +++ b/src/Umbraco.Web/Models/ContentEditing/AssignedUserGroupPermissions.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Runtime.Serialization; namespace Umbraco.Web.Models.ContentEditing @@ -9,10 +10,27 @@ namespace Umbraco.Web.Models.ContentEditing [DataContract(Name = "contentPermission", Namespace = "")] public class AssignedUserGroupPermissions : EntityBasic { + /// - /// The default permissions for the user group organized by permission group name + /// The assigned permissions for the user group organized by permission group name /// [DataMember(Name = "permissions")] public IDictionary> AssignedPermissions { get; set; } + + /// + /// The default permissions for the user group organized by permission group name + /// + [DataMember(Name = "defaultPermissions")] + public IDictionary> DefaultPermissions { get; set; } + + public static IDictionary> ClonePermissions(IDictionary> permissions) + { + var result = new Dictionary>(); + foreach (var permission in permissions) + { + result[permission.Key] = new List(permission.Value.Select(x => (Permission)x.Clone())); + } + return result; + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/ContentEditing/Permission.cs b/src/Umbraco.Web/Models/ContentEditing/Permission.cs index 81b85f093f..9c40dc0aa6 100644 --- a/src/Umbraco.Web/Models/ContentEditing/Permission.cs +++ b/src/Umbraco.Web/Models/ContentEditing/Permission.cs @@ -1,9 +1,10 @@ +using System; using System.Runtime.Serialization; namespace Umbraco.Web.Models.ContentEditing { [DataContract(Name = "permission", Namespace = "")] - public class Permission + public class Permission : ICloneable { [DataMember(Name = "name")] public string Name { get; set; } @@ -28,5 +29,10 @@ namespace Umbraco.Web.Models.ContentEditing /// [DataMember(Name = "permissionCode")] public string PermissionCode { get; set; } + + public object Clone() + { + return this.MemberwiseClone(); + } } } \ 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 1fbe28ecc3..288841374e 100644 --- a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs @@ -138,12 +138,14 @@ namespace Umbraco.Web.Models.Mapping //create a map to assign a user group's default permissions to the AssignedUserGroupPermissions instance config.CreateMap() .ForMember(detail => detail.Udi, opt => opt.Ignore()) - .ForMember(detail => detail.Trashed, opt => opt.Ignore()) + .ForMember(detail => detail.Trashed, opt => opt.Ignore()) .ForMember(detail => detail.AdditionalData, opt => opt.Ignore()) .ForMember(detail => detail.Id, opt => opt.MapFrom(group => group.Id)) .ForMember(detail => detail.ParentId, opt => opt.UseValue(-1)) .ForMember(detail => detail.Path, opt => opt.MapFrom(userGroup => "-1," + userGroup.Id)) - .ForMember(detail => detail.AssignedPermissions, expression => expression.ResolveUsing(new PermissionsResolver(applicationContext.Services.TextService))) + .ForMember(detail => detail.DefaultPermissions, expression => expression.ResolveUsing(new PermissionsResolver(applicationContext.Services.TextService))) + //these will be manually mapped and by default they are null + .ForMember(detail => detail.AssignedPermissions, opt => opt.Ignore()) .AfterMap((group, display) => { if (display.Icon.IsNullOrWhiteSpace())