From f0899ec2639dd5f3fc09bd4324c14bdde8fd8ec5 Mon Sep 17 00:00:00 2001 From: Angelina Filippova Date: Fri, 11 Oct 2019 02:49:16 +0300 Subject: [PATCH 01/13] Update api for AddRight and DeleteRight actions --- src/api/__mocks__/users.js | 2 +- src/api/users.js | 14 ++++++++------ src/store/modules/users.js | 24 ++++++++++++++++-------- src/views/users/index.vue | 4 +++- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/api/__mocks__/users.js b/src/api/__mocks__/users.js index 016772a7..07aca91e 100644 --- a/src/api/__mocks__/users.js +++ b/src/api/__mocks__/users.js @@ -48,7 +48,7 @@ export async function searchUsers(query, filters, authHost, token, page = 1) { }}) } -export async function addRight(nickname, right, authHost, token) { +export async function addRight(nicknames, right, authHost, token) { return Promise.resolve({ data: { [`is_${right}`]: true } }) diff --git a/src/api/users.js b/src/api/users.js index 1e97da92..9351cc45 100644 --- a/src/api/users.js +++ b/src/api/users.js @@ -2,12 +2,13 @@ import request from '@/utils/request' import { getToken } from '@/utils/auth' import { baseName } from './utils' -export async function addRight(nickname, right, authHost, token) { +export async function addRight(nicknames, right, authHost, token) { return await request({ baseURL: baseName(authHost), - url: `/api/pleroma/admin/users/${nickname}/permission_group/${right}`, + url: `/api/pleroma/admin/users/permission_group/${right}`, method: 'post', - headers: authHeaders(token) + headers: authHeaders(token), + data: { nicknames } }) } @@ -21,12 +22,13 @@ export async function createNewAccount(nickname, email, password, authHost, toke }) } -export async function deleteRight(nickname, right, authHost, token) { +export async function deleteRight(nicknames, right, authHost, token) { return await request({ baseURL: baseName(authHost), - url: `/api/pleroma/admin/users/${nickname}/permission_group/${right}`, + url: `/api/pleroma/admin/users/permission_group/${right}`, method: 'delete', - headers: authHeaders(token) + headers: authHeaders(token), + data: { nicknames } }) } diff --git a/src/store/modules/users.js b/src/store/modules/users.js index b3d35108..f087d930 100644 --- a/src/store/modules/users.js +++ b/src/store/modules/users.js @@ -76,6 +76,14 @@ const users = { } }, actions: { + async AddRight({ dispatch, getters, state }, { users, right }) { + const usersNicknames = users.map(user => user.nickname) + await addRight(usersNicknames, right, getters.authHost, getters.token) + + dispatch('FetchUsers', { page: state.currentPage }) + // const updatedUser = { ...user, roles: { ...user.roles, [right]: !user.roles[right] }} + // commit('SWAP_USER', updatedUser) + }, async AddTag({ commit, getters }, { users, tag }) { const nicknames = users.map(user => user.nickname) await tagUser(nicknames, [tag], getters.authHost, getters.token) @@ -90,6 +98,14 @@ const users = { await createNewAccount(nickname, email, password, getters.authHost, getters.token) dispatch('FetchUsers', { page: state.currentPage }) }, + async DeleteRight({ dispatch, getters, state }, { users, right }) { + const usersNicknames = users.map(user => user.nickname) + await deleteRight(usersNicknames, right, getters.authHost, getters.token) + + dispatch('FetchUsers', { page: state.currentPage }) + // const updatedUser = { ...user, roles: { ...user.roles, [right]: !user.roles[right] }} + // commit('SWAP_USER', updatedUser) + }, async DeleteUser({ commit, getters, state }, user) { const { data } = await deleteUser(user.nickname, getters.authHost, getters.token) const users = state.fetchedUsers.filter(user => user.nickname !== data) @@ -146,14 +162,6 @@ const users = { const currentFilters = { ...defaultFilters, ...filters } commit('SET_USERS_FILTERS', currentFilters) dispatch('SearchUsers', { query: state.searchQuery, page: 1 }) - }, - async ToggleRight({ commit, getters }, { user, right }) { - user.roles[right] - ? await deleteRight(user.nickname, right, getters.authHost, getters.token) - : await addRight(user.nickname, right, getters.authHost, getters.token) - - const updatedUser = { ...user, roles: { ...user.roles, [right]: !user.roles[right] }} - commit('SWAP_USER', updatedUser) } } } diff --git a/src/views/users/index.vue b/src/views/users/index.vue index 062b23c8..cce607dd 100644 --- a/src/views/users/index.vue +++ b/src/views/users/index.vue @@ -308,7 +308,9 @@ export default { : this.$store.dispatch('AddTag', { users: [user], tag }) }, toggleUserRight(user, right) { - this.$store.dispatch('ToggleRight', { user, right }) + user.roles[right] + ? this.$store.dispatch('DeleteRight', { users: [user], right }) + : this.$store.dispatch('AddRight', { users: [user], right }) } } } From 77a153deac0c840ee25b0e12c046b546b90a91bf Mon Sep 17 00:00:00 2001 From: Angelina Filippova Date: Fri, 11 Oct 2019 03:28:55 +0300 Subject: [PATCH 02/13] Update api for ActivateUsers and DeactivateUsers actions --- src/api/users.js | 29 +++++++++++++------ src/store/modules/users.js | 15 ++++++---- src/views/reports/components/TimelineItem.vue | 14 +++++---- src/views/users/index.vue | 8 +++-- 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/api/users.js b/src/api/users.js index 9351cc45..12e13e6f 100644 --- a/src/api/users.js +++ b/src/api/users.js @@ -2,6 +2,16 @@ import request from '@/utils/request' import { getToken } from '@/utils/auth' import { baseName } from './utils' +export async function activateUsers(nicknames, authHost, token) { + return await request({ + baseURL: baseName(authHost), + url: `/api/pleroma/admin/users/activate`, + method: 'patch', + headers: authHeaders(token), + data: { nicknames } + }) +} + export async function addRight(nicknames, right, authHost, token) { return await request({ baseURL: baseName(authHost), @@ -22,6 +32,16 @@ export async function createNewAccount(nickname, email, password, authHost, toke }) } +export async function deactivateUsers(nicknames, authHost, token) { + return await request({ + baseURL: baseName(authHost), + url: `/api/pleroma/admin/users/deactivate`, + method: 'patch', + headers: authHeaders(token), + data: { nicknames } + }) +} + export async function deleteRight(nicknames, right, authHost, token) { return await request({ baseURL: baseName(authHost), @@ -96,15 +116,6 @@ export async function tagUser(nicknames, tags, authHost, token) { }) } -export async function toggleUserActivation(nickname, authHost, token) { - return await request({ - baseURL: baseName(authHost), - url: `/api/pleroma/admin/users/${nickname}/toggle_activation`, - method: 'patch', - headers: authHeaders(token) - }) -} - export async function untagUser(nicknames, tags, authHost, token) { return await request({ baseURL: baseName(authHost), diff --git a/src/store/modules/users.js b/src/store/modules/users.js index f087d930..f35ad9aa 100644 --- a/src/store/modules/users.js +++ b/src/store/modules/users.js @@ -1,13 +1,14 @@ import { + activateUsers, addRight, createNewAccount, + deactivateUsers, deleteRight, deleteUser, fetchUsers, getPasswordResetToken, searchUsers, tagUser, - toggleUserActivation, untagUser, requirePasswordReset } from '@/api/users' @@ -76,6 +77,10 @@ const users = { } }, actions: { + async ActivateUsers({ commit, getters }, nicknames) { + const { data } = await activateUsers(nicknames, getters.authHost, getters.token) + commit('SWAP_USERS', data) + }, async AddRight({ dispatch, getters, state }, { users, right }) { const usersNicknames = users.map(user => user.nickname) await addRight(usersNicknames, right, getters.authHost, getters.token) @@ -98,6 +103,10 @@ const users = { await createNewAccount(nickname, email, password, getters.authHost, getters.token) dispatch('FetchUsers', { page: state.currentPage }) }, + async DeactivateUsers({ commit, getters }, nicknames) { + const { data } = await deactivateUsers(nicknames, getters.authHost, getters.token) + commit('SWAP_USERS', data) + }, async DeleteRight({ dispatch, getters, state }, { users, right }) { const usersNicknames = users.map(user => user.nickname) await deleteRight(usersNicknames, right, getters.authHost, getters.token) @@ -148,10 +157,6 @@ const users = { loadUsers(commit, page, response.data) } }, - async ToggleUserActivation({ commit, getters }, nickname) { - const { data } = await toggleUserActivation(nickname, getters.authHost, getters.token) - commit('SWAP_USER', data) - }, async ToggleUsersFilter({ commit, dispatch, state }, filters) { const defaultFilters = { local: false, diff --git a/src/views/reports/components/TimelineItem.vue b/src/views/reports/components/TimelineItem.vue index 771298aa..96433a22 100644 --- a/src/views/reports/components/TimelineItem.vue +++ b/src/views/reports/components/TimelineItem.vue @@ -21,7 +21,7 @@ + @click.native="toggleActivation(report.account)"> {{ report.account.deactivated ? $t('users.activateAccount') : $t('users.deactivateAccount') }} + @click.native="toggleActivation(scope.row)"> {{ scope.row.deactivated ? $t('users.activateAccount') : $t('users.deactivateAccount') }} Date: Sat, 12 Oct 2019 09:44:44 +0300 Subject: [PATCH 03/13] Pass users instead of nicknames to actions that toggle activation --- src/store/modules/users.js | 10 ++++++---- src/views/reports/components/TimelineItem.vue | 8 ++++---- src/views/users/index.vue | 8 ++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/store/modules/users.js b/src/store/modules/users.js index f35ad9aa..b26024dc 100644 --- a/src/store/modules/users.js +++ b/src/store/modules/users.js @@ -77,8 +77,9 @@ const users = { } }, actions: { - async ActivateUsers({ commit, getters }, nicknames) { - const { data } = await activateUsers(nicknames, getters.authHost, getters.token) + async ActivateUsers({ commit, getters }, users) { + const usersNicknames = users.map(user => user.nickname) + const { data } = await activateUsers(usersNicknames, getters.authHost, getters.token) commit('SWAP_USERS', data) }, async AddRight({ dispatch, getters, state }, { users, right }) { @@ -103,8 +104,9 @@ const users = { await createNewAccount(nickname, email, password, getters.authHost, getters.token) dispatch('FetchUsers', { page: state.currentPage }) }, - async DeactivateUsers({ commit, getters }, nicknames) { - const { data } = await deactivateUsers(nicknames, getters.authHost, getters.token) + async DeactivateUsers({ commit, getters }, users) { + const usersNicknames = users.map(user => user.nickname) + const { data } = await deactivateUsers(usersNicknames, getters.authHost, getters.token) commit('SWAP_USERS', data) }, async DeleteRight({ dispatch, getters, state }, { users, right }) { diff --git a/src/views/reports/components/TimelineItem.vue b/src/views/reports/components/TimelineItem.vue index 96433a22..34336f79 100644 --- a/src/views/reports/components/TimelineItem.vue +++ b/src/views/reports/components/TimelineItem.vue @@ -148,10 +148,10 @@ export default { showDeactivatedButton(id) { return this.$store.state.user.id !== id }, - toggleActivation({ deactivated, nickname }) { - deactivated - ? this.$store.dispatch('ActivateUsers', [nickname]) - : this.$store.dispatch('DeactivateUsers', [nickname]) + toggleActivation(user) { + user.deactivated + ? this.$store.dispatch('ActivateUsers', [user]) + : this.$store.dispatch('DeactivateUsers', [user]) }, toggleTag(user, tag) { user.tags.includes(tag) diff --git a/src/views/users/index.vue b/src/views/users/index.vue index bd2cd7c4..b5cf1251 100644 --- a/src/views/users/index.vue +++ b/src/views/users/index.vue @@ -275,10 +275,10 @@ export default { this.$store.dispatch('RequirePasswordReset', { nickname }) }, - toggleActivation({ deactivated, nickname }) { - deactivated - ? this.$store.dispatch('ActivateUsers', [nickname]) - : this.$store.dispatch('DeactivateUsers', [nickname]) + toggleActivation(user) { + user.deactivated + ? this.$store.dispatch('ActivateUsers', [user]) + : this.$store.dispatch('DeactivateUsers', [user]) }, handleDeletion(user) { this.$store.dispatch('DeleteUser', user) From d0025dfe7a4400a4fbd1a7203a0b70ac1a9896ea Mon Sep 17 00:00:00 2001 From: Angelina Filippova Date: Sat, 12 Oct 2019 09:45:43 +0300 Subject: [PATCH 04/13] Update test mocks for api calls that toggle activation --- src/api/__mocks__/users.js | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/api/__mocks__/users.js b/src/api/__mocks__/users.js index 07aca91e..f2e2103b 100644 --- a/src/api/__mocks__/users.js +++ b/src/api/__mocks__/users.js @@ -33,11 +33,6 @@ export async function getPasswordResetToken(nickname, authHost, token) { return Promise.resolve({ data: { token: 'g05lxnBJQnL', link: 'http://url/api/pleroma/password_reset/g05lxnBJQnL' }}) } -export async function toggleUserActivation(nickname, authHost, token) { - const response = users.find(user => user.nickname === nickname) - return Promise.resolve({ data: { ...response, deactivated: !response.deactivated }}) -} - export async function searchUsers(query, filters, authHost, token, page = 1) { const filteredUsers = filterUsers(filters) const response = filteredUsers.filter(user => user.nickname === query) @@ -48,12 +43,29 @@ export async function searchUsers(query, filters, authHost, token, page = 1) { }}) } +export async function activateUsers(nicknames, authHost, token) { + const response = nicknames.map(nickname => { + const currentUser = users.find(user => user.nickname === nickname) + return { ...currentUser, deactivated: false } + }) + return Promise.resolve({ data: response }) +} + export async function addRight(nicknames, right, authHost, token) { + console.log(nicknames) return Promise.resolve({ data: { [`is_${right}`]: true } }) } +export async function deactivateUsers(nicknames, authHost, token) { + const response = nicknames.map(nickname => { + const currentUser = users.find(user => user.nickname === nickname) + return { ...currentUser, deactivated: true } + }) + return Promise.resolve({ data: response }) +} + export async function deleteRight(nickname, right, authHost, token) { return Promise.resolve({ data: { [`is_${right}`]: false } From 58573a7de8773c406e114633cd2b2f7bb5fc8be4 Mon Sep 17 00:00:00 2001 From: Angelina Filippova Date: Sat, 12 Oct 2019 09:53:02 +0300 Subject: [PATCH 05/13] Remove multiple calls of functions that moderate users --- .../users/components/MultipleUsersMenu.vue | 82 ++++++++----------- 1 file changed, 32 insertions(+), 50 deletions(-) diff --git a/src/views/users/components/MultipleUsersMenu.vue b/src/views/users/components/MultipleUsersMenu.vue index ecd2baf2..2c6f630c 100644 --- a/src/views/users/components/MultipleUsersMenu.vue +++ b/src/views/users/components/MultipleUsersMenu.vue @@ -150,87 +150,69 @@ export default { }, methods: { mappers() { - const applyActionToAllUsers = (filteredUsers, fn) => Promise.all(filteredUsers.map(fn)) - .then(() => { - this.$message({ - type: 'success', - message: this.$t('users.completed') - }) - this.$emit('apply-action') - }).catch((err) => { + const applyAction = (users, dispatchAction) => { + try { + dispatchAction(users) + } catch (err) { console.log(err) return + } + this.$message({ + type: 'success', + message: this.$t('users.completed') }) + this.$emit('apply-action') + } return { grantRight: (right) => () => { const filterUsersFn = user => user.local && !user.roles[right] && this.$store.state.user.id !== user.id - const toggleRightFn = async(user) => await this.$store.dispatch('ToggleRight', { user, right }) + const addRightFn = async(users) => await this.$store.dispatch('AddRight', { users, right }) const filtered = this.selectedUsers.filter(filterUsersFn) - applyActionToAllUsers(filtered, toggleRightFn) + applyAction(filtered, addRightFn) }, revokeRight: (right) => () => { const filterUsersFn = user => user.local && user.roles[right] && this.$store.state.user.id !== user.id - const toggleRightFn = async(user) => await this.$store.dispatch('ToggleRight', { user, right }) + const deleteRightFn = async(users) => await this.$store.dispatch('DeleteRight', { users, right }) const filtered = this.selectedUsers.filter(filterUsersFn) - applyActionToAllUsers(filtered, toggleRightFn) + applyAction(filtered, deleteRightFn) }, activate: () => { const filtered = this.selectedUsers.filter(user => user.deactivated && this.$store.state.user.id !== user.id) - const toggleActivationFn = async(user) => await this.$store.dispatch('ToggleUserActivation', user.nickname) + const activateUsersFn = async(users) => await this.$store.dispatch('ActivateUsers', users) - applyActionToAllUsers(filtered, toggleActivationFn) + applyAction(filtered, activateUsersFn) }, deactivate: () => { const filtered = this.selectedUsers.filter(user => !user.deactivated && this.$store.state.user.id !== user.id) - const toggleActivationFn = async(user) => await this.$store.dispatch('ToggleUserActivation', user.nickname) + const deactivateUsersFn = async(users) => await this.$store.dispatch('DeactivateUsers', users) - applyActionToAllUsers(filtered, toggleActivationFn) + applyAction(filtered, deactivateUsersFn) }, remove: () => { const filtered = this.selectedUsers.filter(user => this.$store.state.user.id !== user.id) const deleteAccountFn = async(user) => await this.$store.dispatch('DeleteUser', user) - applyActionToAllUsers(filtered, deleteAccountFn) + applyAction(filtered, deleteAccountFn) }, - addTag: (tag) => async() => { - const filterUsersFn = user => tag === 'disable_remote_subscription' || tag === 'disable_any_subscription' - ? user.local && !user.tags.includes(tag) - : !user.tags.includes(tag) - const users = this.selectedUsers.filter(filterUsersFn) + addTag: (tag) => () => { + const filtered = this.selectedUsers.filter(user => + tag === 'disable_remote_subscription' || tag === 'disable_any_subscription' + ? user.local && !user.tags.includes(tag) + : !user.tags.includes(tag)) + const addTagFn = async(users) => await this.$store.dispatch('AddTag', { users, tag }) - try { - await this.$store.dispatch('AddTag', { users, tag }) - } catch (err) { - console.log(err) - return - } - - this.$message({ - type: 'success', - message: this.$t('users.completed') - }) - this.$emit('apply-action') + applyAction(filtered, addTagFn) }, removeTag: (tag) => async() => { - const filterUsersFn = user => tag === 'disable_remote_subscription' || tag === 'disable_any_subscription' - ? user.local && user.tags.includes(tag) - : user.tags.includes(tag) - const users = this.selectedUsers.filter(filterUsersFn) + const filtered = this.selectedUsers.filter(user => + tag === 'disable_remote_subscription' || tag === 'disable_any_subscription' + ? user.local && user.tags.includes(tag) + : user.tags.includes(tag)) + const removeTagFn = async(users) => await this.$store.dispatch('RemoveTag', { users, tag }) - try { - await this.$store.dispatch('RemoveTag', { users, tag }) - } catch (err) { - console.log(err) - return - } - - this.$message({ - type: 'success', - message: this.$t('users.completed') - }) - this.$emit('apply-action') + applyAction(filtered, removeTagFn) }, requirePasswordReset: () => { this.selectedUsers.map(user => this.$store.dispatch('RequirePasswordReset', user)) From 4d58527201cdbeea34e00ec3f6fb093402ab7b68 Mon Sep 17 00:00:00 2001 From: Angelina Filippova Date: Wed, 16 Oct 2019 14:24:35 +0200 Subject: [PATCH 06/13] Update api function and module action for user deletion --- src/api/users.js | 7 +-- src/store/modules/users.js | 45 ++++++++----------- src/views/reports/components/TimelineItem.vue | 2 +- src/views/users/index.vue | 2 +- 4 files changed, 24 insertions(+), 32 deletions(-) diff --git a/src/api/users.js b/src/api/users.js index 12e13e6f..fb168d6c 100644 --- a/src/api/users.js +++ b/src/api/users.js @@ -52,12 +52,13 @@ export async function deleteRight(nicknames, right, authHost, token) { }) } -export async function deleteUser(nickname, authHost, token) { +export async function deleteUsers(nicknames, authHost, token) { return await request({ baseURL: baseName(authHost), - url: `/api/pleroma/admin/users?nickname=${nickname}`, + url: `/api/pleroma/admin/users`, method: 'delete', - headers: authHeaders(token) + headers: authHeaders(token), + data: { nicknames } }) } diff --git a/src/store/modules/users.js b/src/store/modules/users.js index b26024dc..497f0b87 100644 --- a/src/store/modules/users.js +++ b/src/store/modules/users.js @@ -4,7 +4,7 @@ import { createNewAccount, deactivateUsers, deleteRight, - deleteUser, + deleteUsers, fetchUsers, getPasswordResetToken, searchUsers, @@ -77,24 +77,20 @@ const users = { } }, actions: { - async ActivateUsers({ commit, getters }, users) { + async ActivateUsers({ dispatch, getters, state }, users) { const usersNicknames = users.map(user => user.nickname) - const { data } = await activateUsers(usersNicknames, getters.authHost, getters.token) - commit('SWAP_USERS', data) + await activateUsers(usersNicknames, getters.authHost, getters.token) + dispatch('FetchUsers', { page: state.currentPage }) }, async AddRight({ dispatch, getters, state }, { users, right }) { const usersNicknames = users.map(user => user.nickname) await addRight(usersNicknames, right, getters.authHost, getters.token) - dispatch('FetchUsers', { page: state.currentPage }) - // const updatedUser = { ...user, roles: { ...user.roles, [right]: !user.roles[right] }} - // commit('SWAP_USER', updatedUser) }, - async AddTag({ commit, getters }, { users, tag }) { + async AddTag({ dispatch, getters, state }, { users, tag }) { const nicknames = users.map(user => user.nickname) await tagUser(nicknames, [tag], getters.authHost, getters.token) - - commit('SWAP_USERS', users.map(user => ({ ...user, tags: [...user.tags, tag] }))) + dispatch('FetchUsers', { page: state.currentPage }) }, async ClearFilters({ commit, dispatch, state }) { commit('CLEAR_USERS_FILTERS') @@ -104,46 +100,41 @@ const users = { await createNewAccount(nickname, email, password, getters.authHost, getters.token) dispatch('FetchUsers', { page: state.currentPage }) }, - async DeactivateUsers({ commit, getters }, users) { + async DeactivateUsers({ dispatch, getters, state }, users) { const usersNicknames = users.map(user => user.nickname) - const { data } = await deactivateUsers(usersNicknames, getters.authHost, getters.token) - commit('SWAP_USERS', data) + await deactivateUsers(usersNicknames, getters.authHost, getters.token) + dispatch('FetchUsers', { page: state.currentPage }) }, async DeleteRight({ dispatch, getters, state }, { users, right }) { const usersNicknames = users.map(user => user.nickname) await deleteRight(usersNicknames, right, getters.authHost, getters.token) - dispatch('FetchUsers', { page: state.currentPage }) - // const updatedUser = { ...user, roles: { ...user.roles, [right]: !user.roles[right] }} - // commit('SWAP_USER', updatedUser) }, - async DeleteUser({ commit, getters, state }, user) { - const { data } = await deleteUser(user.nickname, getters.authHost, getters.token) - const users = state.fetchedUsers.filter(user => user.nickname !== data) - commit('SET_USERS', users) + async DeleteUsers({ dispatch, getters, state }, users) { + const usersNicknames = users.map(user => user.nickname) + await deleteUsers(usersNicknames, getters.authHost, getters.token) }, - async RequirePasswordReset({ commit, getters, state }, user) { + async RequirePasswordReset({ getters }, user) { await requirePasswordReset(user.nickname, getters.authHost, getters.token) }, - async FetchUsers({ commit, state, getters, dispatch }, { page }) { + async FetchUsers({ commit, dispatch, getters, state }, { page }) { commit('SET_LOADING', true) const filters = Object.keys(state.filters).filter(filter => state.filters[filter]).join() const response = await fetchUsers(filters, getters.authHost, getters.token, page) - await dispatch('GetNodeInfo') + // await dispatch('GetNodeInfo') loadUsers(commit, page, response.data) }, - async GetPasswordResetToken({ commit, state, getters }, nickname) { + async GetPasswordResetToken({ commit, getters }, nickname) { const { data } = await getPasswordResetToken(nickname, getters.authHost, getters.token) commit('SET_PASSWORD_RESET_TOKEN', data) }, RemovePasswordToken({ commit }) { commit('SET_PASSWORD_RESET_TOKEN', { link: '', token: '' }) }, - async RemoveTag({ commit, getters }, { users, tag }) { + async RemoveTag({ dispatch, getters, state }, { users, tag }) { const nicknames = users.map(user => user.nickname) await untagUser(nicknames, [tag], getters.authHost, getters.token) - - commit('SWAP_USERS', users.map(user => ({ ...user, tags: user.tags.filter(userTag => userTag !== tag) }))) + dispatch('FetchUsers', { page: state.currentPage }) }, async SearchUsers({ commit, dispatch, state, getters }, { query, page }) { if (query.length === 0) { diff --git a/src/views/reports/components/TimelineItem.vue b/src/views/reports/components/TimelineItem.vue index 34336f79..e7e41e7b 100644 --- a/src/views/reports/components/TimelineItem.vue +++ b/src/views/reports/components/TimelineItem.vue @@ -140,7 +140,7 @@ export default { } }, handleDeletion(user) { - this.$store.dispatch('DeleteUser', user) + this.$store.dispatch('DeleteUsers', [user]) }, parseTimestamp(timestamp) { return moment(timestamp).format('L HH:mm') diff --git a/src/views/users/index.vue b/src/views/users/index.vue index b5cf1251..bd255c7b 100644 --- a/src/views/users/index.vue +++ b/src/views/users/index.vue @@ -281,7 +281,7 @@ export default { : this.$store.dispatch('DeactivateUsers', [user]) }, handleDeletion(user) { - this.$store.dispatch('DeleteUser', user) + this.$store.dispatch('DeleteUsers', [user]) }, handlePageChange(page) { const searchQuery = this.$store.state.users.searchQuery From d47b3287bdd594c78d1d3844695115f6f08936e1 Mon Sep 17 00:00:00 2001 From: Angelina Filippova Date: Wed, 16 Oct 2019 21:00:25 +0200 Subject: [PATCH 07/13] Fix user deletion for multiple users --- src/views/users/components/MultipleUsersMenu.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/users/components/MultipleUsersMenu.vue b/src/views/users/components/MultipleUsersMenu.vue index 2c6f630c..53f778b8 100644 --- a/src/views/users/components/MultipleUsersMenu.vue +++ b/src/views/users/components/MultipleUsersMenu.vue @@ -150,9 +150,9 @@ export default { }, methods: { mappers() { - const applyAction = (users, dispatchAction) => { + const applyAction = async(users, dispatchAction) => { try { - dispatchAction(users) + await dispatchAction(users) } catch (err) { console.log(err) return @@ -192,7 +192,7 @@ export default { }, remove: () => { const filtered = this.selectedUsers.filter(user => this.$store.state.user.id !== user.id) - const deleteAccountFn = async(user) => await this.$store.dispatch('DeleteUser', user) + const deleteAccountFn = async(users) => await this.$store.dispatch('DeleteUsers', users) applyAction(filtered, deleteAccountFn) }, From 408b9dcc64bea70d8a6ad25ef121c20c2aa9f252 Mon Sep 17 00:00:00 2001 From: Angelina Filippova Date: Wed, 16 Oct 2019 22:30:29 +0200 Subject: [PATCH 08/13] Fix displaying error message when there is no error response --- src/utils/request.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/utils/request.js b/src/utils/request.js index bbed6501..51e74601 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -10,14 +10,19 @@ const service = axios.create({ service.interceptors.response.use( response => response, error => { + let errorMessage console.log(`Error ${error}`) - console.log(error.response.data) - // If there's an "error" property in the json, use it - const edata = error.response.data.error ? error.response.data.error : error.response.data + if (error.response) { + // If there's an "error" property in the json, use it + const edata = error.response.data.error ? error.response.data.error : error.response.data + errorMessage = `${error.message} - ${edata}` + } else { + errorMessage = error + } Message({ - message: `${error.message} - ${edata}`, + message: errorMessage, type: 'error', duration: 5 * 1000 }) From 6975f435272cd47c2374663ef4ea2e50a5a5197e Mon Sep 17 00:00:00 2001 From: Angelina Filippova Date: Thu, 17 Oct 2019 22:42:04 +0200 Subject: [PATCH 09/13] Add optimistic update for actions that moderate users --- src/store/modules/users.js | 54 +++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/src/store/modules/users.js b/src/store/modules/users.js index 497f0b87..c7b54010 100644 --- a/src/store/modules/users.js +++ b/src/store/modules/users.js @@ -38,12 +38,6 @@ const users = { SET_LOADING: (state, status) => { state.loading = status }, - SWAP_USER: (state, updatedUser) => { - const updated = state.fetchedUsers.map(user => user.id === updatedUser.id ? updatedUser : user) - state.fetchedUsers = updated - .map(user => user.nickname ? user : { ...user, nickname: '' }) - .sort((a, b) => a.nickname.localeCompare(b.nickname)) - }, SWAP_USERS: (state, users) => { const usersWithoutSwapped = users.reduce((acc, user) => { return acc.filter(u => u.id !== user.id) @@ -77,17 +71,32 @@ const users = { } }, actions: { - async ActivateUsers({ dispatch, getters, state }, users) { + async ActivateUsers({ commit, dispatch, getters, state }, users) { + const updatedUsers = users.map(user => { + return { ...user, deactivated: false } + }) + commit('SWAP_USERS', updatedUsers) + const usersNicknames = users.map(user => user.nickname) await activateUsers(usersNicknames, getters.authHost, getters.token) dispatch('FetchUsers', { page: state.currentPage }) }, - async AddRight({ dispatch, getters, state }, { users, right }) { + async AddRight({ commit, dispatch, getters, state }, { users, right }) { + const updatedUsers = users.map(user => { + return user.local ? { ...user, roles: { ...user.roles, [right]: true }} : user + }) + commit('SWAP_USERS', updatedUsers) + const usersNicknames = users.map(user => user.nickname) await addRight(usersNicknames, right, getters.authHost, getters.token) dispatch('FetchUsers', { page: state.currentPage }) }, - async AddTag({ dispatch, getters, state }, { users, tag }) { + async AddTag({ commit, dispatch, getters, state }, { users, tag }) { + const updatedUsers = users.map(user => { + return { ...user, tags: [...user.tags, tag] } + }) + commit('SWAP_USERS', updatedUsers) + const nicknames = users.map(user => user.nickname) await tagUser(nicknames, [tag], getters.authHost, getters.token) dispatch('FetchUsers', { page: state.currentPage }) @@ -100,17 +109,31 @@ const users = { await createNewAccount(nickname, email, password, getters.authHost, getters.token) dispatch('FetchUsers', { page: state.currentPage }) }, - async DeactivateUsers({ dispatch, getters, state }, users) { + async DeactivateUsers({ commit, dispatch, getters, state }, users) { + const updatedUsers = users.map(user => { + return { ...user, deactivated: true } + }) + commit('SWAP_USERS', updatedUsers) + const usersNicknames = users.map(user => user.nickname) await deactivateUsers(usersNicknames, getters.authHost, getters.token) dispatch('FetchUsers', { page: state.currentPage }) }, - async DeleteRight({ dispatch, getters, state }, { users, right }) { + async DeleteRight({ commit, dispatch, getters, state }, { users, right }) { + const updatedUsers = users.map(user => { + return user.local ? { ...user, roles: { ...user.roles, [right]: false }} : user + }) + commit('SWAP_USERS', updatedUsers) + const usersNicknames = users.map(user => user.nickname) await deleteRight(usersNicknames, right, getters.authHost, getters.token) dispatch('FetchUsers', { page: state.currentPage }) }, - async DeleteUsers({ dispatch, getters, state }, users) { + async DeleteUsers({ commit, getters, state }, users) { + const deletedUsersIds = users.map(deletedUser => deletedUser.id) + const updatedUsers = state.fetchedUsers.filter(user => !deletedUsersIds.includes(user.id)) + commit('SET_USERS', updatedUsers) + const usersNicknames = users.map(user => user.nickname) await deleteUsers(usersNicknames, getters.authHost, getters.token) }, @@ -131,7 +154,12 @@ const users = { RemovePasswordToken({ commit }) { commit('SET_PASSWORD_RESET_TOKEN', { link: '', token: '' }) }, - async RemoveTag({ dispatch, getters, state }, { users, tag }) { + async RemoveTag({ commit, dispatch, getters, state }, { 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) await untagUser(nicknames, [tag], getters.authHost, getters.token) dispatch('FetchUsers', { page: state.currentPage }) From fa2410d61409f926ecd047f0b2879d9daf8cbf42 Mon Sep 17 00:00:00 2001 From: Angelina Filippova Date: Fri, 18 Oct 2019 13:57:48 +0200 Subject: [PATCH 10/13] Fix tests for updated user actions --- src/api/__mocks__/users.js | 5 +-- test/views/users/index.test.js | 48 +++++++++------------- test/views/users/multipleUsersMenu.test.js | 7 ---- 3 files changed, 21 insertions(+), 39 deletions(-) diff --git a/src/api/__mocks__/users.js b/src/api/__mocks__/users.js index f2e2103b..b5aca6a1 100644 --- a/src/api/__mocks__/users.js +++ b/src/api/__mocks__/users.js @@ -52,7 +52,6 @@ export async function activateUsers(nicknames, authHost, token) { } export async function addRight(nicknames, right, authHost, token) { - console.log(nicknames) return Promise.resolve({ data: { [`is_${right}`]: true } }) @@ -72,9 +71,9 @@ export async function deleteRight(nickname, right, authHost, token) { }) } -export async function deleteUser(nickname, authHost, token) { +export async function deleteUsers(nicknames, authHost, token) { return Promise.resolve({ data: - nickname + nicknames }) } diff --git a/test/views/users/index.test.js b/test/views/users/index.test.js index d435e1ce..63473aaf 100644 --- a/test/views/users/index.test.js +++ b/test/views/users/index.test.js @@ -75,7 +75,7 @@ describe('Users actions', () => { store = new Vuex.Store(cloneDeep(storeConfig)) }) - it('grants admin and moderator rights to a local user', async (done) => { + it('grants admin right to a local user', async (done) => { const wrapper = mount(Users, { store, localVue, @@ -87,14 +87,28 @@ describe('Users actions', () => { const user = store.state.users.fetchedUsers[2] expect(user.roles.admin).toBe(false) expect(user.roles.moderator).toBe(false) - wrapper.find(htmlElement(3, 1)).trigger('click') - await flushPromises() - wrapper.find(htmlElement(3, 2)).trigger('click') - await flushPromises() const updatedUser = store.state.users.fetchedUsers[2] expect(updatedUser.roles.admin).toBe(true) + done() + }) + + it('grants moderator right to a local user', async (done) => { + const wrapper = mount(Users, { + store, + localVue, + sync: false, + stubs: ['router-link'] + }) + await flushPromises() + + const user = store.state.users.fetchedUsers[2] + expect(user.roles.admin).toBe(false) + expect(user.roles.moderator).toBe(false) + wrapper.find(htmlElement(3, 2)).trigger('click') + + const updatedUser = store.state.users.fetchedUsers[2] expect(updatedUser.roles.moderator).toBe(true) done() }) @@ -126,9 +140,7 @@ describe('Users actions', () => { const user = store.state.users.fetchedUsers[1] expect(user.deactivated).toBe(false) - wrapper.find(htmlElement(2, 1)).trigger('click') - await flushPromises() const updatedUser = store.state.users.fetchedUsers[1] expect(updatedUser.deactivated).toBe(true) @@ -166,9 +178,7 @@ describe('Users actions', () => { expect(user2.tags.length).toBe(1) wrapper.find(htmlElement(1, 5)).trigger('click') - await flushPromises() wrapper.find(htmlElement(2, 5)).trigger('click') - await flushPromises() const updatedUser1 = store.state.users.fetchedUsers[0] const updatedUser2 = store.state.users.fetchedUsers[1] @@ -188,33 +198,13 @@ describe('Users actions', () => { const user = store.state.users.fetchedUsers[1] expect(user.tags.length).toBe(1) - wrapper.find(htmlElement(2, 6)).trigger('click') - await flushPromises() const updatedUser = store.state.users.fetchedUsers[1] expect(updatedUser.tags.length).toBe(0) done() }) - it('shows check icon when tag is added', async (done) => { - const wrapper = mount(Users, { - store, - localVue, - sync: false, - stubs: ['router-link'] - }) - await flushPromises() - - expect(wrapper.find(`${htmlElement(1, 5)} i`).exists()).toBe(false) - - wrapper.find(htmlElement(1, 5)).trigger('click') - await flushPromises() - - expect(wrapper.find(`${htmlElement(1, 5)} i`).exists()).toBe(true) - done() - }) - it('does not change user index in array when tag is added', async (done) => { const wrapper = mount(Users, { store, diff --git a/test/views/users/multipleUsersMenu.test.js b/test/views/users/multipleUsersMenu.test.js index c92232bf..7770a501 100644 --- a/test/views/users/multipleUsersMenu.test.js +++ b/test/views/users/multipleUsersMenu.test.js @@ -51,7 +51,6 @@ describe('Apply users actions to multiple users', () => { expect(user2.roles.admin).toBe(false) expect(user3.roles.admin).toBe(false) grantRight('admin')() - await flushPromises() const updatedUser1 = store.state.users.fetchedUsers[0] const updatedUser2 = store.state.users.fetchedUsers[1] @@ -88,7 +87,6 @@ describe('Apply users actions to multiple users', () => { expect(user2.roles.moderator).toBe(false) expect(user3.roles.moderator).toBe(false) grantRight('moderator')() - await flushPromises() const updatedUser1 = store.state.users.fetchedUsers[0] const updatedUser2 = store.state.users.fetchedUsers[1] @@ -123,7 +121,6 @@ describe('Apply users actions to multiple users', () => { expect(user1.roles.admin).toBe(true) expect(user2.roles.admin).toBe(false) revokeRight('admin')() - await flushPromises() const updatedUser1 = store.state.users.fetchedUsers[0] const updatedUser2 = store.state.users.fetchedUsers[2] @@ -173,7 +170,6 @@ describe('Apply users actions to multiple users', () => { const user = store.state.users.fetchedUsers[2] expect(user.deactivated).toBe(true) activate() - await flushPromises() const updatedUser = store.state.users.fetchedUsers[2] expect(updatedUser.deactivated).toBe(false) @@ -203,7 +199,6 @@ describe('Apply users actions to multiple users', () => { expect(user1.deactivated).toBe(false) expect(user2.deactivated).toBe(false) deactivate() - await flushPromises() const updatedUser1 = store.state.users.fetchedUsers[0] const updatedUser2 = store.state.users.fetchedUsers[1] @@ -270,7 +265,6 @@ describe('Apply users actions to multiple users', () => { expect(user1.tags.length).toBe(0) expect(user2.tags.length).toBe(1) addTag('strip_media')() - await flushPromises() const updatedUser1 = store.state.users.fetchedUsers[0] const updatedUser2 = store.state.users.fetchedUsers[1] @@ -311,7 +305,6 @@ describe('Apply users actions to multiple users', () => { expect(user1.tags.length).toBe(1) expect(user2.tags.length).toBe(1) removeTag('strip_media')() - await flushPromises() const updatedUser1 = store.state.users.fetchedUsers[1] const updatedUser2 = store.state.users.fetchedUsers[2] From caba7eaefd9e2945a5eda918f470ad60d14c8ceb Mon Sep 17 00:00:00 2001 From: Angelina Filippova Date: Fri, 18 Oct 2019 18:12:55 +0200 Subject: [PATCH 11/13] Return dispatching action that gets node info --- src/store/modules/users.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/modules/users.js b/src/store/modules/users.js index c7b54010..df3c946d 100644 --- a/src/store/modules/users.js +++ b/src/store/modules/users.js @@ -144,7 +144,7 @@ const users = { commit('SET_LOADING', true) const filters = Object.keys(state.filters).filter(filter => state.filters[filter]).join() const response = await fetchUsers(filters, getters.authHost, getters.token, page) - // await dispatch('GetNodeInfo') + await dispatch('GetNodeInfo') loadUsers(commit, page, response.data) }, async GetPasswordResetToken({ commit, getters }, nickname) { From 4bd8015dfa58a2a14a50247681c4b8f0deae179e Mon Sep 17 00:00:00 2001 From: Angelina Filippova Date: Fri, 18 Oct 2019 18:27:46 +0200 Subject: [PATCH 12/13] Update Changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index be0fb971..8efd8c5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed - `mailerEnabled` must be set to `true` in order to require password reset (password reset currently only works via email) +- Actions in users module (ActivateUsers, AddRight, DeactivateUsers, DeleteRight, DeleteUsers) now accept an array of users instead of one user + +### Added +- Optimistic update for actions in users module and fetching users after api function finished its execution ## [1.2.0] - 2019-09-27 From 736ec2d7f59f219a9411d1b939ed82583fe0d586 Mon Sep 17 00:00:00 2001 From: Angelina Filippova Date: Sun, 20 Oct 2019 16:38:43 +0000 Subject: [PATCH 13/13] Remove fetching initial data for config --- CHANGELOG.md | 1 + src/api/initialDataForConfig.js | 117 -------------------------------- src/store/modules/settings.js | 7 +- 3 files changed, 2 insertions(+), 123 deletions(-) delete mode 100644 src/api/initialDataForConfig.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 8efd8c5c..cf9cc2a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed - `mailerEnabled` must be set to `true` in order to require password reset (password reset currently only works via email) +- remove fetching initial data for configuring server settings - Actions in users module (ActivateUsers, AddRight, DeactivateUsers, DeleteRight, DeleteUsers) now accept an array of users instead of one user ### Added diff --git a/src/api/initialDataForConfig.js b/src/api/initialDataForConfig.js deleted file mode 100644 index f24ffb9a..00000000 --- a/src/api/initialDataForConfig.js +++ /dev/null @@ -1,117 +0,0 @@ -export const initialSettings = [ - { - group: 'pleroma', - key: ':instance', - value: [ - { 'tuple': [':name', 'Pleroma'] }, - { 'tuple': [':email', 'example@example.com'] }, - { 'tuple': [':notify_email', 'noreply@example.com'] }, - { 'tuple': [':description', 'A Pleroma instance, an alternative fediverse server'] }, - { 'tuple': [':limit', 5000] }, - { 'tuple': [':remote_limit', 100000] }, - { 'tuple': [':upload_limit', 16 * 1048576] }, - { 'tuple': [':avatar_upload_limit', 2 * 1048576] }, - { 'tuple': [':background_upload_limit', 4 * 1048576] }, - { 'tuple': [':banner_upload_limit', 4 * 1048576] }, - { 'tuple': [':poll_limits', [ - { 'tuple': [':max_options', 20] }, - { 'tuple': [':max_option_chars', 200] }, - { 'tuple': [':min_expiration', 0] }, - { 'tuple': [':max_expiration', 365 * 86400] } - ]] }, - { 'tuple': [':registrations_open', true] }, - { 'tuple': [':invites_enabled', false] }, - { 'tuple': [':account_activation_required', false] }, - { 'tuple': [':federating', true] }, - { 'tuple': [':federation_reachability_timeout_days', 7] }, - { 'tuple': - [':federation_publisher_modules', ['Pleroma.Web.ActivityPub.Publisher', 'Pleroma.Web.Websub', 'Pleroma.Web.Salmon']] }, - { 'tuple': [':allow_relay', true] }, - { 'tuple': [':rewrite_policy', 'Pleroma.Web.ActivityPub.MRF.NoOpPolicy'] }, - { 'tuple': [':public', true] }, - { 'tuple': [':managed_config', true] }, - { 'tuple': [':static_dir', 'instance/static/'] }, - { 'tuple': [':allowed_post_formats', ['text/plain', 'text/html', 'text/markdown', 'text/bbcode']] }, - { 'tuple': [':mrf_transparency', true] }, - { 'tuple': [':extended_nickname_format', false] }, - { 'tuple': [':max_pinned_statuses', 1] }, - { 'tuple': [':no_attachment_links', false] }, - { 'tuple': [':max_report_comment_size', 1000] }, - { 'tuple': [':safe_dm_mentions', false] }, - { 'tuple': [':healthcheck', false] }, - { 'tuple': [':remote_post_retention_days', 90] }, - { 'tuple': [':skip_thread_containment', true] }, - { 'tuple': [':limit_to_local_content', ':unauthenticated'] }, - { 'tuple': [':dynamic_configuration', true] }, - { 'tuple': [':max_account_fields', 10] }, - { 'tuple': [':max_remote_account_fields', 20] }, - { 'tuple': [':account_field_name_length', 255] }, - { 'tuple': [':account_field_value_length', 255] }, - { 'tuple': [':external_user_synchronization', true] }, - { 'tuple': [':user_bio_length', 5000] }, - { 'tuple': [':user_name_length', 100] } - ] - }, - { - group: 'mime', - key: ':types', - value: { - 'application/activity+json': ['activity+json'], - 'application/jrd+json': ['jrd+json'], - 'application/ld+json': ['activity+json'], - 'application/xml': ['xml'], - 'application/xrd+xml': ['xrd+xml'] - } - }, - { - group: 'cors_plug', - key: ':max_age', - value: 86400 - }, - { - group: 'cors_plug', - key: ':methods', - value: ['POST', 'PUT', 'DELETE', 'GET', 'PATCH', 'OPTIONS'] - }, - { - group: 'cors_plug', - key: ':expose', - value: [ - 'Link', - 'X-RateLimit-Reset', - 'X-RateLimit-Limit', - 'X-RateLimit-Remaining', - 'X-Request-Id', - 'Idempotency-Key' - ] - }, - { - group: 'cors_plug', - key: ':credentials', - value: true - }, - { - group: 'cors_plug', - key: ':headers', - value: ['Authorization', 'Content-Type', 'Idempotency-Key'] - }, - { - group: 'tesla', - key: ':adapter', - value: 'Tesla.Adapter.Hackney' - }, - { - group: 'pleroma', - key: ':markup', - value: [ - { 'tuple': [':allow_inline_images', true] }, - { 'tuple': [':allow_headings', false] }, - { 'tuple': [':allow_tables', false] }, - { 'tuple': [':allow_fonts', false] }, - { 'tuple': [':scrub_policy', [ - 'Pleroma.HTML.Transform.MediaProxy', - 'Pleroma.HTML.Scrubber.Default' - ]] } - ] - } -] diff --git a/src/store/modules/settings.js b/src/store/modules/settings.js index 3ee58c4e..bb6bd8c1 100644 --- a/src/store/modules/settings.js +++ b/src/store/modules/settings.js @@ -1,5 +1,4 @@ import { fetchSettings, updateSettings, uploadMedia } from '@/api/settings' -import { initialSettings } from '@/api/initialDataForConfig' import { filterIgnored, parseTuples, valueHasTuples, wrapConfig } from './normalizers' const settings = { @@ -116,11 +115,7 @@ const settings = { async FetchSettings({ commit, dispatch, getters }) { commit('SET_LOADING', true) const response = await fetchSettings(getters.authHost, getters.token) - if (response.data.configs.length === 0) { - dispatch('SubmitChanges', initialSettings) - } else { - commit('SET_SETTINGS', response.data.configs) - } + commit('SET_SETTINGS', response.data.configs) commit('SET_LOADING', false) }, RewriteConfig({ commit }, { tab, data }) {