diff --git a/CHANGELOG.md b/CHANGELOG.md index e368ae70..770681f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Ability to confirm users' emails and resend confirmation emails - Report notes - Ability to moderate users on the statuses page +- Ability to moderate user on the user's page ### Fixed diff --git a/src/api/users.js b/src/api/users.js index f3398c68..8298eeca 100644 --- a/src/api/users.js +++ b/src/api/users.js @@ -89,7 +89,7 @@ export async function getPasswordResetToken(nickname, authHost, token) { }) } -export async function requirePasswordReset(nicknames, authHost, token) { +export async function forcePasswordReset(nicknames, authHost, token) { return await request({ baseURL: baseName(authHost), url: `/api/pleroma/admin/users/force_password_reset`, diff --git a/src/lang/en.js b/src/lang/en.js index 1a8a8c90..fa4d3209 100644 --- a/src/lang/en.js +++ b/src/lang/en.js @@ -202,6 +202,7 @@ export default { disableAnySubscriptionForMultiple: 'Disallow following users at all', requirePasswordReset: 'Require password reset on next login', selectUsers: 'Select users to apply actions to multiple users', + moderateUser: 'Moderate user', moderateUsers: 'Moderate multiple users', createAccount: 'Create new account', apply: 'apply', diff --git a/src/store/modules/users.js b/src/store/modules/users.js index dccbb1ca..75b07402 100644 --- a/src/store/modules/users.js +++ b/src/store/modules/users.js @@ -12,7 +12,7 @@ import { searchUsers, tagUser, untagUser, - requirePasswordReset, + forcePasswordReset, confirmUserEmail, resendConfirmationEmail } from '@/api/users' @@ -79,53 +79,46 @@ const users = { } }, actions: { - async ActivateUsers({ commit, dispatch, getters, state }, users) { + async ActivateUsers({ dispatch, getters }, users) { const updatedUsers = users.map(user => { return { ...user, deactivated: false } }) + const nicknames = users.map(user => user.nickname) + const callApiFn = async() => await activateUsers(nicknames, getters.authHost, getters.token) + + dispatch('ApplyChanges', { updatedUsers, callApiFn, userId: users[0].id }) + }, + async ApplyChanges({ commit, dispatch, state }, { updatedUsers, callApiFn, userId }) { commit('SWAP_USERS', updatedUsers) - const usersNicknames = users.map(user => user.nickname) try { - await activateUsers(usersNicknames, getters.authHost, getters.token) + await callApiFn() } catch (_e) { return } finally { dispatch('SearchUsers', { query: state.searchQuery, page: state.currentPage }) } + + dispatch('FetchUserProfile', { userId, godmode: false }) dispatch('SuccessMessage') }, - async AddRight({ commit, dispatch, getters, state }, { users, right }) { + async AddRight({ dispatch, getters }, { users, right }) { const updatedUsers = users.map(user => { return user.local ? { ...user, roles: { ...user.roles, [right]: true }} : user }) - commit('SWAP_USERS', updatedUsers) + const nicknames = users.map(user => user.nickname) + const callApiFn = async() => await addRight(nicknames, right, getters.authHost, getters.token) - const usersNicknames = users.map(user => user.nickname) - try { - await addRight(usersNicknames, right, getters.authHost, getters.token) - } catch (_e) { - return - } finally { - dispatch('SearchUsers', { query: state.searchQuery, page: state.currentPage }) - } - dispatch('SuccessMessage') + dispatch('ApplyChanges', { updatedUsers, callApiFn, userId: users[0].id }) }, - async AddTag({ commit, dispatch, getters, state }, { users, tag }) { + async AddTag({ dispatch, getters }, { users, tag }) { const updatedUsers = users.map(user => { return { ...user, tags: [...user.tags, tag] } }) - commit('SWAP_USERS', updatedUsers) - const nicknames = users.map(user => user.nickname) - try { - await tagUser(nicknames, [tag], getters.authHost, getters.token) - } catch (_e) { - return - } finally { - dispatch('SearchUsers', { query: state.searchQuery, page: state.currentPage }) - } - dispatch('SuccessMessage') + const callApiFn = async() => await tagUser(nicknames, [tag], getters.authHost, getters.token) + + dispatch('ApplyChanges', { updatedUsers, callApiFn, userId: users[0].id }) }, async ClearFilters({ commit, dispatch, state }) { commit('CLEAR_USERS_FILTERS') @@ -141,37 +134,23 @@ const users = { } dispatch('SuccessMessage') }, - async DeactivateUsers({ commit, dispatch, getters, state }, users) { + async DeactivateUsers({ dispatch, getters }, users) { const updatedUsers = users.map(user => { return { ...user, deactivated: true } }) - commit('SWAP_USERS', updatedUsers) + const nicknames = users.map(user => user.nickname) + const callApiFn = async() => await deactivateUsers(nicknames, getters.authHost, getters.token) - const usersNicknames = users.map(user => user.nickname) - try { - await deactivateUsers(usersNicknames, getters.authHost, getters.token) - } catch (_e) { - return - } finally { - dispatch('SearchUsers', { query: state.searchQuery, page: state.currentPage }) - } - dispatch('SuccessMessage') + dispatch('ApplyChanges', { updatedUsers, callApiFn, userId: users[0].id }) }, - async ConfirmUsersEmail({ commit, dispatch, getters, state }, users) { + async ConfirmUsersEmail({ dispatch, getters }, users) { const updatedUsers = users.map(user => { return { ...user, confirmation_pending: false } }) - commit('SWAP_USERS', updatedUsers) + const nicknames = users.map(user => user.nickname) + const callApiFn = async() => await confirmUserEmail(nicknames, getters.authHost, getters.token) - const usersNicknames = users.map(user => user.nickname) - try { - await confirmUserEmail(usersNicknames, getters.authHost, getters.token) - } catch (_e) { - return - } finally { - dispatch('SearchUsers', { query: state.searchQuery, page: state.currentPage }) - } - dispatch('SuccessMessage') + dispatch('ApplyChanges', { updatedUsers, callApiFn, userId: users[0].id }) }, async ResendConfirmationEmail({ dispatch, getters }, users) { const usersNicknames = users.map(user => user.nickname) @@ -182,21 +161,14 @@ const users = { } dispatch('SuccessMessage') }, - async DeleteRight({ commit, dispatch, getters, state }, { users, right }) { + async DeleteRight({ dispatch, getters }, { users, right }) { const updatedUsers = users.map(user => { return user.local ? { ...user, roles: { ...user.roles, [right]: false }} : user }) - commit('SWAP_USERS', updatedUsers) + const nicknames = users.map(user => user.nickname) + const callApiFn = async() => await deleteRight(nicknames, right, getters.authHost, getters.token) - const usersNicknames = users.map(user => user.nickname) - try { - await deleteRight(usersNicknames, right, getters.authHost, getters.token) - } catch (_e) { - return - } finally { - dispatch('SearchUsers', { query: state.searchQuery, page: state.currentPage }) - } - dispatch('SuccessMessage') + dispatch('ApplyChanges', { updatedUsers, callApiFn, userId: users[0].id }) }, async DeleteUsers({ commit, dispatch, getters, state }, users) { const usersNicknames = users.map(user => user.nickname) @@ -208,6 +180,8 @@ const users = { const deletedUsersIds = users.map(deletedUser => deletedUser.id) const updatedUsers = state.fetchedUsers.filter(user => !deletedUsersIds.includes(user.id)) commit('SET_USERS', updatedUsers) + + dispatch('FetchUserProfile', { userId: users[0].id, godmode: false }) dispatch('SuccessMessage') }, async FetchUsers({ commit, dispatch, getters, state }, { page }) { @@ -224,25 +198,19 @@ const users = { RemovePasswordToken({ commit }) { commit('SET_PASSWORD_RESET_TOKEN', { link: '', token: '' }) }, - async RemoveTag({ commit, dispatch, getters, state }, { users, tag }) { + async RemoveTag({ dispatch, getters }, { users, tag }) { const updatedUsers = users.map(user => { return { ...user, tags: user.tags.filter(userTag => userTag !== tag) } }) - commit('SWAP_USERS', updatedUsers) + const nicknames = users.map(user => user.nickname) + const callApiFn = async() => await untagUser(nicknames, [tag], getters.authHost, getters.token) + dispatch('ApplyChanges', { updatedUsers, callApiFn, userId: users[0].id }) + }, + async RequirePasswordReset({ dispatch, getters }, users) { const nicknames = users.map(user => user.nickname) try { - await untagUser(nicknames, [tag], getters.authHost, getters.token) - } catch (_e) { - return - } finally { - dispatch('SearchUsers', { query: state.searchQuery, page: state.currentPage }) - } - dispatch('SuccessMessage') - }, - async RequirePasswordReset({ dispatch, getters }, { nicknames }) { - try { - await requirePasswordReset(nicknames, getters.authHost, getters.token) + await forcePasswordReset(nicknames, getters.authHost, getters.token) } catch (_e) { return } diff --git a/src/views/users/components/ModerationDropdown.vue b/src/views/users/components/ModerationDropdown.vue new file mode 100644 index 00000000..d6e361ad --- /dev/null +++ b/src/views/users/components/ModerationDropdown.vue @@ -0,0 +1,182 @@ + + + + + diff --git a/src/views/users/components/MultipleUsersMenu.vue b/src/views/users/components/MultipleUsersMenu.vue index 6eb18c1c..de058ad8 100644 --- a/src/views/users/components/MultipleUsersMenu.vue +++ b/src/views/users/components/MultipleUsersMenu.vue @@ -216,9 +216,9 @@ export default { }, requirePasswordReset: () => { const filtered = this.selectedUsers.filter(user => user.local) - const nicknames = filtered.map(u => u.nickname) - this.$store.dispatch('RequirePasswordReset', { nicknames }) - this.$emit('apply-action') + const requirePasswordResetFn = async(users) => await this.$store.dispatch('RequirePasswordReset', users) + + applyAction(filtered, requirePasswordResetFn) }, confirmAccounts: () => { const filtered = this.selectedUsers.filter(user => user.local && user.confirmation_pending) diff --git a/src/views/users/index.vue b/src/views/users/index.vue index fee15291..2940f776 100644 --- a/src/views/users/index.vue +++ b/src/views/users/index.vue @@ -66,96 +66,10 @@ @@ -191,13 +105,15 @@ import numeral from 'numeral' import UsersFilter from './components/UsersFilter' import MultipleUsersMenu from './components/MultipleUsersMenu' import NewAccountDialog from './components/NewAccountDialog' +import ModerationDropdown from './components/ModerationDropdown' export default { name: 'Users', components: { - UsersFilter, + NewAccountDialog, + ModerationDropdown, MultipleUsersMenu, - NewAccountDialog + UsersFilter }, data() { return { @@ -257,6 +173,10 @@ export default { clearSelection() { this.$refs.usersTable.clearSelection() }, + closeResetPasswordDialog() { + this.resetPasswordDialogOpen = false + this.$store.dispatch('RemovePasswordToken') + }, async createNewAccount(accountData) { await this.$store.dispatch('CreateNewAccount', accountData) this.createAccountDialogOpen = false @@ -264,29 +184,6 @@ export default { getFirstLetter(str) { return str.charAt(0).toUpperCase() }, - getPasswordResetToken(nickname) { - this.resetPasswordDialogOpen = true - this.$store.dispatch('GetPasswordResetToken', nickname) - }, - requirePasswordReset(nickname) { - const mailerEnabled = this.$store.state.user.nodeInfo.metadata.mailerEnabled - - if (!mailerEnabled) { - this.$alert(this.$t('users.mailerMustBeEnabled'), 'Error', { type: 'error' }) - - return - } - - this.$store.dispatch('RequirePasswordReset', { nicknames: [nickname] }) - }, - toggleActivation(user) { - user.deactivated - ? this.$store.dispatch('ActivateUsers', [user]) - : this.$store.dispatch('DeactivateUsers', [user]) - }, - handleDeletion(user) { - this.$store.dispatch('DeleteUsers', [user]) - }, handlePageChange(page) { const searchQuery = this.$store.state.users.searchQuery if (searchQuery === '') { @@ -298,31 +195,11 @@ export default { handleSelectionChange(value) { this.$data.selectedUsers = value }, - closeResetPasswordDialog() { - this.resetPasswordDialogOpen = false - this.$store.dispatch('RemovePasswordToken') - }, - showAdminAction({ local, id }) { - return local && this.showDeactivatedButton(id) + openResetPasswordDialog() { + this.resetPasswordDialogOpen = true }, showDeactivatedButton(id) { return this.$store.state.user.id !== id - }, - toggleTag(user, tag) { - user.tags.includes(tag) - ? this.$store.dispatch('RemoveTag', { users: [user], tag }) - : this.$store.dispatch('AddTag', { users: [user], tag }) - }, - toggleUserRight(user, right) { - user.roles[right] - ? this.$store.dispatch('DeleteRight', { users: [user], right }) - : this.$store.dispatch('AddRight', { users: [user], right }) - }, - handleEmailConfirmation(user) { - this.$store.dispatch('ConfirmUsersEmail', [user]) - }, - handleConfirmationResend(user) { - this.$store.dispatch('ResendConfirmationEmail', [user]) } } } diff --git a/src/views/users/show.vue b/src/views/users/show.vue index 2b1146de..aa826c07 100644 --- a/src/views/users/show.vue +++ b/src/views/users/show.vue @@ -1,9 +1,28 @@