diff --git a/src/components/dialog_modal/dialog_modal.vue b/src/components/dialog_modal/dialog_modal.vue index 0feef27b..0042d057 100644 --- a/src/components/dialog_modal/dialog_modal.vue +++ b/src/components/dialog_modal/dialog_modal.vue @@ -69,7 +69,7 @@ padding: 1rem 1rem; background-color: $fallback--bg; background-color: var(--bg, $fallback--bg); - white-space: normal; + white-space: pre-wrap; } .dialog-modal-footer { diff --git a/src/components/mod_modal/tabs/users_tab/users_tab.js b/src/components/mod_modal/tabs/users_tab/users_tab.js index fd3ab389..ca7dd924 100644 --- a/src/components/mod_modal/tabs/users_tab/users_tab.js +++ b/src/components/mod_modal/tabs/users_tab/users_tab.js @@ -49,7 +49,6 @@ const UsersTab = { users () { return this.$store.state.users.adminUsers }, isActive () { const tabSwitcher = this.$parent - console.log(this.users) return tabSwitcher ? tabSwitcher.isActive('users') : false } }, diff --git a/src/components/mod_modal/tabs/users_tab/users_tab.vue b/src/components/mod_modal/tabs/users_tab/users_tab.vue index a13b2396..36cc67fd 100644 --- a/src/components/mod_modal/tabs/users_tab/users_tab.vue +++ b/src/components/mod_modal/tabs/users_tab/users_tab.vue @@ -151,10 +151,6 @@ :class="{ 'menu-checkbox-checked': all(actorType) }" />{{ $t('moderation.users.filter.all') }} - @@ -170,7 +166,11 @@ class="user" > - + diff --git a/src/components/moderation_tools/moderation_tools.js b/src/components/moderation_tools/moderation_tools.js index 2469327a..c2da3a69 100644 --- a/src/components/moderation_tools/moderation_tools.js +++ b/src/components/moderation_tools/moderation_tools.js @@ -16,7 +16,8 @@ const QUARANTINE = 'mrf_tag:quarantine' const ModerationTools = { props: [ - 'user' + 'user', + 'extended' ], data () { return { @@ -30,7 +31,10 @@ const ModerationTools = { QUARANTINE }, showDeleteUserDialog: false, - toggled: false + showPasswordTokenDialog: false, + toggled: false, + passwordResetToken: {}, + bot: this.user.bot } }, components: { @@ -83,6 +87,9 @@ const ModerationTools = { deleteUserDialog (show) { this.showDeleteUserDialog = show }, + passwordTokenDialog (show) { + this.showPasswordTokenDialog = show + }, deleteUser () { const store = this.$store const user = this.user @@ -97,6 +104,29 @@ const ModerationTools = { } }) }, + getPasswordResetToken () { + this.$store.state.api.backendInteractor.getPasswordResetToken({ nickname: this.user.screen_name }) + .then(data => { + this.passwordResetToken = data + this.passwordTokenDialog(true) + }) + }, + forcePasswordReset () { + this.$store.state.api.backendInteractor.forcePasswordReset({ nickname: this.user.screen_name }) + }, + forceDisableMFA () { + this.$store.state.api.backendInteractor.forceDisableMFA({ nickname: this.user.screen_name }) + }, + toggleBot () { + const params = { bot: !this.bot } + + this.$store.state.api.backendInteractor + .updateProfile({ params }) + .then((user) => { + this.$store.commit('addNewUsers', [user]) + this.bot = !this.bot + }) + }, setToggled (value) { this.toggled = value } diff --git a/src/components/moderation_tools/moderation_tools.vue b/src/components/moderation_tools/moderation_tools.vue index 75ea08cc..f93f4847 100644 --- a/src/components/moderation_tools/moderation_tools.vue +++ b/src/components/moderation_tools/moderation_tools.vue @@ -120,6 +120,37 @@ {{ $t('user_card.admin_menu.quarantine') }} + + + +

+ {{ $t('user_card.admin_menu.password_reset_token_content', { token: passwordResetToken.token }) }} + {{ passwordResetToken.link }} +

+ +
diff --git a/src/i18n/en.json b/src/i18n/en.json index 21fb6f99..e91be01d 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -1109,17 +1109,23 @@ "user_card": { "admin_menu": { "activate_account": "Activate account", + "convert_to": "Convert to { type }", "deactivate_account": "Deactivate account", "delete_account": "Delete account", "delete_user": "Delete user", "delete_user_data_and_deactivate_confirmation": "This will permanently delete the data from this account and deactivate it. Are you absolutely sure?", "disable_any_subscription": "Disallow following user at all", + "disable_mfa": "Disable multi-factor authentication", "disable_remote_subscription": "Disallow following user from remote instances", "force_nsfw": "Mark all posts as NSFW", + "force_password_reset": "Require password reset on next login", "force_unlisted": "Force posts to be unlisted", + "get_password_reset_token": "Get password reset token", "grant_admin": "Grant Admin", "grant_moderator": "Grant Moderator", "moderation": "Moderation", + "password_reset_token": "Password reset token", + "password_reset_token_content": "Password reset token has been generated: { token }\nYou can also use this link to reset the password: ", "quarantine": "Disallow user posts from federating", "revoke_admin": "Revoke Admin", "revoke_moderator": "Revoke Moderator", diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index da4cc238..126bfc8a 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -23,6 +23,9 @@ const NOTIFICATION_READ_URL = '/api/v1/pleroma/notifications/read' const ADMIN_REPORTS_URL = '/api/v1/pleroma/admin/reports' const ADMIN_REPORT_NOTES_URL = id => `/api/v1/pleroma/admin/reports/${id}/notes` const ADMIN_REPORT_NOTE_URL = (report, note) => `/api/v1/pleroma/admin/reports/${report}/notes/${note}` +const ADMIN_PASSWORD_RESET_TOKEN_URL = nickname => `/api/v1/pleroma/admin/users/${nickname}/password_reset` +const ADMIN_FORCE_PASSWORD_RESET_URL = '/api/v1/pleroma/admin/users/force_password_reset' +const ADMIN_DISABLE_MFA_URL = '/api/v1/pleroma/admin/users/disable_mfa' const MFA_SETTINGS_URL = '/api/pleroma/accounts/mfa' const MFA_BACKUP_CODES_URL = '/api/pleroma/accounts/mfa/backup_codes' @@ -717,6 +720,31 @@ const deleteNoteFromReport = ({ report, note, credentials }) => { }) } +const getPasswordResetToken = ({ nickname, credentials }) => { + const url = ADMIN_PASSWORD_RESET_TOKEN_URL(nickname) + + return fetch(url, { headers: authHeaders(credentials) }) + .then(data => data.json()) +} + +const forcePasswordReset = ({ nickname, credentials }) => { + return promisedRequest({ + url: ADMIN_FORCE_PASSWORD_RESET_URL, + method: 'PATCH', + payload: { nicknames: [nickname] }, + credentials + }) +} + +const forceDisableMFA = ({ nickname, credentials }) => { + return promisedRequest({ + url: ADMIN_DISABLE_MFA_URL, + method: 'PUT', + payload: { nickname }, + credentials + }) +} + const fetchTimeline = ({ timeline, credentials, @@ -1793,6 +1821,9 @@ const apiService = { deleteRight, activateUser, deactivateUser, + getPasswordResetToken, + forcePasswordReset, + forceDisableMFA, register, getCaptcha, updateProfileImages, diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index 04a474a8..14f76cc9 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -143,6 +143,7 @@ export const parseUser = (data) => { // TODO: handle is_local output.is_local = !output.screen_name.includes('@') } else if (admin) { + output.bot = data.actor_type === 'Service' output.screen_name = data.nickname output.name = data.display_name output.profile_image_url = data.avatar