From ec5dbe17923aad22170ed91b484dc4d0a76ce7a2 Mon Sep 17 00:00:00 2001 From: luce Date: Mon, 18 Aug 2025 16:47:18 +0200 Subject: [PATCH] bulk menu and some cleanup, not finished yet --- .../confirm_modal/generic_confirm.js | 36 +++ .../confirm_modal/generic_confirm.vue | 19 ++ .../selectable_list/selectable_list.js | 4 +- .../settings_modal/admin_tabs/admin_card.js | 2 + .../settings_modal/admin_tabs/admin_card.vue | 174 ++++++------- .../settings_modal/admin_tabs/users_tab.js | 124 ++++++++- .../settings_modal/admin_tabs/users_tab.scss | 13 + .../settings_modal/admin_tabs/users_tab.vue | 243 +++++++++++++----- src/i18n/en.json | 56 ++-- src/modules/adminSettings.js | 14 +- src/services/api/api.service.js | 153 ++++++----- 11 files changed, 595 insertions(+), 243 deletions(-) create mode 100644 src/components/confirm_modal/generic_confirm.js create mode 100644 src/components/confirm_modal/generic_confirm.vue diff --git a/src/components/confirm_modal/generic_confirm.js b/src/components/confirm_modal/generic_confirm.js new file mode 100644 index 000000000..21bf3f61b --- /dev/null +++ b/src/components/confirm_modal/generic_confirm.js @@ -0,0 +1,36 @@ +import ConfirmModal from './confirm_modal.vue' +//import Select from 'src/components/select/select.vue' + +export default { + props: { + callback: { + type: Function + }, + title: { + type: String + }, + cancelText: { + type: String + }, + confirmText: { + type: String + } + }, + emits: ['hide', 'show'], + data: () => ({ + showing: false + }), + components: { + ConfirmModal + }, + methods: { + show () { + this.showing = true + this.$emit('show') + }, + hide () { + this.showing = false + this.$emit('hide') + } + } +} diff --git a/src/components/confirm_modal/generic_confirm.vue b/src/components/confirm_modal/generic_confirm.vue new file mode 100644 index 000000000..d0cbae2ca --- /dev/null +++ b/src/components/confirm_modal/generic_confirm.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/i18n/en.json b/src/i18n/en.json index e6720db26..6b5630a07 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -1143,33 +1143,58 @@ "management": "Management", "search_users": "Search for users...", "loading": "Loading...", - "placeholder_query": "Query", - "placeholder_name": "Name", - "placeholder_email": "Email", + "label_query": "Query", + "label_name": "Name", + "label_email": "Email", + "label_origin": "Origin", + "label_activity": "Activity", + "label_privileges": "Privileges", "all": "All", - "only_local": "Local only", - "only_external": "External only", - "only_active": "Active only", - "only_deactivated": "Deactivated only", - "only_administrators": "Admin only", - "all_privileged": "All privileged", - "only_moderators": "Moderators only", - "only_unapproved": "Unapproved only", - "only_unconfirmed": "Unconfirmed only", + "only_local": "Local Only", + "only_external": "External Only", + "only_active": "Active Only", + "only_deactivated": "Deactivated Only", + "only_administrators": "Admin Only", + "all_privileged": "All Privileged", + "only_moderators": "Moderators Only", + "only_unapproved": "Unapproved Only", + "only_unconfirmed": "Unconfirmed Only", "refresh": "Refresh", + "bulk_actions": { + "title": "Bulk Actions", + "yes": "Yes", + "no": "No", + "activate": "Activate Selected Users?", + "deactivate": "Deactivate Selected Users?", + "delete": "Delete Selected Users?", + "grant_admin": "Grant Admin Privileges To Selected Users?", + "revoke_admin": "Revoke Admin Privileges From Selected Users?", + "grant_moderator": "Grant Moderator Privileges To Selected Users?", + "revoke_moderator": "Revoke Moderator Privileges From Selected Users?", + "approve": "Approve Selected Users?", + "confirm": "Confirm Selected Users?", + "require_password_change": "Require Password Change For Selected Users?" + }, "activate": "Activate", "deactivate": "Deactivate", + "resend_confirmation_email": "Resend confirmation email", + "approve": "Approve", + "grant_admin": "Grant Admin", + "revoke_admin": "Revoke Admin", + "grant_moderator": "Grant Moderator", + "revoke_moderator": "Revoke Moderator", + "confirm_user": "Confirm", + "resend_confirmation_email": "Resend Confirmation Email", + "require_password_change": "Require Password Change", "delete": "Delete", "loading_user": "Loading user...", + "delete_user": "Delete User", "expand_user": "Expand user", "collapse_user": "Collapse user", "is_admin": "Is admin", "is_moderator": "Is moderator", "is_confirmed": "Is confirmed", - "resend_confirmation_email": "Resend confirmation email", - "approve": "Approve", "is_active": "Is active", - "delete_user": "Delete user", "expand_timeline": "Expand timeline", "collapse_timeline": "Collapse timeline", "expand_raw_info": "Expand raw info", @@ -1188,7 +1213,6 @@ "link_source": "Source", "title_database": "Database", "title_details": "Details", - "title_filter_user_search": "Filter User Search", "title_users": "Users", "user_has_no_posts": "User has no posts" }, diff --git a/src/modules/adminSettings.js b/src/modules/adminSettings.js index e42af810f..70210a1eb 100644 --- a/src/modules/adminSettings.js +++ b/src/modules/adminSettings.js @@ -70,16 +70,22 @@ const adminSettingsStorage = { .then(res => store.commit('updateRight', { user, right: 'admin', value: res.is_admin })) }, adminRemoveUserFromAdminGroup (store, user) { - return store.rootState.api.backendInteractor.adminRemoveUserFromAdminGroup({ user }) - .then(res => store.commit('updateRight', { user, right: 'admin', value: res.is_admin })) + // prevent revokation of own rights + if (user.id !== store.state.users.currentUser.id) { + return store.rootState.api.backendInteractor.adminRemoveUserFromAdminGroup({ user }) + .then(res => store.commit('updateRight', { user, right: 'admin', value: res.is_admin })) + } }, adminAddUserToModeratorGroup (store, user) { return store.rootState.api.backendInteractor.adminAddUserToModeratorGroup({ user }) .then(res => store.commit('updateRight', { user, right: 'moderator', value: res.is_moderator })) }, adminRemoveUserFromModeratorGroup (store, user) { - return store.rootState.api.backendInteractor.adminRemoveUserFromModeratorGroup({ user }) - .then(res => store.commit('updateRight', { user, right: 'moderator', value: res.is_moderator })) + // prevent revokation of own rights + if (user.id !== store.state.users.currentUser.id) { + return store.rootState.api.backendInteractor.adminRemoveUserFromModeratorGroup({ user }) + .then(res => store.commit('updateRight', { user, right: 'moderator', value: res.is_moderator })) + } }, adminActivateUser (store, user) { return store.rootState.api.backendInteractor.activateUser({ user }) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index fff3d38a8..56b324635 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -145,7 +145,7 @@ const PLEROMA_ADMIN_RESEND_CONFIRMATION_EMAIL_URL = '/api/v1/pleroma/admin/users const PLEROMA_ADMIN_APPROVE_URL = '/api/v1/pleroma/admin/users/approve' const PLEROMA_ADMIN_LIST_STATUSES_URL = (id, pageSize, godmode, withReblogs) => `/api/v1/pleroma/admin/users/${id}/statuses?page_size=${pageSize}&godmode=${godmode}&with_reblogs=${withReblogs}` const PLEROMA_ADMIN_CHANGE_STATUS_SCOPE_URL = (id) => `/api/v1/pleroma/admin/statuses/${id}` - +const PLEROMA_ADMIN_REQUIRE_PASSWORD_CHANGE_URL = '/api/v1/pleroma/admin/users/force_password_reset' const PLEROMA_EMOJI_RELOAD_URL = '/api/pleroma/admin/reload_emoji' const PLEROMA_EMOJI_IMPORT_FS_URL = '/api/pleroma/emoji/packs/import' const PLEROMA_EMOJI_PACKS_URL = (page, pageSize) => `/api/v1/pleroma/emoji/packs?page=${page}&page_size=${pageSize}` @@ -1489,104 +1489,116 @@ const dismissAnnouncement = ({ id, credentials }) => { const adminListUsers = ({ opts, credentials }) => { // the reported list is hardly useful because standards are for dating i guess, // so make sure to fetchIfMissing right afterward using this call - const url = PLEROMA_ADMIN_USERS_URL(opts) - return promisedRequest({ - url: url, - credentials, - method: 'GET' - }).then((data) => data.users.map(parseUser)) + const url = PLEROMA_ADMIN_USERS_URL(opts) + return promisedRequest({ + url: url, + credentials, + method: 'GET' + }).then((data) => data.users.map(parseUser)) } const adminAddUserToAdminGroup = ({ user, credentials }) => { - const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'admin') - return promisedRequest({ url: url, - credentials, - method: 'POST' - }) + const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'admin') + return promisedRequest({ url: url, + credentials, + method: 'POST' + }) } const adminRemoveUserFromAdminGroup = ({ user, credentials }) => { - const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'admin') - return promisedRequest({ url: url, - credentials, - method: 'DELETE' - }) + const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'admin') + return promisedRequest({ url: url, + credentials, + method: 'DELETE' + }) } const adminAddUserToModeratorGroup = ({ user, credentials }) => { - const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'moderator') - return promisedRequest({ url: url, - credentials, - method: 'POST' - }) + const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'moderator') + return promisedRequest({ url: url, + credentials, + method: 'POST' + }) } const adminRemoveUserFromModeratorGroup = ({ user, credentials }) => { - const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'moderator') - return promisedRequest({ url: url, - credentials, - method: 'DELETE' - }) + const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'moderator') + return promisedRequest({ url: url, + credentials, + method: 'DELETE' + }) } const adminConfirmUser = ({user: { screen_name: nickname }, credentials }) => { - const url = PLEROMA_ADMIN_CONFIRM_USER_URL - return promisedRequest({url: url, - credentials, - method: 'PATCH', - payload: { - nicknames: [nickname] - } - }) + const url = PLEROMA_ADMIN_CONFIRM_USER_URL + return promisedRequest({url: url, + credentials, + method: 'PATCH', + payload: { + nicknames: [nickname] + } + }) } const adminResendConfirmationEmail = ({user: { screen_name: nickname }, credentials }) => { - const url = PLEROMA_ADMIN_RESEND_CONFIRMATION_EMAIL_URL - return promisedRequest({url: url, - credentials, - method: 'PATCH', - payload: { - nicknames: [nickname] - } - }) + const url = PLEROMA_ADMIN_RESEND_CONFIRMATION_EMAIL_URL + return promisedRequest({url: url, + credentials, + method: 'PATCH', + payload: { + nicknames: [nickname] + } + }) } const adminApproveUser = ({user : { screen_name: nickname }, credentials }) => { - const url = PLEROMA_ADMIN_APPROVE_URL - const r = promisedRequest({url: url, - credentials, - method: 'PATCH', - payload: { - nicknames: [nickname] - } - }) - return r + const url = PLEROMA_ADMIN_APPROVE_URL + const r = promisedRequest({url: url, + credentials, + method: 'PATCH', + payload: { + nicknames: [nickname] + } + }) + return r } const adminListStatuses = ({user: { id }, opts: { pageSize, godmode, withReblogs }, credentials }) => { - const url = PLEROMA_ADMIN_LIST_STATUSES_URL(id, pageSize, godmode, withReblogs) - return promisedRequest({url: url, - credentials, - method: 'GET' - }) + const url = PLEROMA_ADMIN_LIST_STATUSES_URL(id, pageSize, godmode, withReblogs) + return promisedRequest({url: url, + credentials, + method: 'GET' + }) } const adminChangeStatusScope = ({opts: { id, sensitive, visibility}, credentials }) => { - const url = PLEROMA_ADMIN_CHANGE_STATUS_SCOPE_URL(id) - var payload = {} - if (typeof(sensitive) !== 'undefined') { - payload['sensitive'] = sensitive - } - if (typeof(visibility) !== 'undefined') { - payload['visibility'] = visibility - } - return promisedRequest({url: url, - credentials, - method: 'PUT', - payload - }) + const url = PLEROMA_ADMIN_CHANGE_STATUS_SCOPE_URL(id) + var payload = {} + if (typeof(sensitive) !== 'undefined') { + payload['sensitive'] = sensitive + } + if (typeof(visibility) !== 'undefined') { + payload['visibility'] = visibility + } + return promisedRequest({ + url, + credentials, + method: 'PUT', + payload + }) } +const adminRequirePasswordChange = ({ user: { screen_name: nickname }, credentials }) => { + const url = PLEROMA_ADMIN_REQUIRE_PASSWORD_CHANGE_URL + return promisedRequest({ + url, + credentials, + method: 'PATCH', + payload: { + nicknames: [nickname] + } + }) +} const announcementToPayload = ({ content, startsAt, endsAt, allDay }) => { const payload = { content } @@ -2270,6 +2282,7 @@ const apiService = { adminApproveUser, adminListStatuses, adminChangeStatusScope, + adminRequirePasswordChange } export default apiService