From 4f28cb19176b63e1cdb18ef030724dbbde2a69d5 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 29 Jun 2017 19:15:04 +1000 Subject: [PATCH] Gets content permissions saving per user group --- .../Models/Membership/EntityPermissionSet.cs | 2 +- .../Repositories/ContentRepository.cs | 7 +- .../Repositories/PermissionRepository.cs | 2 +- src/Umbraco.Core/Services/ContentService.cs | 2 +- src/Umbraco.Core/Services/IContentService.cs | 4 +- .../Services/UserServiceExtensions.cs | 4 +- .../src/common/resources/content.resource.js | 19 +++- .../common/resources/usergroups.resource.js | 2 +- .../content/content.rights.controller.js | 24 ++++- .../src/views/content/rights.html | 2 +- .../umbraco_client/Application/Extensions.js | 6 +- src/Umbraco.Web/Editors/ContentController.cs | 92 ++++++++++++++----- .../UserGroupPermissionsSave.cs | 40 ++++++++ .../Models/ContentEditing/UserGroupSave.cs | 11 ++- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 15 files changed, 175 insertions(+), 43 deletions(-) create mode 100644 src/Umbraco.Web/Models/ContentEditing/UserGroupPermissionsSave.cs diff --git a/src/Umbraco.Core/Models/Membership/EntityPermissionSet.cs b/src/Umbraco.Core/Models/Membership/EntityPermissionSet.cs index ea1e927a59..d91cd3a352 100644 --- a/src/Umbraco.Core/Models/Membership/EntityPermissionSet.cs +++ b/src/Umbraco.Core/Models/Membership/EntityPermissionSet.cs @@ -3,7 +3,7 @@ namespace Umbraco.Core.Models.Membership { /// - /// Represents an entity -> user & permission key value pair collection + /// Represents an entity -> user group & permission key value pair collection /// public class EntityPermissionSet { diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 1f876bb7b0..6b99439093 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -884,7 +884,12 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder", var repo = new PermissionRepository(UnitOfWork, _cacheHelper, SqlSyntax); repo.AssignEntityPermission(entity, permission, groupIds); } - + + /// + /// Returns permissions directly assigned to the content item for all user groups + /// + /// + /// public IEnumerable GetPermissionsForEntity(int entityId) { var repo = new PermissionRepository(UnitOfWork, _cacheHelper, SqlSyntax); diff --git a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs index 3c7273eb0b..ca9045507b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs @@ -130,7 +130,7 @@ namespace Umbraco.Core.Persistence.Repositories } /// - /// Returns permissions for all groups for a given entity + /// Returns permissions directly assigned to the content item for all user groups /// /// /// diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 65367c24d1..6e09d5f845 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -130,7 +130,7 @@ namespace Umbraco.Core.Services } /// - /// Gets the list of permissions for the content item + /// Returns permissions directly assigned to the content item for all user groups /// /// /// diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index cefe39a599..289af7324b 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -137,7 +137,7 @@ namespace Umbraco.Core.Services /// /// Used to bulk update the permissions set for a content item. This will replace all permissions - /// assigned to an entity with a list of user id & permission pairs. + /// assigned to an entity with a list of user group id & permission pairs. /// /// void ReplaceContentPermissions(EntityPermissionSet permissionSet); @@ -151,7 +151,7 @@ namespace Umbraco.Core.Services void AssignContentPermission(IContent entity, char permission, IEnumerable groupIds); /// - /// Gets the list of permissions for the content item + /// Returns permissions directly assigned to the content item for all user groups /// /// /// diff --git a/src/Umbraco.Core/Services/UserServiceExtensions.cs b/src/Umbraco.Core/Services/UserServiceExtensions.cs index 6130077771..0492a59d03 100644 --- a/src/Umbraco.Core/Services/UserServiceExtensions.cs +++ b/src/Umbraco.Core/Services/UserServiceExtensions.cs @@ -7,7 +7,7 @@ namespace Umbraco.Core.Services internal static class UserServiceExtensions { /// - /// Remove all permissions for this user for all nodes specified + /// Remove all permissions for this user group for all nodes specified /// /// /// @@ -18,7 +18,7 @@ namespace Umbraco.Core.Services } /// - /// Remove all permissions for this user for all nodes + /// Remove all permissions for this user group for all nodes /// /// /// diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index 49c3745970..d24a29dc61 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -40,7 +40,24 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { return { - + + savePermissions: function (saveModel) { + if (!saveModel) { + throw "saveModel cannot be null"; + } + if (!saveModel.contentId) { + throw "saveModel.contentId cannot be null"; + } + if (!saveModel.permissions) { + throw "saveModel.permissions cannot be null"; + } + + return umbRequestHelper.resourcePromise( + $http.post(umbRequestHelper.getApiUrl("contentApiBaseUrl", "PostSaveUserGroupPermissions"), + saveModel), + 'Failed to save permissions'); + }, + getRecycleBin: function () { return umbRequestHelper.resourcePromise( diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/usergroups.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/usergroups.resource.js index a48806f48c..f5b9e3e6d7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/usergroups.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/usergroups.resource.js @@ -36,7 +36,7 @@ "PostSaveUserGroup"), formattedSaveData), "Failed to save user group"); - } + } function getUserGroup(id) { 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 3c2cc55362..c82d41e201 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 @@ -13,6 +13,7 @@ vm.setViewSate = setViewSate; vm.editPermissions = editPermissions; vm.setPermissions = setPermissions; + vm.save = save; vm.removePermissions = removePermissions; vm.cancelManagePermissions = cancelManagePermissions; @@ -35,7 +36,7 @@ function setPermissions(group) { // clear allowed permissions before we make the list - // so we don't have deplicates + // so we don't have duplicates group.allowedPermissions = []; // get list of checked permissions @@ -67,6 +68,27 @@ setViewSate("manageGroups"); } + function save() { + + //this is a dictionary that we need to format + var permissionsSave = {}; + angular.forEach(vm.selectedUserGroups, function(g) { + permissionsSave[g.id] = []; + angular.forEach(g.allowedPermissions, function(p) { + permissionsSave[g.id].push(p.permissionCode); + }); + }); + + var saveModel = { + contentId: $scope.currentNode.id, + permissions: permissionsSave + }; + + contentResource.savePermissions(saveModel).then(function() { + alert("hooray!"); + }); + } + onInit(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/content/rights.html b/src/Umbraco.Web.UI.Client/src/views/content/rights.html index 7f5739122c..78779cc510 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/rights.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/rights.html @@ -43,7 +43,7 @@ Cancel - diff --git a/src/Umbraco.Web.UI/umbraco_client/Application/Extensions.js b/src/Umbraco.Web.UI/umbraco_client/Application/Extensions.js index 87eb0863ad..9884df1c80 100644 --- a/src/Umbraco.Web.UI/umbraco_client/Application/Extensions.js +++ b/src/Umbraco.Web.UI/umbraco_client/Application/Extensions.js @@ -27,12 +27,12 @@ }; } - if (!window.location.search.getParams) { + if (!window.location.getParams) { var pl = /\+/g; // Regex for replacing addition symbol with a space var search = /([^&=]+)=?([^&]*)/g; var decode = function(s) { return decodeURIComponent(s.replace(pl, " ")); }; - window.location.search.getParams = function() { + window.location.getParams = function() { var match; var query = window.location.search.substring(1); @@ -393,7 +393,7 @@ xhr.setRequestHeader("X-XSRF-TOKEN", cookieVal); } - var queryString = window.location.search.getParams(); + var queryString = window.location.getParams(); if (queryString.umbDebug === "true") { xhr.setRequestHeader("X-UMB-DEBUG", cookieVal); } diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 098cabf74e..001bbd21b9 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -83,6 +83,55 @@ namespace Umbraco.Web.Editors return foundContent.Select(Mapper.Map); } + /// + /// Updates the permissions for a content item for a particular user group + /// + /// + /// + public IEnumerable PostSaveUserGroupPermissions(UserGroupPermissionsSave saveModel) + { + if (saveModel.ContentId <= 0) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); + + var content = Services.ContentService.GetById(saveModel.ContentId); + if (content == null) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); + + //current permissions explicitly assigned to this content item + var contentPermissions = Services.ContentService.GetPermissionsForEntity(content) + .ToDictionary(x => x.UserGroupId, x => x); + + var allUserGroups = Services.UserService.GetAllUserGroups().ToArray(); + + //loop through each user group + foreach (var userGroup in allUserGroups) + { + //check if there's a permission set posted up for this user group + IEnumerable groupPermissions; + if (saveModel.AssignedPermissions.TryGetValue(userGroup.Id, out groupPermissions)) + { + //create a string collection of the assigned letters + var groupPermissionCodes = groupPermissions.ToArray(); + + //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)) + { + //only remove them if they are actually currently assigned + if (contentPermissions.ContainsKey(userGroup.Id)) + { + //remove these permissions from this node for this group since the ones being assigned are the same as the defaults + Services.UserService.RemoveUserGroupPermissions(userGroup.Id, content.Id); + } + } + 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); + } + } + } + + return GetDetailedPermissions(content, allUserGroups); + } + /// /// Returns the user group permissions for user groups assigned to this node /// @@ -93,41 +142,38 @@ namespace Umbraco.Web.Editors /// [EnsureUserPermissionForContent("contentId", 'R')] public IEnumerable GetDetailedPermissions(int contentId) - { + { if (contentId <= 0) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); - var found = Services.ContentService.GetById(contentId); - if (found == null) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); + var content = Services.ContentService.GetById(contentId); + if (content == null) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); - - //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 allUserGroups = Services.UserService.GetAllUserGroups(); + + return GetDetailedPermissions(content, allUserGroups); + } + + private IEnumerable GetDetailedPermissions(IContent content, IEnumerable allUserGroups) + { + //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) .ToDictionary(x => Convert.ToInt32(x.Id), x => x); - + //get the actual assigned permissions - var assignedPermissionsByGroup = Services.ContentService.GetPermissionsForEntity(found).ToArray(); - + var assignedPermissionsByGroup = Services.ContentService.GetPermissionsForEntity(content).ToArray(); + //iterate over assigned and update the defaults with the real values foreach (var assignedGroupPermission in assignedPermissionsByGroup) { var defaultUserGroupPermissions = defaultPermissionsByGroup[assignedGroupPermission.UserGroupId]; - //iterate each assigned permission for this group, the permission is essentially the letter of the action - foreach (var assignedPermission in assignedGroupPermission.AssignedPermissions) + //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 + foreach (var permission in defaultUserGroupPermissions.AssignedPermissions.SelectMany(x => x.Value)) { - //find this permission letter in the default model - - //iterate through the dictionary - foreach (var defaultUserGroupPermissionByGroup in defaultUserGroupPermissions.AssignedPermissions) - { - //iterate through the key/value pair values - foreach (var permissionInGroup in defaultUserGroupPermissionByGroup.Value) - { - //assigned the checked parameter based on the actual assigned permission - permissionInGroup.Checked = permissionInGroup.PermissionCode == assignedPermission; - } - } + permission.Checked = false; + permission.Checked = assignedGroupPermission.AssignedPermissions.Contains(permission.PermissionCode, StringComparer.InvariantCulture); } } diff --git a/src/Umbraco.Web/Models/ContentEditing/UserGroupPermissionsSave.cs b/src/Umbraco.Web/Models/ContentEditing/UserGroupPermissionsSave.cs new file mode 100644 index 0000000000..b38a6d75e3 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/UserGroupPermissionsSave.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Runtime.Serialization; +using Umbraco.Core; + +namespace Umbraco.Web.Models.ContentEditing +{ + /// + /// Used to assign user group permissions to a content node + /// + [DataContract(Name = "contentPermission", Namespace = "")] + public class UserGroupPermissionsSave : IValidatableObject + { + public UserGroupPermissionsSave() + { + AssignedPermissions = new Dictionary>(); + } + + //TODO: we should have an option to clear the permissions assigned to this node and instead just have them inherit - yes once we actually have inheritance! + + [DataMember(Name = "contentId", IsRequired = true)] + [Required] + public int ContentId { get; set; } + + /// + /// A dictionary of permissions to assign, the key is the user group id + /// + [DataMember(Name = "permissions")] + public IDictionary> AssignedPermissions { get; set; } + + public IEnumerable Validate(ValidationContext validationContext) + { + if (AssignedPermissions.SelectMany(x => x.Value).Any(x => x.IsNullOrWhiteSpace())) + { + yield return new ValidationResult("A permission value cannot be null or empty", new[] { "Permissions" }); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/ContentEditing/UserGroupSave.cs b/src/Umbraco.Web/Models/ContentEditing/UserGroupSave.cs index f18c863b77..f122f46617 100644 --- a/src/Umbraco.Web/Models/ContentEditing/UserGroupSave.cs +++ b/src/Umbraco.Web/Models/ContentEditing/UserGroupSave.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.Linq; using System.Runtime.Serialization; using Newtonsoft.Json; +using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; @@ -50,11 +52,10 @@ namespace Umbraco.Web.Models.ContentEditing public IEnumerable Validate(ValidationContext validationContext) { - //TODO: Add other server side validation - //if (CultureInfo.GetCultureInfo(Culture)) - // yield return new ValidationResult("The culture is invalid", new[] { "Culture" }); - - yield break; + if (Permissions.Any(x => x.IsNullOrWhiteSpace())) + { + yield return new ValidationResult("A permission value cannot be null or empty", new[] { "Permissions" }); + } } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index c275de8e92..dcdba57775 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -398,6 +398,7 @@ +