From ea8374adb771e9b4d5c5cce5cdebefcbf5ca79aa Mon Sep 17 00:00:00 2001 From: MK Fain Date: Wed, 5 Aug 2020 22:53:23 +0000 Subject: [PATCH] Admin manage pending accounts --- src/api/__mocks__/users.js | 15 ++++-- src/api/users.js | 10 ++++ src/lang/en.js | 11 ++++ src/store/modules/users.js | 14 ++++- .../users/components/ModerationDropdown.vue | 30 +++++++++++ .../users/components/MultipleUsersMenu.vue | 29 +++++++++++ src/views/users/components/UsersFilter.vue | 9 ++-- src/views/users/index.vue | 24 +++++++++ src/views/users/show.vue | 13 ++++- test/views/users/index.test.js | 10 ++-- test/views/users/multipleUsersMenu.test.js | 51 +++++++++++++++---- test/views/users/usersFilters.test.js | 16 +++--- 12 files changed, 200 insertions(+), 32 deletions(-) diff --git a/src/api/__mocks__/users.js b/src/api/__mocks__/users.js index 1133c705..b21cac33 100644 --- a/src/api/__mocks__/users.js +++ b/src/api/__mocks__/users.js @@ -1,7 +1,8 @@ export let users = [ - { active: true, deactivated: false, id: '2', nickname: 'allis', local: true, external: false, roles: { admin: true, moderator: false }, tags: [] }, - { active: true, deactivated: false, id: '10', nickname: 'bob', local: false, external: true, roles: { admin: false, moderator: false }, tags: ['mrf_tag:sandbox'] }, - { active: false, deactivated: true, id: 'abc', nickname: 'john', local: true, external: false, roles: { admin: false, moderator: false }, tags: ['mrf_tag:media-strip'] } + { active: true, approval_pending: false, deactivated: false, id: '2', nickname: 'allis', local: true, external: false, roles: { admin: true, moderator: false }, tags: [] }, + { active: true, approval_pending: false, deactivated: false, id: '10', nickname: 'bob', local: false, external: true, roles: { admin: false, moderator: false }, tags: ['mrf_tag:sandbox'] }, + { active: false, approval_pending: false, deactivated: true, id: 'abc', nickname: 'john', local: true, external: false, roles: { admin: false, moderator: false }, tags: ['mrf_tag:media-strip'] }, + { active: true, approval_pending: true, deactivated: false, id: '100', nickname: 'sally', local: true, external: false, roles: { admin: false, moderator: false }, tags: [] } ] const userProfile = { avatar: 'avatar.jpg', nickname: 'allis', id: '2', tags: [], roles: { admin: true, moderator: false }, local: true, external: false } @@ -85,6 +86,14 @@ export async function deactivateUsers(nicknames, authHost, token) { return Promise.resolve({ data: response }) } +export async function approveUserAccount(nicknames, authHost, token) { + const response = nicknames.map(nickname => { + const currentUser = users.find(user => user.nickname === nickname) + return { ...currentUser, approval_pending: false } + }) + return Promise.resolve({ data: response }) +} + export async function deleteRight(nickname, right, authHost, token) { return Promise.resolve({ data: { [`is_${right}`]: false } diff --git a/src/api/users.js b/src/api/users.js index b62df5d7..71028fd7 100644 --- a/src/api/users.js +++ b/src/api/users.js @@ -166,6 +166,16 @@ export async function fetchUserStatuses(id, authHost, godmode, token) { }) } +export async function approveUserAccount(nicknames, authHost, token) { + return await request({ + baseURL: baseName(authHost), + url: '/api/pleroma/admin/users/approve', + method: 'patch', + headers: authHeaders(token), + data: { nicknames } + }) +} + export async function confirmUserEmail(nicknames, authHost, token) { return await request({ baseURL: baseName(authHost), diff --git a/src/lang/en.js b/src/lang/en.js index 478a5cc7..d86a3a1e 100644 --- a/src/lang/en.js +++ b/src/lang/en.js @@ -191,6 +191,7 @@ export default { external: 'External', deactivated: 'Deactivated', active: 'Active', + unapproved: 'Pending', unconfirmed: 'Unconfirmed', actions: 'Actions', activate: 'Activate', @@ -233,6 +234,8 @@ export default { addTagForMultipleUsersConfirmation: 'Are you sure you want to apply tag to all selected users?', removeTagFromMultipleUsersConfirmation: 'Are you sure you want to remove tag from all selected users?', requirePasswordResetConfirmation: 'Are you sure you want to require password reset for all selected users?', + approveAccountsConfirmation: 'Are you sure you want to approve accounts for all selected users?', + rejectAccountsConfirmation: 'Are you sure you want to reject accounts for all selected users?', confirmAccountsConfirmation: 'Are you sure you want to confirm emails for all selected users?', resendEmailConfirmation: 'Are you sure you want to resend confirmation email for all selected users?', mailerMustBeEnabled: 'To require user\'s password reset you must enable mailer.', @@ -253,9 +256,14 @@ export default { getPasswordResetToken: 'Get password reset token', passwordResetTokenCreated: 'Password reset token was created', accountCreated: 'New account was created!', + approveAccount: 'Approve account', + approveAccounts: 'Approve accounts', + unapprovedAccount: 'User account is pending approval', unconfirmedEmail: 'User didn\'t confirm the email', confirmAccount: 'Confirm account', confirmAccounts: 'Confirm accounts', + rejectAccount: 'Reject account', + rejectAccounts: 'Reject accounts', resendConfirmation: 'Resend confirmation email', invalidAccount: 'This account has invalid nickname and can\'t be modified', invalidNickname: 'invalid nickname', @@ -287,7 +295,9 @@ export default { roles: 'Roles', active: 'Active', status: 'Status', + reason: 'Registration Reason', deactivated: 'Deactivated', + pending: 'Pending', noStatuses: 'No statuses to show', openAccountInInstance: 'Open account in instance', securitySettings: { @@ -313,6 +323,7 @@ export default { external: 'External', byStatus: 'By status', active: 'Active', + pending: 'Pending', deactivated: 'Deactivated' }, reports: { diff --git a/src/store/modules/users.js b/src/store/modules/users.js index 4db8042f..ccfc10b3 100644 --- a/src/store/modules/users.js +++ b/src/store/modules/users.js @@ -14,6 +14,7 @@ import { tagUser, untagUser, forcePasswordReset, + approveUserAccount, confirmUserEmail, resendConfirmationEmail } from '@/api/users' @@ -30,6 +31,7 @@ const users = { local: false, external: false, active: false, + needApproval: false, deactivated: false }, passwordResetToken: { @@ -126,7 +128,7 @@ const users = { }, ClearUsersState({ commit }) { commit('SET_SEARCH_QUERY', '') - commit('SET_USERS_FILTERS', { local: false, external: false, active: false, deactivated: false }) + commit('SET_USERS_FILTERS', { local: false, external: false, active: false, needApproval: false, deactivated: false }) }, async ClearFilters({ commit, dispatch, state }) { commit('CLEAR_USERS_FILTERS') @@ -159,6 +161,15 @@ const users = { } dispatch('SuccessMessage') }, + async ApproveUsersAccount({ dispatch, getters }, { users, _userId, _statusId }) { + const updatedUsers = users.map(user => { + return { ...user, approval_pending: false } + }) + const nicknames = users.map(user => user.nickname) + const callApiFn = async() => await approveUserAccount(nicknames, getters.authHost, getters.token) + + dispatch('ApplyChanges', { updatedUsers, callApiFn, userId: _userId, statusId: _statusId }) + }, async ConfirmUsersEmail({ dispatch, getters }, { users, _userId, _statusId }) { const updatedUsers = users.map(user => { return { ...user, confirmation_pending: false } @@ -260,6 +271,7 @@ const users = { local: false, external: false, active: false, + needApproval: false, deactivated: false } const currentFilters = { ...defaultFilters, ...filters } diff --git a/src/views/users/components/ModerationDropdown.vue b/src/views/users/components/ModerationDropdown.vue index 8b800c46..b8312b4c 100644 --- a/src/views/users/components/ModerationDropdown.vue +++ b/src/views/users/components/ModerationDropdown.vue @@ -37,6 +37,17 @@ @click.native="handleDeletion(user)"> {{ $t('users.deleteAccount') }} + + {{ $t('users.approveAccount') }} + + + {{ $t('users.rejectAccount') }} + { + this.$store.dispatch('DeleteUsers', { users: [user], _userId: user.id }) + }).catch(() => { + this.$message({ + type: 'info', + message: 'Delete canceled' + }) + }) + }, handleEmailConfirmation(user) { this.$store.dispatch('ConfirmUsersEmail', { users: [user], _userId: user.id, _statusId: this.statusId }) }, diff --git a/src/views/users/components/MultipleUsersMenu.vue b/src/views/users/components/MultipleUsersMenu.vue index a192b8a8..652436a5 100644 --- a/src/views/users/components/MultipleUsersMenu.vue +++ b/src/views/users/components/MultipleUsersMenu.vue @@ -26,6 +26,15 @@ @click.native="revokeRightFromMultipleUsers('moderator')"> {{ $t('users.revokeModerator') }} + + {{ $t('users.approveAccounts') }} + + + {{ $t('users.rejectAccounts') }} + @@ -219,6 +228,12 @@ export default { applyAction(filtered, requirePasswordResetFn) }, + approveAccounts: () => { + const filtered = this.selectedUsers.filter(user => this.isLocalUser(user) && user.approval_pending) + const approveAccountFn = async(users) => await this.$store.dispatch('ApproveUsersAccount', { users }) + + applyAction(filtered, approveAccountFn) + }, confirmAccounts: () => { const filtered = this.selectedUsers.filter(user => this.isLocalUser(user) && user.confirmation_pending) const confirmAccountFn = async(users) => await this.$store.dispatch('ConfirmUsersEmail', { users }) @@ -300,6 +315,20 @@ export default { removeTag(tag) ) }, + approveAccountsForMultipleUsers() { + const { approveAccounts } = this.mappers() + this.confirmMessage( + this.$t('users.approveAccountsConfirmation'), + approveAccounts + ) + }, + rejectAccountsForMultipleUsers() { + const { remove } = this.mappers() + this.confirmMessage( + this.$t('users.deleteMultipleUsersConfirmation'), + remove + ) + }, confirmAccountsForMultipleUsers() { const { confirmAccounts } = this.mappers() this.confirmMessage( diff --git a/src/views/users/components/UsersFilter.vue b/src/views/users/components/UsersFilter.vue index 01bd384e..424b7071 100644 --- a/src/views/users/components/UsersFilter.vue +++ b/src/views/users/components/UsersFilter.vue @@ -7,12 +7,13 @@ class="select-field" @change="toggleFilters"> - {{ $t('usersFilter.local') }} - {{ $t('usersFilter.external') }} + {{ $t('usersFilter.local') }} + {{ $t('usersFilter.external') }} - {{ $t('usersFilter.active') }} - {{ $t('usersFilter.deactivated') }} + {{ $t('usersFilter.active') }} + {{ $t('usersFilter.pending') }} + {{ $t('usersFilter.deactivated') }} diff --git a/src/views/users/index.vue b/src/views/users/index.vue index c8ea487b..3831ba80 100644 --- a/src/views/users/index.vue +++ b/src/views/users/index.vue @@ -71,7 +71,16 @@ {{ isDesktop ? $t('users.unconfirmed') : getFirstLetter($t('users.unconfirmed')) }} + + + {{ isDesktop ? $t('users.unapproved') : getFirstLetter($t('users.unapproved')) }} + + +
+ "{{ scope.row.registration_reason | truncate(100, '...') }}" +
+