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)
+
+
+
+
\ 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)
{