bulk actions and modal details, WIP

This commit is contained in:
luce 2025-09-06 15:12:32 +02:00
commit 8a29bd5fdd
8 changed files with 188 additions and 141 deletions

View file

@ -140,7 +140,7 @@ const AdminCard = {
fetchStatuses (store, opts) {
const u = this.$store.getters.findUser(this.userDetails.id)
const res = store.dispatch('adminListStatuses', { user: u, opts: { pageSize: opts.pageSize, godmode: true, withReblogs: true}})
return Promise.resolve(res.then(r => r.activities))
return res.then(r => r.activities)
}
}
}

View file

@ -133,13 +133,47 @@
:fetch-page="(store, opts) => fetchStatuses(store, opts)"
>
<template #header>
<button
class="button button-default btn"
type="button"
@click="deleteSelection"
<Popover
ref="dropdown"
trigger="click"
placement="bottom"
>
{{ $t('admin_dash.users.delete') }}
</button>
<template #trigger>
<button
class="button button-default btn"
>
{{ $t('admin_dash.users.bulk_actions.title') }}
</button>
</template>
<template #content>
<div class="dropdown-menu">
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="confirmSelection('confirmDelete')"
>
{{ $t('admin_dash.users.delete') }}
</button>
</div>
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="confirmSelection('confirmSetSensitive')"
>
{{ $t('admin_dash.users.set_sensitive') }}
</button>
</div>
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="confirmSelection('confirmUnsetSensitive')"
>
{{ $t('admin_dash.users.unset_sensitive') }}
</button>
</div>
</div>
</template>
</Popover>
</template>
<template #item="{item}">
<AdminStatusCard :status-details="item" />
@ -178,6 +212,55 @@
<pre> {{ JSON.stringify(user_details, null, 2) }} </pre>
</div>
</div>
<GenericConfirm
ref="confirmDelete"
:title="$t('admin_dash.users.bulk_actions.activate')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="selectionConfirmed('adminDeleteStatus')"
/>
<GenericConfirm
ref="confirmSetSensitive"
:title="$t('admin_dash.users.bulk_actions.change_sensitivity')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="selectionConfirmed('adminChangeStatusScope', { sensitive: true })"
/>
<GenericConfirm
ref="confirmUnsetSensitive"
:title="$t('admin_dash.users.bulk_actions.unmark_as_sensitive')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="selectionConfirmed('adminChangeStatusScope', { sensitive: false })"
/>
<GenericConfirm
ref="confirmSetPublic"
:title="$t('admin_dash.users.bulk_actions.mark_as_public')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="selectionConfirmed('adminChangeStatusScope', { visiblity: 'public' })"
/>
<GenericConfirm
ref="confirmSetUnlisted"
:title="$t('admin_dash.users.bulk_actions.mark_as_unlisted')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="selectionConfirmed('adminChangeStatusScope', { visiblity: 'unlisted' })"
/>
<GenericConfirm
ref="confirmSetPrivate"
:title="$t('admin_dash.users.bulk_actions.mark_as_private')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="selectionConfirmed('adminChangeStatusScope', { visiblity: 'private' })"
/>
<GenericConfirm
ref="confirmSetDirect"
:title="$t('admin_dash.users.bulk_actions.mark_as_direct')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="selectionConfirmed('adminChangeStatusScope', { visiblity: 'direct' })"
/>
</div>
</template>

View file

