From 49df332734a11db7b293ae3ca0b1d94bf271c6bc Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 21 Oct 2013 17:02:33 +1100 Subject: [PATCH] Completes: U4-3194 member editor needs group/role support --- src/Umbraco.Core/Constants-Conventions.cs | 5 +++ .../src/common/services/util.service.js | 13 +++++- .../src/less/property-editors.less | 16 ++++++- .../membergroups/membergroups.controller.js | 36 +++++++++++++++ .../membergroups/membergroups.html | 18 ++++++++ src/Umbraco.Web/Editors/MemberController.cs | 14 ++++++ .../Models/ContentEditing/MemberSave.cs | 6 ++- .../Models/Mapping/MemberModelMapper.cs | 45 +++++++++++++++---- src/Umbraco.Web/Security/WebSecurity.cs | 2 - .../UmbracoUserTimeoutFilterAttribute.cs | 3 ++ 10 files changed, 144 insertions(+), 14 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergroups/membergroups.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergroups/membergroups.html diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index 660a8073c9..951934ddf6 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -99,6 +99,11 @@ namespace Umbraco.Core /// public static class Member { + /// + /// if a role starts with __umbracoRole we won't show it as it's an internal role used for public access + /// + public static readonly string InternalRolePrefix = "__umbracoRole"; + public static readonly string UmbracoMemberProviderName = "UmbracoMembershipProvider"; public static readonly string UmbracoRoleProviderName = "UmbracoRoleProvider"; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js index 0dc30be9c4..0d9a0bc083 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js @@ -245,10 +245,21 @@ function umbDataFormatter() { var propPass = _.find(genericTab.properties, function (item) { return item.alias === "_umb_password"; }); + var propGroups = _.find(genericTab.properties, function (item) { + return item.alias === "_umb_membergroup"; + }); saveModel.email = propEmail.value; saveModel.username = propLogin.value; saveModel.password = propPass.value; - + + var selectedGroups = []; + for (var n in propGroups.value) { + if (propGroups.value[n] === true) { + selectedGroups.push(n); + } + } + saveModel.memberGroups = selectedGroups; + return saveModel; }, diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index b0472e8e76..747d71c41a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -281,4 +281,18 @@ ul.color-picker li a { } .umb-fileupload input { font-size: 12px - } \ No newline at end of file + } + +// +// Member group picker +// -------------------------------------------------- + +.umb-member-group-box { + width: 45%; +} +.umb-member-group-box:nth-child(1){ + float:left; +} +.umb-member-group-box:nth-child(2){ + float:right; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergroups/membergroups.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergroups/membergroups.controller.js new file mode 100644 index 0000000000..7f5ba2af55 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergroups/membergroups.controller.js @@ -0,0 +1,36 @@ +function memberGroupController($rootScope, $scope, dialogService, mediaResource, imageHelper, $log) { + + //set the available to the keys of the dictionary who's value is true + $scope.getAvailable = function () { + var available = []; + for (var n in $scope.model.value) { + if ($scope.model.value[n] === false) { + available.push(n); + } + } + return available; + }; + //set the selected to the keys of the dictionary who's value is true + $scope.getSelected = function () { + var selected = []; + for (var n in $scope.model.value) { + if ($scope.model.value[n] === true) { + selected.push(n); + } + } + return selected; + }; + + $scope.addItem = function(item) { + //keep the model up to date + $scope.model.value[item] = true; + }; + + $scope.removeItem = function (item) { + //keep the model up to date + $scope.model.value[item] = false; + }; + + +} +angular.module('umbraco').controller("Umbraco.PropertyEditors.MemberGroupController", memberGroupController); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergroups/membergroups.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergroups/membergroups.html new file mode 100644 index 0000000000..3448f1b302 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergroups/membergroups.html @@ -0,0 +1,18 @@ +
+
+
Not a member of group(s)
+ +
+
+
Member of group(s)
+ +
+
\ No newline at end of file diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs index bd8895d3a0..0991aca377 100644 --- a/src/Umbraco.Web/Editors/MemberController.cs +++ b/src/Umbraco.Web/Editors/MemberController.cs @@ -157,6 +157,20 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay)); } + //Now let's do the role provider stuff + var currGroups = Roles.GetRolesForUser(contentItem.PersistedContent.Username); + //find the ones to remove and remove them + var toRemove = currGroups.Except(contentItem.Groups).ToArray(); + if (toRemove.Any()) + { + Roles.RemoveUserFromRoles(contentItem.PersistedContent.Username, toRemove); + } + if (contentItem.Groups.Any()) + { + //add the ones submitted + Roles.AddUserToRoles(contentItem.PersistedContent.Username, contentItem.Groups.ToArray()); + } + //save the item //NOTE: We are setting the password to NULL - this indicates to the system to not actually save the password // so it will not get overwritten! diff --git a/src/Umbraco.Web/Models/ContentEditing/MemberSave.cs b/src/Umbraco.Web/Models/ContentEditing/MemberSave.cs index ba2d91ab23..f0f7bb6d7d 100644 --- a/src/Umbraco.Web/Models/ContentEditing/MemberSave.cs +++ b/src/Umbraco.Web/Models/ContentEditing/MemberSave.cs @@ -1,4 +1,5 @@ -using System.Runtime.Serialization; +using System.Collections.Generic; +using System.Runtime.Serialization; using Newtonsoft.Json.Linq; using Umbraco.Core.Models; using Umbraco.Core.Models.Validation; @@ -25,5 +26,8 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "password")] public ChangingPasswordModel Password { get; set; } + + [DataMember(Name = "memberGroups")] + public IEnumerable Groups { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs index 9fef0209ae..d3056f8a27 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Web.Security; using AutoMapper; using Umbraco.Core; @@ -66,7 +67,7 @@ namespace Umbraco.Web.Models.Mapping /// /// private static void MapGenericCustomProperties(IMember member, MemberDisplay display) - { + { TabsAndPropertiesResolver.MapGenericProperties( member, display, new ContentPropertyDisplay @@ -77,6 +78,14 @@ namespace Umbraco.Web.Models.Mapping View = "textbox", Config = new Dictionary {{"IsRequired", true}} }, + new ContentPropertyDisplay + { + Alias = string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = ui.Text("general", "email"), + Value = display.Email, + View = "email", + Config = new Dictionary {{"IsRequired", true}} + }, new ContentPropertyDisplay { Alias = string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix), @@ -89,22 +98,40 @@ namespace Umbraco.Web.Models.Mapping //TODO: Hard coding this because the changepassword doesn't necessarily need to be a resolvable (real) property editor View = "changepassword", Config = new Dictionary( - //initialize the dictionary with the configuration from the default membership provider - Membership.Provider.GetConfiguration()) + //initialize the dictionary with the configuration from the default membership provider + Membership.Provider.GetConfiguration()) { //the password change toggle will only be displayed if there is already a password assigned. - {"hasPassword", member.Password.IsNullOrWhiteSpace() == false} + {"hasPassword", member.Password.IsNullOrWhiteSpace() == false} } }, new ContentPropertyDisplay { - Alias = string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = ui.Text("general", "email"), - Value = display.Email, - View = "email", + Alias = string.Format("{0}membergroup", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = ui.Text("content", "membergroup"), + Value = GetMemberGroupValue(display.Username), + View = "membergroups", Config = new Dictionary {{"IsRequired", true}} }); + } + + internal static IDictionary GetMemberGroupValue(string username) + { + var result = new Dictionary(); + foreach (var role in Roles.GetAllRoles().Distinct()) + { + result.Add(role, false); + // if a role starts with __umbracoRole we won't show it as it's an internal role used for public access + if (role.StartsWith(Constants.Conventions.Member.InternalRolePrefix) == false) + { + if (Roles.IsUserInRole(username, role)) + { + result[role] = true; + } + } + } + return result; } /// diff --git a/src/Umbraco.Web/Security/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs index 6192f8471a..c4a55cd99f 100644 --- a/src/Umbraco.Web/Security/WebSecurity.cs +++ b/src/Umbraco.Web/Security/WebSecurity.cs @@ -104,8 +104,6 @@ namespace Umbraco.Web.Security return allowAction; } - private static readonly int UmbracoTimeOutInMinutes = GlobalSettings.TimeOutInMinutes; - private IUser _currentUser; /// diff --git a/src/Umbraco.Web/WebApi/Filters/UmbracoUserTimeoutFilterAttribute.cs b/src/Umbraco.Web/WebApi/Filters/UmbracoUserTimeoutFilterAttribute.cs index 012e2024fb..23739574ba 100644 --- a/src/Umbraco.Web/WebApi/Filters/UmbracoUserTimeoutFilterAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/UmbracoUserTimeoutFilterAttribute.cs @@ -15,6 +15,9 @@ namespace Umbraco.Web.WebApi.Filters { base.OnActionExecuted(actionExecutedContext); + //this can occur if an error has already occurred. + if (actionExecutedContext.Response == null) return; + var httpContextAttempt = actionExecutedContext.Request.TryGetHttpContext(); if (httpContextAttempt.Success) {