Merge pull request #2122 from umbraco/temp-U4-10243

U4-10243 Umbraco 7.7.  Difference between "Enable/Disable" and "LockedOut"
This commit is contained in:
Shannon Deminick
2017-08-15 12:31:50 +10:00
committed by GitHub
9 changed files with 373 additions and 30 deletions

View File

@@ -545,6 +545,9 @@ namespace Umbraco.Core.Security
/// </summary>
/// <param name="user"/><param name="lockoutEnd"/>
/// <returns/>
/// <remarks>
/// Currently we do not suport a timed lock out, when they are locked out, an admin will have to reset the status
/// </remarks>
public Task SetLockoutEndDateAsync(BackOfficeIdentityUser user, DateTimeOffset lockoutEnd)
{
if (user == null) throw new ArgumentNullException("user");

View File

@@ -4,13 +4,33 @@
* @function
*
* @description
* Used by the users section to get users and send requests to create, invite, delete, etc. users.
* Used by the users section to get users and send requests to create, invite, disable, etc. users.
*/
(function () {
'use strict';
function usersResource($http, umbRequestHelper, $q, umbDataFormatter) {
/**
* @ngdoc method
* @name umbraco.resources.usersResource#clearAvatar
* @methodOf umbraco.resources.usersResource
*
* @description
* Deletes the user avatar
*
* ##usage
* <pre>
* usersResource.clearAvatar(1)
* .then(function() {
* alert("avatar is gone");
* });
* </pre>
*
* @param {Array} id id of user.
* @returns {Promise} resourcePromise object.
*
*/
function clearAvatar(userId) {
return umbRequestHelper.resourcePromise(
@@ -22,6 +42,26 @@
'Failed to clear the user avatar ' + userId);
}
/**
* @ngdoc method
* @name umbraco.resources.usersResource#disableUsers
* @methodOf umbraco.resources.usersResource
*
* @description
* Disables a collection of users
*
* ##usage
* <pre>
* usersResource.disableUsers([1, 2, 3, 4, 5])
* .then(function() {
* alert("users were disabled");
* });
* </pre>
*
* @param {Array} ids ids of users to disable.
* @returns {Promise} resourcePromise object.
*
*/
function disableUsers(userIds) {
if (!userIds) {
throw "userIds not specified";
@@ -39,6 +79,26 @@
'Failed to disable the users ' + userIds.join(","));
}
/**
* @ngdoc method
* @name umbraco.resources.usersResource#enableUsers
* @methodOf umbraco.resources.usersResource
*
* @description
* Enables a collection of users
*
* ##usage
* <pre>
* usersResource.enableUsers([1, 2, 3, 4, 5])
* .then(function() {
* alert("users were enabled");
* });
* </pre>
*
* @param {Array} ids ids of users to enable.
* @returns {Promise} resourcePromise object.
*
*/
function enableUsers(userIds) {
if (!userIds) {
throw "userIds not specified";
@@ -55,6 +115,63 @@
'Failed to enable the users ' + userIds.join(","));
}
/**
* @ngdoc method
* @name umbraco.resources.usersResource#unlockUsers
* @methodOf umbraco.resources.usersResource
*
* @description
* Unlocks a collection of users
*
* ##usage
* <pre>
* usersResource.unlockUsers([1, 2, 3, 4, 5])
* .then(function() {
* alert("users were unlocked");
* });
* </pre>
*
* @param {Array} ids ids of users to unlock.
* @returns {Promise} resourcePromise object.
*
*/
function unlockUsers(userIds) {
if (!userIds) {
throw "userIds not specified";
}
//we need to create a custom query string for the usergroup array, so create it now and we can append the user groups if needed
var qry = "userIds=" + userIds.join("&userIds=");
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"userApiBaseUrl",
"PostUnlockUsers", qry)),
'Failed to enable the users ' + userIds.join(","));
}
/**
* @ngdoc method
* @name umbraco.resources.usersResource#setUserGroupsOnUsers
* @methodOf umbraco.resources.usersResource
*
* @description
* Overwrites the existing user groups on a collection of users
*
* ##usage
* <pre>
* usersResource.setUserGroupsOnUsers(['admin', 'editor'], [1, 2, 3, 4, 5])
* .then(function() {
* alert("users were updated");
* });
* </pre>
*
* @param {Array} userGroupAliases aliases of user groups.
* @param {Array} ids ids of users to update.
* @returns {Promise} resourcePromise object.
*
*/
function setUserGroupsOnUsers(userGroups, userIds) {
var userGroupAliases = userGroups.map(function(o) { return o.alias; });
var query = "userGroupAliases=" + userGroupAliases.join("&userGroupAliases=") + "&userIds=" + userIds.join("&userIds=");
@@ -67,6 +184,34 @@
'Failed to set user groups ' + userGroupAliases.join(",") + ' on the users ' + userIds.join(","));
}
/**
* @ngdoc method
* @name umbraco.resources.usersResource#getPagedResults
* @methodOf umbraco.resources.usersResource
*
* @description
* Get users
*
* ##usage
* <pre>
* usersResource.getPagedResults({pageSize: 10, pageNumber: 2})
* .then(function(data) {
* var users = data.items;
* alert('they are here!');
* });
* </pre>
*
* @param {Object} options optional options object
* @param {Int} options.pageSize if paging data, number of users per page, default = 25
* @param {Int} options.pageNumber if paging data, current page index, default = 1
* @param {String} options.filter if provided, query will only return those with names matching the filter
* @param {String} options.orderDirection can be `Ascending` or `Descending` - Default: `Ascending`
* @param {String} options.orderBy property to order users by, default: `Username`
* @param {Array} options.userGroups property to filter users by user group
* @param {Array} options.userStates property to filter users by user state
* @returns {Promise} resourcePromise object containing an array of content items.
*
*/
function getPagedResults(options) {
var defaults = {
pageSize: 25,
@@ -119,6 +264,26 @@
'Failed to retrieve users paged result');
}
/**
* @ngdoc method
* @name umbraco.resources.usersResource#getUser
* @methodOf umbraco.resources.usersResource
*
* @description
* Gets a user
*
* ##usage
* <pre>
* usersResource.getUser(1)
* .then(function(user) {
* alert("It's here");
* });
* </pre>
*
* @param {Array} id user id.
* @returns {Promise} resourcePromise object containing the user.
*
*/
function getUser(userId) {
return umbRequestHelper.resourcePromise(
@@ -130,6 +295,26 @@
"Failed to retrieve data for user " + userId);
}
/**
* @ngdoc method
* @name umbraco.resources.usersResource#createUser
* @methodOf umbraco.resources.usersResource
*
* @description
* Creates a new user
*
* ##usage
* <pre>
* usersResource.createUser(user)
* .then(function(newUser) {
* alert("It's here");
* });
* </pre>
*
* @param {Object} user user to create
* @returns {Promise} resourcePromise object containing the new user.
*
*/
function createUser(user) {
if (!user) {
throw "user not specified";
@@ -147,6 +332,26 @@
"Failed to save user");
}
/**
* @ngdoc method
* @name umbraco.resources.usersResource#inviteUser
* @methodOf umbraco.resources.usersResource
*
* @description
* Creates and sends an email invitation to a new user
*
* ##usage
* <pre>
* usersResource.inviteUser(user)
* .then(function(newUser) {
* alert("It's here");
* });
* </pre>
*
* @param {Object} user user to invite
* @returns {Promise} resourcePromise object containing the new user.
*
*/
function inviteUser(user) {
if (!user) {
throw "user not specified";
@@ -164,6 +369,26 @@
"Failed to invite user");
}
/**
* @ngdoc method
* @name umbraco.resources.usersResource#saveUser
* @methodOf umbraco.resources.usersResource
*
* @description
* Saves a user
*
* ##usage
* <pre>
* usersResource.saveUser(user)
* .then(function(updatedUser) {
* alert("It's here");
* });
* </pre>
*
* @param {Object} user object to save
* @returns {Promise} resourcePromise object containing the updated user.
*
*/
function saveUser(user) {
if (!user) {
throw "user not specified";
@@ -185,6 +410,7 @@
var resource = {
disableUsers: disableUsers,
enableUsers: enableUsers,
unlockUsers: unlockUsers,
setUserGroupsOnUsers: setUserGroupsOnUsers,
getPagedResults: getPagedResults,
getUser: getUser,

View File

@@ -9,7 +9,6 @@
display: inline-flex;
align-items: center;
justify-content: center;
text-transform: capitalize;
}
// Colors

View File

@@ -30,6 +30,7 @@
vm.removeSelectedItem = removeSelectedItem;
vm.disableUser = disableUser;
vm.enableUser = enableUser;
vm.unlockUser = unlockUser;
vm.clearAvatar = clearAvatar;
vm.save = save;
vm.toggleChangePassword = toggleChangePassword;
@@ -254,11 +255,11 @@
function disableUser() {
vm.disableUserButtonState = "busy";
usersResource.disableUsers([vm.user.id]).then(function (data) {
vm.user.userState = 1;
setUserDisplayState();
vm.disableUserButtonState = "success";
formHelper.showNotifications(data);
}, function(error){
vm.user.userState = 1;
setUserDisplayState();
vm.disableUserButtonState = "success";
formHelper.showNotifications(data);
}, function (error) {
vm.disableUserButtonState = "error";
formHelper.showNotifications(error.data);
});
@@ -267,16 +268,28 @@
function enableUser() {
vm.enableUserButtonState = "busy";
usersResource.enableUsers([vm.user.id]).then(function (data) {
vm.user.userState = 0;
setUserDisplayState();
vm.enableUserButtonState = "success";
formHelper.showNotifications(data);
}, function(error){
vm.disableUserButtonState = "error";
vm.user.userState = 0;
setUserDisplayState();
vm.enableUserButtonState = "success";
formHelper.showNotifications(data);
}, function (error) {
vm.enableUserButtonState = "error";
formHelper.showNotifications(error.data);
});
}
function unlockUser() {
vm.unlockUserButtonState = "busy";
usersResource.unlockUsers([vm.user.id]).then(function (data) {
vm.user.userState = 0;
setUserDisplayState();
vm.unlockUserButtonState = "success";
formHelper.showNotifications(data);
}, function (error) {
vm.unlockUserButtonState = "error";
formHelper.showNotifications(error.data);
});
}
function clearAvatar() {
// get user

View File

@@ -1,6 +1,8 @@
<div ng-controller="Umbraco.Editors.Users.UserController as vm" class="clearfix">
<umb-load-indicator ng-show="vm.loading"></umb-load-indicator>
<umb-load-indicator
ng-if="vm.loading">
</umb-load-indicator>
<form name="editUserForm" novalidate val-form-manager>
@@ -234,29 +236,43 @@
</div>
<div style="margin-top: 20px;">
<div style="margin-bottom: 10px;">
<umb-button
ng-if="vm.user.userDisplayState.key === 'Active' && !vm.user.isCurrentUser"
type="button"
button-style="[danger,block]"
action="vm.disableUser()"
state="vm.disableUserButtonState"
label="Disable"
label-key="actions_disable"
size="m">
</umb-button>
</div>
<div style="margin-bottom: 10px;">
<umb-button
ng-if="vm.user.userDisplayState.key === 'Disabled' && !vm.user.isCurrentUser || vm.user.userDisplayState.key === 'LockedOut' && !vm.user.isCurrentUser"
ng-if="vm.user.userDisplayState.key === 'Disabled' && !vm.user.isCurrentUser"
type="button"
button-style="[success,block]"
state="vm.enableUserButtonState"
action="vm.enableUser()"
label="Enable"
label-key="actions_enable"
size="m">
size="s">
</umb-button>
</div>
<div style="margin-bottom: 10px;">
<umb-button
ng-if="vm.user.userDisplayState.key === 'LockedOut' && !vm.user.isCurrentUser"
type="button"
button-style="[success,block]"
state="vm.unlockUserButtonState"
action="vm.unlockUser()"
label="Unlock"
label-key="actions_unlock"
size="s">
</umb-button>
</div>
<div style="margin-bottom: 10px;">
<umb-button
ng-if="vm.user.userDisplayState.key !== 'Disabled' && !vm.user.isCurrentUser"
type="button"
button-style="[info,block]"
action="vm.disableUser()"
state="vm.disableUserButtonState"
label="Disable"
label-key="actions_disable"
size="s">
</umb-button>
</div>
@@ -268,7 +284,7 @@
label-key="general_changePassword"
state="changePasswordButtonState"
ng-if="vm.changePasswordModel.isChanging === false"
size="m">
size="s">
</umb-button>
<ng-form ng-if="vm.changePasswordModel.isChanging" name="passwordForm" class="block-form" val-form-manager>

View File

@@ -28,6 +28,7 @@
vm.allowDisableUser = true;
vm.allowEnableUser = true;
vm.allowUnlockUser = true;
vm.allowSetUserGroup = true;
vm.layouts = [
@@ -76,6 +77,7 @@
vm.clickUser = clickUser;
vm.disableUsers = disableUsers;
vm.enableUsers = enableUsers;
vm.unlockUsers = unlockUsers;
vm.openBulkUserGroupPicker = openBulkUserGroupPicker;
vm.openUserGroupPicker = openUserGroupPicker;
vm.removeSelectedUserGroup = removeSelectedUserGroup;
@@ -241,6 +243,28 @@
});
}
function unlockUsers() {
vm.unlockUserButtonState = "busy";
usersResource.unlockUsers(vm.selection).then(function (data) {
// update userState
angular.forEach(vm.selection, function (userId) {
var user = getUserFromArrayById(userId, vm.users);
if (user) {
user.userState = 0;
}
});
// show the correct badges
setUserDisplayState(vm.users);
// show notification
formHelper.showNotifications(data);
vm.unlockUserButtonState = "init";
clearSelection();
}, function (error) {
vm.unlockUserButtonState = "error";
formHelper.showNotifications(error.data);
});
}
function getUserFromArrayById(userId, users) {
return _.find(users, function (u) { return u.id === userId });
}
@@ -560,6 +584,7 @@
// reset all states
vm.allowDisableUser = true;
vm.allowEnableUser = true;
vm.allowUnlockUser = true;
vm.allowSetUserGroup = true;
var firstSelectedUserGroups;
@@ -574,6 +599,7 @@
if (user.isCurrentUser) {
vm.allowDisableUser = false;
vm.allowEnableUser = false;
vm.allowUnlockUser = false;
vm.allowSetUserGroup = false;
return;
}
@@ -590,6 +616,14 @@
vm.allowEnableUser = false;
}
if (user.userDisplayState && user.userDisplayState.key === "LockedOut") {
vm.allowEnableUser = false;
}
if (user.userDisplayState && user.userDisplayState.key !== "LockedOut") {
vm.allowUnlockUser = false;
}
// store the user group aliases of the first selected user
if (!firstSelectedUserGroups) {
firstSelectedUserGroups = user.userGroups.map(function (ug) { return ug.alias; });

View File

@@ -81,6 +81,17 @@
action="vm.enableUsers()">
</umb-button>
</div>
<div style="margin-right: 5px;">
<umb-button
ng-if="vm.allowUnlockUser"
type="button"
size="xs"
state="vm.unlockUserButtonState"
label-key="actions_unlock"
icon="icon-unlocked"
action="vm.unlockUsers()">
</umb-button>
</div>
<div>
<umb-button
ng-if="vm.allowDisableUser"

View File

@@ -42,6 +42,7 @@
<key alias="translate">Translate</key>
<key alias="update">Update</key>
<key alias="setPermissions">Set permissions</key>
<key alias="unlock">Unlock</key>
</area>
<area alias="actionCategories">
<key alias="content">Content</key>
@@ -1216,6 +1217,10 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="setUserGroupOnUsersSuccess">User groups have been set</key>
<key alias="deleteUserGroupsSuccess">Deleted %0% user groups</key>
<key alias="deleteUserGroupSuccess">%0% was deleted</key>
<key alias="unlockUsersSuccess">Unlocked %0% users</key>
<key alias="unlockUsersError">An error occurred while unlocking the users</key>
<key alias="unlockUserSuccess">%0% is now unlocked</key>
<key alias="unlockUserError">An error occurred while unlocking the user</key>
</area>
<area alias="stylesheet">
<key alias="aliasHelp">Uses CSS syntax ex: h1, .redHeader, .blueTex</key>

View File

@@ -557,6 +557,42 @@ namespace Umbraco.Web.Editors
return Request.CreateNotificationSuccessResponse(
Services.TextService.Localize("speechBubbles/enableUserSuccess", new[] { users[0].Name }));
}
/// <summary>
/// Unlocks the users with the given user ids
/// </summary>
/// <param name="userIds"></param>
public async Task<HttpResponseMessage> PostUnlockUsers([FromUri]int[] userIds)
{
if (userIds.Length <= 0)
return Request.CreateResponse(HttpStatusCode.OK);
if (userIds.Length == 1)
{
var unlockResult = await UserManager.SetLockoutEndDateAsync(userIds[0], DateTimeOffset.Now);
if (unlockResult.Succeeded == false)
{
return Request.CreateValidationErrorResponse(
string.Format("Could not unlock for user {0} - error {1}", userIds[0], unlockResult.Errors.First()));
}
var user = await UserManager.FindByIdAsync(userIds[0]);
return Request.CreateNotificationSuccessResponse(
Services.TextService.Localize("speechBubbles/unlockUserSuccess", new[] { user.Name }));
}
foreach (var u in userIds)
{
var unlockResult = await UserManager.SetLockoutEndDateAsync(u, DateTimeOffset.Now);
if (unlockResult.Succeeded == false)
{
return Request.CreateValidationErrorResponse(
string.Format("Could not unlock for user {0} - error {1}", u, unlockResult.Errors.First()));
}
}
return Request.CreateNotificationSuccessResponse(
Services.TextService.Localize("speechBubbles/unlockUsersSuccess", new[] { userIds.Length.ToString() }));
}
public HttpResponseMessage PostSetUserGroupsOnUsers([FromUri]string[] userGroupAliases, [FromUri]int[] userIds)