@ -25,6 +25,22 @@ const AdminStatusCard = {
},
changeVisibility (v) {
this.$store.dispatch('adminChangeStatusScope', { opts: { id: this.statusDetails.id, visibility: v }}).then(res => parseStatus(res)).then(s => this.statusCache = s)
},
// show popup
confirmSelection(box) {
this.$refs[box].show()
this.$refs.dropdown.hidePopover()
},
// do the thing
selectionConfirmed(action, opts) {
const restricted = []
const s = this.$refs.userList.getSelected()
s.forEach(u => {
if (restricted.includes(action) !== false || u.id !== this.$store.state.users.currentUser.id) {
this.$store.dispatch(action, { id: this.statusDetails.id, ...(opts || {}) })
}
})
this.reset()
}
},
components: {

View file

@ -86,123 +86,21 @@ const UsersTab = {
reset () {
this.$refs.userList.reset()
},
activateSelection () {
this.$refs.confirmActivate.show()
// show popup
confirmSelection(box) {
this.$refs[box].show()
this.$refs.dropdown.hidePopover()
},
activateSelectionConfirmed () {
const s = this.$refs.userList.getSelected()
s.forEach(u => this.$store.dispatch('adminActivateUser', this.$store.getters.findUser(u.id)))
this.reset()
},
deactivateSelection () {
this.$refs.confirmDeactivate.show()
this.$refs.dropdown.hidePopover()
},
deactivateSelectionConfirmed () {
// do the thing
selectionConfirmed(action) {
const restricted = []
const s = this.$refs.userList.getSelected()
s.forEach(u => {
// avoid deactivating yourself
if (u.id !== this.$store.state.users.currentUser.id) {
this.$store.dispatch('adminDeactivateUser', this.$store.getters.findUser(u.id))
if (restricted.includes(action) !== false || u.id !== this.$store.state.users.currentUser.id) {
this.$store.dispatch(action, this.$store.getters.findUser(u.id))
}
})
this.reset()
},
deleteSelection () {
this.$refs.confirmDelete.show()
this.$refs.dropdown.hidePopover()
},
deleteSelectionConfirmed () {
const s = this.$refs.userList.getSelected()
s.forEach(u => {
// avoid deleting yourself
if (u.id !== this.$store.state.users.currentUser.id) {
this.$store.dispatch('adminDeleteUser', this.$store.getters.findUser(u.id))
}
})
this.reset()
},
grantAdminSelection () {
this.$refs.confirmGrantAdmin.show()
this.$refs.dropdown.hidePopover()
},
grantAdminSelectionConfirmed () {
const s = this.$refs.userList.getSelected()
s.forEach(u => this.$store.dispatch('adminAddUserToAdminGroup', this.$store.getters.findUser(u.id)))
this.reset()
},
revokeAdminSelection () {
this.$refs.confirmRevokeAdminSelection.show()
this.$refs.dropdown.hidePopover()
},
revokeAdminSelectionConfirmed () {
const s = this.$refs.userList.getSelected()
s.forEach(u => {
// avoid shooting yourself in the foot
if (u.id !== this.$store.state.users.currentUser.id) {
this.$store.dispatch('adminRemoveUserToAdminGroup', this.$store.getters.findUser(u.id))
}
})
this.reset()
},
grantModeratorSelection () {
this.$refs.confirmGrantModeratorSelection.show()
this.$refs.dropdown.hidePopover()
},
grantModeratorSelectionConfirmed () {
const s = this.$refs.userList.getSelected()
s.forEach(u => this.$store.dispatch('adminAddUserToModeratorGroup', this.$store.getters.findUser(u.id)))
this.reset()
},
revokeModeratorSelection () {
this.$refs.confirmRevokeModeratorSelection.show()
this.$refs.dropdown.hidePopover()
},
revokeModeratorSelectionConfirmed () {
const s = this.$refs.userList.getSelected()
s.forEach(u => {
// you know the drill
if (u.id !== this.$store.state.users.currentUser.id) {
this.$store.dispatch('adminRemoveUserToModeratorGroup', this.$store.getters.findUser(u.id))
}
})
this.reset()
},
approveSelection () {
this.$refs.confirmApproveSelection.show()
this.$refs.dropdown.hidePopover()
},
approveSelectionConfirmed () {
const s = this.$refs.userList.getSelected()
s.forEach(u => this.$store.dispatch('adminApproveUser', this.$store.getters.findUser(u.id)))
this.reset()
},
confirmUserSelection () {
this.$refs.confirmSelection
},
confirmUserSelectionConfirmed () {
const s = this.$refs.userList.getSelected()
s.forEach(u => this.$store.dispatch('adminConfirmUser', this.$store.getters.findUser(u.id)))
this.reset()
},
resendEmailSelection () {
this.$refs.resendEmailSelection.show()
this.$refs.dropdown.hidePopover()
},
resendEmailSelectionConfirmed () {
const s = this.$refs.userList.getSelected()
s.forEach(u => this.$store.dispatch('adminResendConfirmationEmail', this.$store.getters.findUser(u.id)))
this.reset()
},
requirePasswordChangeSelection () {
this.$refs.requirePasswordChangeSelection.show()
this.$refs.dropdown.hidePopover()
},
requirePasswordChangeSelectionConfirmed () {
const s = this.$refs.userList.getSelected()
s.forEach(u => this.$store.dispatch('adminRequirePasswordChange', this.$store.getters.findUser(u.id)))
this.reset()
}
},
mounted () {

View file

@ -136,7 +136,7 @@
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="activateSelection"
@click="confirmSelection('confirmActivate')"
>
{{ $t('admin_dash.users.activate') }}
</button>
@ -144,7 +144,7 @@
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="deactivateSelection"
@click="confirmSelection('confirmDeactivate')"
>
{{ $t('admin_dash.users.deactivate') }}
</button>
@ -152,7 +152,7 @@
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="deleteSelection"
@click="confirmSelection('confirmDelete')"
>
{{ $t('admin_dash.users.delete') }}
</button>
@ -160,7 +160,7 @@
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="grantAdminSelection"
@click="confirmSelection('confirmGrantAdmin')"
>
{{ $t('admin_dash.users.grant_admin') }}
</button>
@ -168,7 +168,7 @@
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="revokeAdminSelection"
@click="confirmSelection('confirmRevokeAdmin')"
>
{{ $t('admin_dash.users.revoke_admin') }}
</button>
@ -176,7 +176,7 @@
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="grantModeratorSelection"
@click="confirmSelection('confirmGrantModerator')"
>
{{ $t('admin_dash.users.grant_moderator') }}
</button>
@ -184,7 +184,7 @@
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="revokeModeratorSelection"
@click="confirmSelection('confirmRevokeModerator')"
>
{{ $t('admin_dash.users.revoke_moderator') }}
</button>
@ -192,7 +192,7 @@
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="approveSelection"
@click="confirmSelection('confirmApprove')"
>
{{ $t('admin_dash.users.approve') }}
</button>
@ -200,7 +200,7 @@
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="confirmUserSelection"
@click="confirmSelection('confirmConfirm')"
>
{{ $t('admin_dash.users.confirm_user') }}
</button>
@ -208,11 +208,27 @@
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="requirePasswordChangeSelection"
@click="confirmSelection('confirmResendEmail')"
>
{{ $t('admin_dash.users.resend_confirmation_email') }}
</button>
</div>
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="confirmSelection('confirmRequirePasswordChange')"
>
{{ $t('admin_dash.users.require_password_change') }}
</button>
</div>
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="confirmSelection('adminDisableMFA')"
>
{{ $t('admin_dash.users.disable_mfa') }}
</button>
</div>
</div>
</template>
</Popover>
@ -227,70 +243,84 @@
:title="$t('admin_dash.users.bulk_actions.activate')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="activateSelectedConfirmed"
@callback="selectionConfirmed('adminActivateUser')"
/>
<GenericConfirm
ref="confirmDeactivate"
:title="$t('admin_dash.users.bulk_actions.deactivate')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="deactivateSelectionConfirmed"
@callback="selectionConfirmed('adminDeactivateUser')"
/>
<GenericConfirm
ref="confirmDelete"
:title="$t('admin_dash.users.bulk_actions.delete')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="deleteSelectionConfirmed"
@callback="selectionConfirmed('adminDeleteUser')"
/>
<GenericConfirm
ref="confirmGrantAdmin"
:title="$t('admin_dash.users.bulk_actions.grant_admin')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="grantAdminSelectionConfirmed"
@callback="selectionConfirmed('adminAddUserToAdminGroup')"
/>
<GenericConfirm
ref="confirmRevokeAdmin"
:title="$t('admin_dash.users.bulk_actions.revoke_admin')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="revokeAdminSelectionConfirmed"
@callback="selectionConfirmed('adminRemoveUserFromAdminGroup')"
/>
<GenericConfirm
ref="confirmGrantModerator"
:title="$t('admin_dash.users.bulk_actions.grant_moderator')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="grantModeratorSelectionConfirmed"
@callback="selectionConfirmed('adminAddUserToModeratorGroup')"
/>
<GenericConfirm
ref="confirmRevokeModerator"
:title="$t('admin_dash.users.bulk_actions.revoke_moderator')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="revokeModeratorSelectionConfirmed"
@callback="selectionConfirmed('adminRemoveUserFromModeratorGroup')"
/>
<GenericConfirm
ref="confirmConfirmUser"
:title="$t('admin_dash.users.bulk_actions.confirmUser')"
ref="confirmApprove"
:title="$t('admin_dash.users.bulk_actions.approve')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="confirmUserSelectionConfirmed"
@callback="selectionConfirmed('adminApproveUser')"
/>
<GenericConfirm
ref="confirmConfirm"
:title="$t('admin_dash.users.bulk_actions.confirm')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="selectionConfirmed('adminConfirmUser')"
/>
<GenericConfirm
ref="confirmResendEmail"
:title="$t('admin_dash.users.bulk_actions.resend_confirmation_email')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="resendEmailSelectionConfirmed"
@callback="selectionConfirmed('adminResendConfirmationEmail')"
/>
<GenericConfirm
ref="confirmRequirePasswordChange"
:title="$t('admin_dash.users.bulk_actions.require_password_change')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="requirePasswordChangeSelectionConfirmed"
@callback="selectionConfirmed('adminRequirePasswordChange')"
/>
<GenericConfirm
ref="confirmDisableMFA"
:title="$t('admin_dash.users.bulk_actions.disable_mfa')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="selectionConfirmed('adminDisableMFA')"
/>
</div>
</template>

View file

@ -1173,7 +1173,9 @@
"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?"
"require_password_change": "Require Password Change For Selected Users?",
"resend_confirmation_email": "Resend Confirmation Email For Selected Users?",
"disable_mfa": "Disable MFA For Selected Users?"
},
"activate": "Activate",
"deactivate": "Deactivate",
@ -1186,6 +1188,7 @@
"confirm_user": "Confirm",
"resend_confirmation_email": "Resend Confirmation Email",
"require_password_change": "Require Password Change",
"disable_mfa": "Disable MFA",
"delete": "Delete",
"loading_user": "Loading user...",
"delete_user": "Delete User",

View file

@ -114,6 +114,9 @@ const adminSettingsStorage = {
adminChangeStatusScope (store, { opts }) {
return store.rootState.api.backendInteractor.adminChangeStatusScope({ opts })
},
adminDisableMFA (store, { opts }) {
return store.rootState.api.backendInteractor.adminDisableMFA({ opts })
},
loadFrontendsStuff ({ rootState, commit }) {
rootState.api.backendInteractor.fetchAvailableFrontends()
.then(frontends => commit('setAvailableFrontends', { frontends }))

View file

@ -146,6 +146,7 @@ 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_ADMIN_DISABLE_MFA_URL = '/api/v1/pleroma/admin/users/disable_mfa'
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}`
@ -1600,6 +1601,18 @@ const adminRequirePasswordChange = ({ user: { screen_name: nickname }, credentia
})
}
const adminDisableMFA = ({ user : { screen_name: nickname }, credentials }) => {
const url = PLEROMA_ADMIN_DISABLE_MFA_URL
return promisedRequest({
url,
credentials,
method: 'PUT',
payload: {
nickname
}
})
}
const announcementToPayload = ({ content, startsAt, endsAt, allDay }) => {
const payload = { content }
@ -2282,7 +2295,8 @@ const apiService = {
adminApproveUser,
adminListStatuses,
adminChangeStatusScope,
adminRequirePasswordChange
adminRequirePasswordChange,
adminDisableMFA
}
export default apiService