From 46658b8fd0b24ad79a83737261f7a2ac6bdb6241 Mon Sep 17 00:00:00 2001 From: Angelina Filippova <linakirsanova@gmail.com> Date: Tue, 24 Mar 2020 20:59:12 +0000 Subject: [PATCH] Merge branch 'user-edit' into 'develop' Add the ability to set user password and email Closes #69 See merge request pleroma/admin-fe!92 (cherry picked from commit 747dc65e9bf81895266696579584a614ae6bf4d0) 9395cc78 Add the Security Settings modal for setting the user's email and password --- src/api/users.js | 19 +++ src/lang/en.js | 17 +- src/store/modules/userProfile.js | 14 +- .../components/SecuritySettingsModal.vue | 155 ++++++++++++++++++ src/views/users/show.vue | 21 ++- 5 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 src/views/users/components/SecuritySettingsModal.vue diff --git a/src/api/users.js b/src/api/users.js index 8298eeca..642dd1a7 100644 --- a/src/api/users.js +++ b/src/api/users.js @@ -71,6 +71,25 @@ export async function fetchUser(id, authHost, token) { }) } +export async function fetchUserCredentials(nickname, authHost, token) { + return await request({ + baseURL: baseName(authHost), + url: `/api/pleroma/admin/users/${nickname}/credentials`, + method: 'get', + headers: authHeaders(token) + }) +} + +export async function updateUserCredentials(nickname, credentials, authHost, token) { + return await request({ + baseURL: baseName(authHost), + url: `/api/pleroma/admin/users/${nickname}/credentials`, + method: 'patch', + headers: authHeaders(token), + data: credentials + }) +} + export async function fetchUsers(filters, authHost, token, page = 1) { return await request({ baseURL: baseName(authHost), diff --git a/src/lang/en.js b/src/lang/en.js index 8b8e9ff7..5e98a3cb 100644 --- a/src/lang/en.js +++ b/src/lang/en.js @@ -261,7 +261,22 @@ export default { activeUppercase: 'Active', active: 'active', deactivated: 'deactivated', - noStatuses: 'No statuses to show' + noStatuses: 'No statuses to show', + securitySettings: { + email: 'Email', + password: 'Password', + securitySettings: 'Security settings', + passwordChangeWarning1: 'Setting a new password will cause the user to be signed out from any client they have used before.', + passwordChangeWarning2: 'When the user signs in with this password, they will be asked to set a new one.', + passwordLengthNotice: 'Make sure it\'s at least {minLength} characters long.', + inputNewEmail: 'Input new email', + inputNewPassword: 'Input new password', + passwordUpdated: 'The password has been updated', + emailUpdated: 'The email has been updated', + success: 'Success', + submit: 'Submit', + close: 'Close' + } }, usersFilter: { inputPlaceholder: 'Select filter', diff --git a/src/store/modules/userProfile.js b/src/store/modules/userProfile.js index 5a7e4394..af54072c 100644 --- a/src/store/modules/userProfile.js +++ b/src/store/modules/userProfile.js @@ -1,10 +1,11 @@ -import { fetchUser, fetchUserStatuses } from '@/api/users' +import { fetchUser, fetchUserStatuses, fetchUserCredentials, updateUserCredentials } from '@/api/users' const userProfile = { state: { statuses: [], statusesLoading: true, user: {}, + userCredentials: {}, userProfileLoading: true }, mutations: { @@ -19,6 +20,9 @@ const userProfile = { }, SET_USER_PROFILE_LOADING: (state, status) => { state.userProfileLoading = status + }, + SET_USER_CREDENTIALS: (state, userCredentials) => { + state.userCredentials = userCredentials } }, actions: { @@ -38,6 +42,14 @@ const userProfile = { commit('SET_STATUSES', statuses.data) commit('SET_STATUSES_LOADING', false) + }, + async FetchUserCredentials({ commit, getters }, { nickname }) { + const userResponse = await fetchUserCredentials(nickname, getters.authHost, getters.token) + commit('SET_USER_CREDENTIALS', userResponse.data) + }, + async UpdateUserCredentials({ dispatch, getters }, { nickname, credentials }) { + await updateUserCredentials(nickname, credentials, getters.authHost, getters.token) + dispatch('FetchUserCredentials', { nickname }) } } } diff --git a/src/views/users/components/SecuritySettingsModal.vue b/src/views/users/components/SecuritySettingsModal.vue new file mode 100644 index 00000000..58c757b1 --- /dev/null +++ b/src/views/users/components/SecuritySettingsModal.vue @@ -0,0 +1,155 @@ +<template> + <el-dialog + :before-close="close" + :title="$t('userProfile.securitySettings.securitySettings')" + :visible="visible" + class="security-settings-modal"> + <el-row> + <p> + <label> + {{ $t('userProfile.securitySettings.email') }} + </label> + </p> + </el-row> + <el-row> + <el-input + :placeholder="$t('userProfile.securitySettings.inputNewEmail')" + v-model="emailForm.newEmail" /> + </el-row> + <br> + <el-row type="flex" justify="end"> + <el-button + :loading="emailForm.isLoading" + :disabled="!emailForm.newEmail || emailForm.newEmail === userCredentials.email" + type="primary" + @click="updateEmail()"> + {{ $t('userProfile.securitySettings.submit') }} + </el-button> + </el-row> + <el-row> + <p> + <label> + {{ $t('userProfile.securitySettings.password') }} + </label> + </p> + </el-row> + <el-row> + <el-input + :placeholder="$t('userProfile.securitySettings.inputNewPassword')" + v-model="passwordForm.newPassword" + show-password /> + <small class="form-text"> + {{ $t('userProfile.securitySettings.passwordLengthNotice', { minLength: 8 }) }} + </small> + <br> + <el-alert + :closable="false" + type="warning" + show-icon> + <p>{{ $t('userProfile.securitySettings.passwordChangeWarning1') }}</p> + <p>{{ $t('userProfile.securitySettings.passwordChangeWarning2') }}</p> + </el-alert> + </el-row> + <br> + <el-row type="flex" justify="end"> + <el-button + :loading="passwordForm.isLoading" + :disabled="passwordForm.newPassword.length < 8" + type="primary" + @click="updatePassword()"> + {{ $t('userProfile.securitySettings.submit') }} + </el-button> + </el-row> + </el-dialog> +</template> + +<script> + +export default { + name: 'SecuritySettingsModal', + props: { + visible: { + type: Boolean, + default: false + }, + user: { + type: Object, + default: function() { + return {} + } + } + }, + data() { + return { + emailForm: { + newEmail: '', + isLoading: false + }, + passwordForm: { + newPassword: '', + isLoading: false + } + } + }, + computed: { + userCredentials() { + return this.$store.state.userProfile.userCredentials + } + }, + mounted: async function() { + await this.$store.dispatch('FetchUserCredentials', { nickname: this.user.nickname }) + this.emailForm.newEmail = this.userCredentials.email + }, + methods: { + async updateEmail() { + const credentials = { email: this.emailForm.newEmail } + this.emailForm.isLoading = true + await this.$store.dispatch('UpdateUserCredentials', { nickname: this.user.nickname, credentials }) + this.emailForm.isLoading = false + this.$notify.success({ + title: this.$t('userProfile.securitySettings.success'), + message: this.$t('userProfile.securitySettings.emailUpdated'), + duration: 2000 + }) + }, + async updatePassword() { + const credentials = { password: this.passwordForm.newPassword } + this.passwordForm.isLoading = true + await this.$store.dispatch('UpdateUserCredentials', { nickname: this.user.nickname, credentials }) + this.passwordForm.isLoading = false + this.passwordForm.newPassword = '' + this.$notify.success({ + title: this.$t('userProfile.securitySettings.success'), + message: this.$t('userProfile.securitySettings.passwordUpdated'), + duration: 2000 + }) + }, + close() { + this.$emit('close', true) + } + } +} +</script> + +<style rel='stylesheet/scss' lang='scss'> +@media all and (max-width: 800px) { + .security-settings-modal { + .el-dialog { + width: 90%; + } + } +} + +.security-settings-modal { + .el-alert .el-alert__description { + word-break: break-word; + font-size: 1em; + } + + .form-text { + display: block; + margin-top: .25rem; + color: #909399; + } +} +</style> diff --git a/src/views/users/show.vue b/src/views/users/show.vue index fd24486d..84cfef32 100644 --- a/src/views/users/show.vue +++ b/src/views/users/show.vue @@ -73,6 +73,18 @@ <el-tag v-if="user.deactivated" type="danger">{{ $t('userProfile.deactivated') }}</el-tag> </td> </tr> + <tr class="el-table__row"> + <td> + <el-button icon="el-icon-lock" @click="securitySettingsModalVisible = true"> + {{ $t('userProfile.securitySettings.securitySettings') }} + </el-button> + <SecuritySettingsModal + :user="user" + :visible="securitySettingsModalVisible" + @close="securitySettingsModalVisible = false" /> + </td> + <td /> + </tr> </tbody> </table> </div> @@ -96,14 +108,16 @@ <script> import Status from '@/components/Status' import ModerationDropdown from './components/ModerationDropdown' +import SecuritySettingsModal from './components/SecuritySettingsModal' export default { name: 'UsersShow', - components: { ModerationDropdown, Status }, + components: { ModerationDropdown, Status, SecuritySettingsModal }, data() { return { showPrivate: false, - resetPasswordDialogOpen: false + resetPasswordDialogOpen: false, + securitySettingsModalVisible: false } }, computed: { @@ -127,6 +141,9 @@ export default { }, userProfileLoading() { return this.$store.state.userProfile.userProfileLoading + }, + userCredentials() { + return this.$store.state.userProfile.userCredentials } }, mounted: function() { -- GitLab