From 78121dd0f2e8d93daba1f68235f1d2e7df37e1dd Mon Sep 17 00:00:00 2001 From: AndyButland Date: Mon, 9 Apr 2018 08:52:54 +0200 Subject: [PATCH] Re-send of invitation from user profile. Allow delete of users that haven't logged in from user profile. Prevent disable/enable and change password options from user profile for invited users. --- .../src/common/resources/users.resource.js | 33 ++++++++++++- .../src/views/users/user.controller.js | 46 ++++++++++++++++++- .../src/views/users/views/user/details.html | 34 ++++++++++++-- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 7 +++ .../umbraco/config/lang/en_us.xml | 7 +++ src/Umbraco.Web/Editors/UsersController.cs | 32 +++++++++++++ 6 files changed, 154 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js index 72564398c0..0fd308ffd0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js @@ -280,7 +280,7 @@ * }); * * - * @param {Array} id user id. + * @param {Int} userId user id. * @returns {Promise} resourcePromise object containing the user. * */ @@ -406,6 +406,36 @@ "Failed to save user"); } + /** + * @ngdoc method + * @name umbraco.resources.usersResource#deleteNonLoggedInUser + * @methodOf umbraco.resources.usersResource + * + * @description + * Deletes a user that hasn't already logged in (and hence we know has made no content updates that would create related records) + * + * ##usage + *
+          * usersResource.deleteNonLoggedInUser(1)
+          *    .then(function() {
+          *        alert("user was deleted");
+          *    });
+          * 
+ * + * @param {Int} userId user id. + * @returns {Promise} resourcePromise object. + * + */ + function deleteNonLoggedInUser(userId) { + + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "userApiBaseUrl", + "PostDeleteNonLoggedInUser", { id: userId })), + 'Failed to delete the user ' + userId); + } + var resource = { disableUsers: disableUsers, @@ -417,6 +447,7 @@ createUser: createUser, inviteUser: inviteUser, saveUser: saveUser, + deleteNonLoggedInUser: deleteNonLoggedInUser, clearAvatar: clearAvatar }; diff --git a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js index 0f3519c7ba..a1b30e38b7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js @@ -31,6 +31,8 @@ vm.disableUser = disableUser; vm.enableUser = enableUser; vm.unlockUser = unlockUser; + vm.resendInvite = resendInvite; + vm.deleteNonLoggedInUser = deleteNonLoggedInUser; vm.changeAvatar = changeAvatar; vm.clearAvatar = clearAvatar; vm.save = save; @@ -49,7 +51,9 @@ "sections_users", "content_contentRoot", "media_mediaRoot", - "user_noStartNodes" + "user_noStartNodes", + "user_defaultInvitationMessage", + "user_deleteUserConfirmation" ]; localizationService.localizeMany(labelKeys).then(function (values) { @@ -61,6 +65,8 @@ vm.labels.contentRoot = values[5]; vm.labels.mediaRoot = values[6]; vm.labels.noStartNodes = values[7]; + vm.labels.defaultInvitationMessage = values[8]; + vm.labels.deleteUserConfirmation = values[9]; }); // get user @@ -350,6 +356,44 @@ }); } + function resendInvite() { + vm.resendInviteButtonState = "busy"; + + if (vm.resendInviteMessage) { + vm.user.message = vm.resendInviteMessage; + } + else { + vm.user.message = vm.labels.defaultInvitationMessage; + } + + usersResource.inviteUser(vm.user).then(function (data) { + vm.resendInviteButtonState = "success"; + vm.resendInviteMessage = ""; + formHelper.showNotifications(data); + }, function (error) { + vm.resendInviteButtonState = "error"; + formHelper.showNotifications(error.data); + }); + } + + function deleteNonLoggedInUser() { + vm.deleteNotLoggedInUserButtonState = "busy"; + + var confirmationMessage = vm.labels.deleteUserConfirmation; + if (!confirm(confirmationMessage)) { + vm.deleteNotLoggedInUserButtonState = "danger"; + return; + } + + usersResource.deleteNonLoggedInUser(vm.user.id).then(function (data) { + formHelper.showNotifications(data); + goToPage(vm.breadcrumbs[0]); + }, function (error) { + vm.deleteNotLoggedInUserButtonState = "error"; + formHelper.showNotifications(error.data); + }); + } + function clearAvatar() { // get user usersResource.clearAvatar(vm.user.id).then(function (data) { diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html b/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html index 67f204aa2f..8b9be7acdc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html @@ -239,7 +239,7 @@
-
- + + + +
+ + + +
+
Last login: @@ -358,4 +386,4 @@
-
\ No newline at end of file + diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 0137e5d0e0..bdc2b0cd6f 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -46,6 +46,7 @@ Set permissions Unlock Create Content Template + Resend Invitation Content @@ -1419,6 +1420,9 @@ To manage your website, simply open the Umbraco back office and start adding con An error occurred while unlocking the user Member was exported to file An error occurred while exporting the member + User %0% was deleted + Invite user + Invitation has been re-sent to %0% Uses CSS syntax ex: h1, .redHeader, .blueTex @@ -1996,6 +2000,9 @@ To manage your website, simply open the Umbraco back office and start adding con ]]> Invite + Resending invitation... + Delete User + Are you sure you wish to delete this user account? diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml index 1ff807b79b..e1db8003f6 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -46,6 +46,7 @@ Set permissions Unlock Create Content Template + Resend Invitation Content @@ -1416,6 +1417,9 @@ To manage your website, simply open the Umbraco back office and start adding con An error occurred while unlocking the user Member was exported to file An error occurred while exporting the member + User %0% was deleted + Invite user + Invitation has been re-sent to %0% Uses CSS syntax ex: h1, .redHeader, .blueTex @@ -1988,6 +1992,9 @@ To manage your website, simply open the Umbraco back office and start adding con ]]> Invite + Resending invitation... + Delete User + Are you sure you wish to delete this user account? Validation diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs index f1670df346..f089c44afd 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -412,6 +412,8 @@ namespace Umbraco.Web.Editors await SendUserInviteEmailAsync(display, Security.CurrentUser.Name, Security.CurrentUser.Email, user, userSave.Message); + display.AddSuccessNotification(Services.TextService.Localize("speechBubbles/resendInviteHeader"), Services.TextService.Localize("speechBubbles/resendInviteSuccess", new[] { user.Name })); + return display; } @@ -699,6 +701,36 @@ namespace Umbraco.Web.Editors Services.TextService.Localize("speechBubbles/setUserGroupOnUsersSuccess")); } + /// + /// Deletes the non-logged in user provided id + /// + /// User Id + /// + /// Limited to users that haven't logged in to avoid issues with related records constrained + /// with a foreign key on the user Id + /// + public async Task PostDeleteNonLoggedInUser(int id) + { + var user = Services.UserService.GetUserById(id); + if (user == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + // Check user hasn't logged in. If they have they may have made content changes which will mean + // the Id is associated with audit trails, versions etc. and can't be removed. + if (user.LastLoginDate != default(DateTime)) + { + throw new HttpResponseException(HttpStatusCode.BadRequest); + } + + var userName = user.Name; + Services.UserService.Delete(user, true); + + return Request.CreateNotificationSuccessResponse( + Services.TextService.Localize("speechBubbles/deleteUserSuccess", new[] { userName })); + } + public class PagedUserResult : PagedResult { public PagedUserResult(long totalItems, long pageNumber, long pageSize) : base(totalItems, pageNumber, pageSize)