bulk menu and some cleanup, not finished yet

This commit is contained in:
luce 2025-08-18 16:47:18 +02:00
commit ec5dbe1792
11 changed files with 595 additions and 243 deletions

View file

@ -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')
}
}
}

View file

@ -0,0 +1,19 @@
<template>
<ConfirmModal
v-if="showing"
:title="title"
:cancel-text="cancelText"
:confirm-text="confirmText"
@accepted="callback"
@cancelled="hide"
/>
</template>
<script src="./generic_confirm.js" />
<style lang="scss">
.expiry-amount {
width: 4em;
text-align: right;
}
</style>

View file

@ -8,8 +8,8 @@ const SelectableList = {
}, },
props: { props: {
boxOnly: { boxOnly: {
type: Boolean, type: Boolean,
default: false default: false
}, },
items: { items: {
type: Array, type: Array,

View file

@ -2,6 +2,7 @@ import BasicUserCard from '../../basic_user_card/basic_user_card.vue'
import Checkbox from 'src/components/checkbox/checkbox.vue' import Checkbox from 'src/components/checkbox/checkbox.vue'
import PageList from 'src/components/page_list/page_list.vue' import PageList from 'src/components/page_list/page_list.vue'
import AdminStatusCard from 'src/components/settings_modal/admin_tabs/admin_status_card.vue' import AdminStatusCard from 'src/components/settings_modal/admin_tabs/admin_status_card.vue'
import Modal from 'src/components/modal/modal.vue'
const AdminCard = { const AdminCard = {
props: { props: {
@ -80,6 +81,7 @@ const AdminCard = {
Checkbox, Checkbox,
PageList, PageList,
AdminStatusCard, AdminStatusCard,
Modal
}, },
methods: { methods: {
toggleAdmin (v) { toggleAdmin (v) {

View file

@ -4,19 +4,21 @@
{{ $t('admin_dash.users.loading_user') }} {{ $t('admin_dash.users.loading_user') }}
</div> </div>
<div v-else> <div v-else>
<div v-if="userDetails.id !== $store.state.users.currentUser.id"> <BasicUserCard :user="user" />
<BasicUserCard :user="user" /> <div v-if="!topLevelExpanded">
<div v-if="!topLevelExpanded"> <button
<button class="button button-default btn"
class="button button-default btn" type="button"
type="button" @click="topLevelExpanded = true"
@click="topLevelExpanded = true" >
> {{ $t('admin_dash.users.expand_user') }}
{{ $t('admin_dash.users.expand_user') }} </button>
</button> </div>
</div> <div
<div v-else
v-else >
<Modal
@backdrop-clicked="() => { topLevelExpanded = false }"
> >
<ul class="setting-list"> <ul class="setting-list">
<li> <li>
@ -99,81 +101,81 @@
</button> </button>
</li> </li>
</ul> </ul>
</div> </Modal>
<div v-if="!timelineExpanded"> </div>
<button <div v-if="!timelineExpanded">
class="button button-default btn" <button
type="button" class="button button-default btn"
@click="timelineExpanded = true" type="button"
> @click="timelineExpanded = true"
{{ $t('admin_dash.users.expand_timeline') }}
</button>
</div>
<div
v-else
class="setting-item"
> >
<button {{ $t('admin_dash.users.expand_timeline') }}
class="button button-default btn" </button>
type="button" </div>
@click="timelineExpanded = false" <div
> v-else
{{ $t('admin_dash.users.collapse_timeline') }} class="setting-item"
</button> >
<PageList <button
ref="timelineList" class="button button-default btn"
:refresh="true" type="button"
:get-key="i => i" @click="timelineExpanded = false"
:box-only="true"
:page-size="20"
:single-page="true"
:fetch-page="(store, opts) => fetchStatuses(store, opts)"
>
<template #header>
<button
class="button button-default btn"
type="button"
@click="deleteSelection"
>
{{ $t('admin_dash.users.delete') }}
</button>
</template>
<template #item="{item}">
<AdminStatusCard :status-details="item" />
</template>
<template #empty>
<p> {{ $t('admin_dash.users.user_has_no_posts') }} </p>
</template>
<template #load>
<p> {{ $t('admin_dash.users.loading') }} </p>
</template>
</PageList>
</div>
<div v-if="!jsonExpanded">
<button
class="button button-default btn"
type="button"
@click="jsonExpanded = true"
>
{{ $t('admin_dash.users.expand_raw_info') }}
</button>
</div>
<div
v-else
class="setting-item"
> >
<button {{ $t('admin_dash.users.collapse_timeline') }}
class="button button-default btn" </button>
type="button" <PageList
@click="jsonExpanded = false" ref="timelineList"
> :refresh="true"
{{ $t('admin_dash.users.collapse_raw_info') }} :get-key="i => i"
</button> :box-only="true"
<h2> {{ $t('admin_dash.users.title_database') }} </h2> :page-size="20"
<pre> {{ JSON.stringify(user, null, 2) }} </pre> :single-page="true"
<h2> {{ $t('admin_dash.users.title_details') }} </h2> :fetch-page="(store, opts) => fetchStatuses(store, opts)"
<pre> {{ JSON.stringify(user_details, null, 2) }} </pre> >
</div> <template #header>
<button
class="button button-default btn"
type="button"
@click="deleteSelection"
>
{{ $t('admin_dash.users.delete') }}
</button>
</template>
<template #item="{item}">
<AdminStatusCard :status-details="item" />
</template>
<template #empty>
<p> {{ $t('admin_dash.users.user_has_no_posts') }} </p>
</template>
<template #load>
<p> {{ $t('admin_dash.users.loading') }} </p>
</template>
</PageList>
</div>
<div v-if="!jsonExpanded">
<button
class="button button-default btn"
type="button"
@click="jsonExpanded = true"
>
{{ $t('admin_dash.users.expand_raw_info') }}
</button>
</div>
<div
v-else
class="setting-item"
>
<button
class="button button-default btn"
type="button"
@click="jsonExpanded = false"
>
{{ $t('admin_dash.users.collapse_raw_info') }}
</button>
<h2> {{ $t('admin_dash.users.title_database') }} </h2>
<pre> {{ JSON.stringify(user, null, 2) }} </pre>
<h2> {{ $t('admin_dash.users.title_details') }} </h2>
<pre> {{ JSON.stringify(user_details, null, 2) }} </pre>
</div> </div>
</div> </div>
</div> </div>

View file

@ -5,6 +5,8 @@ import ProgressButton from 'src/components/progress_button/progress_button.vue'
import AdminCard from 'src/components/settings_modal/admin_tabs/admin_card.vue' import AdminCard from 'src/components/settings_modal/admin_tabs/admin_card.vue'
import PageList from 'src/components/page_list/page_list.vue' import PageList from 'src/components/page_list/page_list.vue'
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
import Popover from 'src/components/popover/popover.vue'
import GenericConfirm from 'src/components/confirm_modal/generic_confirm.vue'
const UsersTab = { const UsersTab = {
provide () { provide () {
@ -18,7 +20,7 @@ const UsersTab = {
init: false, init: false,
filtersOrigin: 'local', filtersOrigin: 'local',
filtersActivity: 'all', filtersActivity: 'all',
filtersPermission: 'all', filtersPrivileges: 'all',
filtersNeedApproval: false, filtersNeedApproval: false,
filtersUnconfirmed: false, filtersUnconfirmed: false,
filtersQuery: '', filtersQuery: '',
@ -30,10 +32,10 @@ const UsersTab = {
}, },
computed: { computed: {
filtersIsAdmin () { filtersIsAdmin () {
return this.filtersPermission === 'admin' || this.filtersPermission === 'modsnadmins' return this.filtersPrivileges === 'admin' || this.filtersPrivileges === 'modsnadmins'
}, },
filtersIsModerator () { filtersIsModerator () {
return this.filtersPermission === 'moderator' || this.filtersPermission === 'modsnadmins' return this.filtersPrivileges === 'moderator' || this.filtersPrivileges === 'modsnadmins'
}, },
filtersActive () { filtersActive () {
return this.filtersActivity === 'active' return this.filtersActivity === 'active'
@ -56,6 +58,8 @@ const UsersTab = {
ProgressButton, ProgressButton,
AdminCard, AdminCard,
TabSwitcher, TabSwitcher,
Popover,
GenericConfirm
}, },
methods: { methods: {
fetchPage (store, opts) { fetchPage (store, opts) {
@ -70,28 +74,134 @@ const UsersTab = {
needApproval: this.filtersNeedApproval, needApproval: this.filtersNeedApproval,
unconfirmed: this.filtersUnconfirmeUnconfirmed unconfirmed: this.filtersUnconfirmeUnconfirmed
} }
const users = store.dispatch('fetchAdminUsers', { ...opts, ...{ const nopts = { ...opts, ...{
query: this.filtersQuery, query: this.filtersQuery,
filters, filters,
name: this.filtersName, name: this.filtersName,
email: this.filtersEmail email: this.filtersEmail
}}) }}
const users = store.dispatch('fetchAdminUsers', nopts)
return users return users
}, },
reset () { reset () {
this.$refs.userList.reset() this.$refs.userList.reset()
}, },
activateSelection () { activateSelection () {
this.$refs.confirmActivate.show()
this.$refs.dropdown.hidePopover()
},
activateSelectionConfirmed () {
const s = this.$refs.userList.getSelected() const s = this.$refs.userList.getSelected()
s.forEach(u => this.$store.dispatch('adminActivateUser', this.$store.getters.findUser(u.id))) s.forEach(u => this.$store.dispatch('adminActivateUser', this.$store.getters.findUser(u.id)))
this.reset()
}, },
deactivateSelection () { deactivateSelection () {
this.$refs.confirmDeactivate.show()
this.$refs.dropdown.hidePopover()
},
deactivateSelectionConfirmed () {
const s = this.$refs.userList.getSelected() const s = this.$refs.userList.getSelected()
s.forEach(u => this.$store.dispatch('adminDeactivateUser', this.$store.getters.findUser(u.id))) 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))
}
})
this.reset()
}, },
deleteSelection () { deleteSelection () {
this.$refs.confirmDelete.show()
this.$refs.dropdown.hidePopover()
},
deleteSelectionConfirmed () {
const s = this.$refs.userList.getSelected() const s = this.$refs.userList.getSelected()
s.forEach(u => this.$store.dispatch('adminDeleteUser', this.$store.getters.findUser(u.id))) 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() this.reset()
} }
}, },

View file

@ -1,3 +1,16 @@
.user-tab { .user-tab {
height: 100%; height: 100%;
} }
.query-label {
padding-right: 10px;
padding-left: 10px;
}
.filter-input {
width: 200px;
}
.filter-btn-box {
text-align: right;
}

View file

@ -1,35 +1,32 @@
<template> <template>
<div :label="$t('admin_dash.users.management')"> <div :label="$t('admin_dash.users.management')">
<div class="setting-item"> <div
<h2> {{ $t('admin_dash.users.title_filter_user_search') }} </h2> class="setting-item"
<ul >
class="setting-list" <h2> {{ $t('admin_dash.users.title_users') }} </h2>
> <ul class="setting-list">
<li> <li>
<label class="query-label"> {{ $t('admin_dash.users.label_query') }} </label>
<input <input
v-model="filtersQuery" v-model="filtersQuery"
:placeholder="$t('admin_dash.users.placeholder_query')" class="input string-input filter-input"
class="input string-input"
@input="reset()" @input="reset()"
> >
</li> <label class="query-label"> {{ $t('admin_dash.users.label_name') }} </label>
<li>
<input <input
v-model="filtersName" v-model="filtersName"
:placeholder="$t('admin_dash.users.placeholder_name')" class="input string-input filter-input"
class="input string-input"
@input="reset()" @input="reset()"
> >
</li> <label class="query-label"> {{ $t('admin_dash.users.label_email') }} </label>
<li>
<input <input
v-model="filtersEmail" v-model="filtersEmail"
:placeholder="$t('admin_dash.users.placeholder_email')" class="input string-input filter-input"
class="input string-input"
@input="reset()" @input="reset()"
> >
</li> </li>
<li> <li>
<label class="query-label"> {{ $t('admin_dash.users.label_origin') }} </label>
<Select <Select
v-model="filtersOrigin" v-model="filtersOrigin"
@update:model-value="reset" @update:model-value="reset"
@ -50,8 +47,7 @@
{{ $t('admin_dash.users.only_external') }} {{ $t('admin_dash.users.only_external') }}
</option> </option>
</Select> </Select>
</li> <label class="query-label"> {{ $t('admin_dash.users.label_activity') }} </label>
<li>
<Select <Select
v-model="filtersActivity" v-model="filtersActivity"
@update:model-value="reset" @update:model-value="reset"
@ -72,10 +68,9 @@
{{ $t('admin_dash.users.only_deactivated') }} {{ $t('admin_dash.users.only_deactivated') }}
</option> </option>
</Select> </Select>
</li> <label class="query-label"> {{ $t('admin_dash.users.label_privileges') }} </label>
<li>
<Select <Select
v-model="filtersPermission" v-model="filtersPrivileges"
@update:model-value="reset" @update:model-value="reset"
> >
<option <option
@ -102,33 +97,19 @@
</li> </li>
<li> <li>
<Checkbox <Checkbox
@update:model-value="v => {filtersNneedApproval = v; reset();}" class="query-label"
@update:model-value="v => {filtersNeedApproval = v; reset();}"
> >
{{ $t('admin_dash.users.only_unapproved') }} {{ $t('admin_dash.users.only_unapproved') }}
</Checkbox> </Checkbox>
</li>
<li>
<Checkbox <Checkbox
@update:model-value="v => {filtersUnconfirmed = v; reset();}" class="query-label"
@update:model-value="v => {filtersUncomfirmed = v; reset();}"
> >
{{ $t('admin_dash.users.only_unconfirmed') }} {{ $t('admin_dash.users.only_unconfirmed') }}
</Checkbox> </Checkbox>
</li> </li>
<li>
<button
class="button button-default btn"
type="button"
@click="reset"
>
{{ $t('admin_dash.users.refresh') }}
</button>
</li>
</ul> </ul>
</div>
<div
class="setting-item"
>
<h2> {{ $t('admin_dash.users.title_users') }} </h2>
<PageList <PageList
ref="userList" ref="userList"
:refresh="true" :refresh="true"
@ -138,33 +119,179 @@
:fetch-page="(store, opts) => fetchPage(store, opts)" :fetch-page="(store, opts) => fetchPage(store, opts)"
> >
<template #header> <template #header>
<button <Popover
class="button button-default btn" ref="dropdown"
type="button" trigger="click"
@click="activateSelection" placement="bottom"
> >
{{ $t('admin_dash.users.activate') }} <template #trigger>
</button> <button
<button class="button button-default btn"
class="button button-default btn" >
type="button" {{ $t('admin_dash.users.bulk_actions.title') }}
@click="deactivateSelection" </button>
> </template>
{{ $t('admin_dash.users.deactivate') }} <template #content>
</button> <div class="dropdown-menu">
<button <div class="menu-item dropdown-item">
class="button button-default btn" <button
type="button" class="main-button"
@click="deleteSelection" @click="activateSelection"
> >
{{ $t('admin_dash.users.delete') }} {{ $t('admin_dash.users.activate') }}
</button> </button>
</div>
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="deactivateSelection"
>
{{ $t('admin_dash.users.deactivate') }}
</button>
</div>
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="deleteSelection"
>
{{ $t('admin_dash.users.delete') }}
</button>
</div>
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="grantAdminSelection"
>
{{ $t('admin_dash.users.grant_admin') }}
</button>
</div>
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="revokeAdminSelection"
>
{{ $t('admin_dash.users.revoke_admin') }}
</button>
</div>
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="grantModeratorSelection"
>
{{ $t('admin_dash.users.grant_moderator') }}
</button>
</div>
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="revokeModeratorSelection"
>
{{ $t('admin_dash.users.revoke_moderator') }}
</button>
</div>
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="approveSelection"
>
{{ $t('admin_dash.users.approve') }}
</button>
</div>
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="confirmUserSelection"
>
{{ $t('admin_dash.users.confirm_user') }}
</button>
</div>
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="requirePasswordChangeSelection"
>
{{ $t('admin_dash.users.require_password_change') }}
</button>
</div>
</div>
</template>
</Popover>
</template> </template>
<template #item="{item}"> <template #item="{item}">
<AdminCard :user-details="item" /> <AdminCard :user-details="item" />
</template> </template>
</PageList> </PageList>
</div> </div>
<GenericConfirm
ref="confirmActivate"
: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"
/>
<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"
/>
<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"
/>
<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"
/>
<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"
/>
<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"
/>
<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"
/>
<GenericConfirm
ref="confirmConfirmUser"
:title="$t('admin_dash.users.bulk_actions.confirmUser')"
:cancel-text="$t('admin_dash.users.bulk_actions.no')"
:confirm-text="$t('admin_dash.users.bulk_actions.yes')"
@callback="confirmUserSelectionConfirmed"
/>
<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"
/>
<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"
/>
</div> </div>
</template> </template>
<script src="./users_tab.js"></script> <script src="./users_tab.js"></script>

View file

@ -1143,33 +1143,58 @@
"management": "Management", "management": "Management",
"search_users": "Search for users...", "search_users": "Search for users...",
"loading": "Loading...", "loading": "Loading...",
"placeholder_query": "Query", "label_query": "Query",
"placeholder_name": "Name", "label_name": "Name",
"placeholder_email": "Email", "label_email": "Email",
"label_origin": "Origin",
"label_activity": "Activity",
"label_privileges": "Privileges",
"all": "All", "all": "All",
"only_local": "Local only", "only_local": "Local Only",
"only_external": "External only", "only_external": "External Only",
"only_active": "Active only", "only_active": "Active Only",
"only_deactivated": "Deactivated only", "only_deactivated": "Deactivated Only",
"only_administrators": "Admin only", "only_administrators": "Admin Only",
"all_privileged": "All privileged", "all_privileged": "All Privileged",
"only_moderators": "Moderators only", "only_moderators": "Moderators Only",
"only_unapproved": "Unapproved only", "only_unapproved": "Unapproved Only",
"only_unconfirmed": "Unconfirmed only", "only_unconfirmed": "Unconfirmed Only",
"refresh": "Refresh", "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", "activate": "Activate",
"deactivate": "Deactivate", "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", "delete": "Delete",
"loading_user": "Loading user...", "loading_user": "Loading user...",
"delete_user": "Delete User",
"expand_user": "Expand user", "expand_user": "Expand user",
"collapse_user": "Collapse user", "collapse_user": "Collapse user",
"is_admin": "Is admin", "is_admin": "Is admin",
"is_moderator": "Is moderator", "is_moderator": "Is moderator",
"is_confirmed": "Is confirmed", "is_confirmed": "Is confirmed",
"resend_confirmation_email": "Resend confirmation email",
"approve": "Approve",
"is_active": "Is active", "is_active": "Is active",
"delete_user": "Delete user",
"expand_timeline": "Expand timeline", "expand_timeline": "Expand timeline",
"collapse_timeline": "Collapse timeline", "collapse_timeline": "Collapse timeline",
"expand_raw_info": "Expand raw info", "expand_raw_info": "Expand raw info",
@ -1188,7 +1213,6 @@
"link_source": "Source", "link_source": "Source",
"title_database": "Database", "title_database": "Database",
"title_details": "Details", "title_details": "Details",
"title_filter_user_search": "Filter User Search",
"title_users": "Users", "title_users": "Users",
"user_has_no_posts": "User has no posts" "user_has_no_posts": "User has no posts"
}, },

View file

@ -70,16 +70,22 @@ const adminSettingsStorage = {
.then(res => store.commit('updateRight', { user, right: 'admin', value: res.is_admin })) .then(res => store.commit('updateRight', { user, right: 'admin', value: res.is_admin }))
}, },
adminRemoveUserFromAdminGroup (store, user) { adminRemoveUserFromAdminGroup (store, user) {
return store.rootState.api.backendInteractor.adminRemoveUserFromAdminGroup({ user }) // prevent revokation of own rights
.then(res => store.commit('updateRight', { user, right: 'admin', value: res.is_admin })) 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) { adminAddUserToModeratorGroup (store, user) {
return store.rootState.api.backendInteractor.adminAddUserToModeratorGroup({ user }) return store.rootState.api.backendInteractor.adminAddUserToModeratorGroup({ user })
.then(res => store.commit('updateRight', { user, right: 'moderator', value: res.is_moderator })) .then(res => store.commit('updateRight', { user, right: 'moderator', value: res.is_moderator }))
}, },
adminRemoveUserFromModeratorGroup (store, user) { adminRemoveUserFromModeratorGroup (store, user) {
return store.rootState.api.backendInteractor.adminRemoveUserFromModeratorGroup({ user }) // prevent revokation of own rights
.then(res => store.commit('updateRight', { user, right: 'moderator', value: res.is_moderator })) 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) { adminActivateUser (store, user) {
return store.rootState.api.backendInteractor.activateUser({ user }) return store.rootState.api.backendInteractor.activateUser({ user })

View file

@ -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_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_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_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_RELOAD_URL = '/api/pleroma/admin/reload_emoji'
const PLEROMA_EMOJI_IMPORT_FS_URL = '/api/pleroma/emoji/packs/import' 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}` 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 }) => { const adminListUsers = ({ opts, credentials }) => {
// the reported list is hardly useful because standards are for dating i guess, // the reported list is hardly useful because standards are for dating i guess,
// so make sure to fetchIfMissing right afterward using this call // so make sure to fetchIfMissing right afterward using this call
const url = PLEROMA_ADMIN_USERS_URL(opts) const url = PLEROMA_ADMIN_USERS_URL(opts)
return promisedRequest({ return promisedRequest({
url: url, url: url,
credentials, credentials,
method: 'GET' method: 'GET'
}).then((data) => data.users.map(parseUser)) }).then((data) => data.users.map(parseUser))
} }
const adminAddUserToAdminGroup = ({ user, credentials }) => { const adminAddUserToAdminGroup = ({ user, credentials }) => {
const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'admin') const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'admin')
return promisedRequest({ url: url, return promisedRequest({ url: url,
credentials, credentials,
method: 'POST' method: 'POST'
}) })
} }
const adminRemoveUserFromAdminGroup = ({ user, credentials }) => { const adminRemoveUserFromAdminGroup = ({ user, credentials }) => {
const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'admin') const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'admin')
return promisedRequest({ url: url, return promisedRequest({ url: url,
credentials, credentials,
method: 'DELETE' method: 'DELETE'
}) })
} }
const adminAddUserToModeratorGroup = ({ user, credentials }) => { const adminAddUserToModeratorGroup = ({ user, credentials }) => {
const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'moderator') const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'moderator')
return promisedRequest({ url: url, return promisedRequest({ url: url,
credentials, credentials,
method: 'POST' method: 'POST'
}) })
} }
const adminRemoveUserFromModeratorGroup = ({ user, credentials }) => { const adminRemoveUserFromModeratorGroup = ({ user, credentials }) => {
const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'moderator') const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'moderator')
return promisedRequest({ url: url, return promisedRequest({ url: url,
credentials, credentials,
method: 'DELETE' method: 'DELETE'
}) })
} }
const adminConfirmUser = ({user: { screen_name: nickname }, credentials }) => { const adminConfirmUser = ({user: { screen_name: nickname }, credentials }) => {
const url = PLEROMA_ADMIN_CONFIRM_USER_URL const url = PLEROMA_ADMIN_CONFIRM_USER_URL
return promisedRequest({url: url, return promisedRequest({url: url,
credentials, credentials,
method: 'PATCH', method: 'PATCH',
payload: { payload: {
nicknames: [nickname] nicknames: [nickname]
} }
}) })
} }
const adminResendConfirmationEmail = ({user: { screen_name: nickname }, credentials }) => { const adminResendConfirmationEmail = ({user: { screen_name: nickname }, credentials }) => {
const url = PLEROMA_ADMIN_RESEND_CONFIRMATION_EMAIL_URL const url = PLEROMA_ADMIN_RESEND_CONFIRMATION_EMAIL_URL
return promisedRequest({url: url, return promisedRequest({url: url,
credentials, credentials,
method: 'PATCH', method: 'PATCH',
payload: { payload: {
nicknames: [nickname] nicknames: [nickname]
} }
}) })
} }
const adminApproveUser = ({user : { screen_name: nickname }, credentials }) => { const adminApproveUser = ({user : { screen_name: nickname }, credentials }) => {
const url = PLEROMA_ADMIN_APPROVE_URL const url = PLEROMA_ADMIN_APPROVE_URL
const r = promisedRequest({url: url, const r = promisedRequest({url: url,
credentials, credentials,
method: 'PATCH', method: 'PATCH',
payload: { payload: {
nicknames: [nickname] nicknames: [nickname]
} }
}) })
return r return r
} }
const adminListStatuses = ({user: { id }, opts: { pageSize, godmode, withReblogs }, credentials }) => { const adminListStatuses = ({user: { id }, opts: { pageSize, godmode, withReblogs }, credentials }) => {
const url = PLEROMA_ADMIN_LIST_STATUSES_URL(id, pageSize, godmode, withReblogs) const url = PLEROMA_ADMIN_LIST_STATUSES_URL(id, pageSize, godmode, withReblogs)
return promisedRequest({url: url, return promisedRequest({url: url,
credentials, credentials,
method: 'GET' method: 'GET'
}) })
} }
const adminChangeStatusScope = ({opts: { id, sensitive, visibility}, credentials }) => { const adminChangeStatusScope = ({opts: { id, sensitive, visibility}, credentials }) => {
const url = PLEROMA_ADMIN_CHANGE_STATUS_SCOPE_URL(id) const url = PLEROMA_ADMIN_CHANGE_STATUS_SCOPE_URL(id)
var payload = {} var payload = {}
if (typeof(sensitive) !== 'undefined') { if (typeof(sensitive) !== 'undefined') {
payload['sensitive'] = sensitive payload['sensitive'] = sensitive
} }
if (typeof(visibility) !== 'undefined') { if (typeof(visibility) !== 'undefined') {
payload['visibility'] = visibility payload['visibility'] = visibility
} }
return promisedRequest({url: url, return promisedRequest({
credentials, url,
method: 'PUT', credentials,
payload 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 announcementToPayload = ({ content, startsAt, endsAt, allDay }) => {
const payload = { content } const payload = { content }
@ -2270,6 +2282,7 @@ const apiService = {
adminApproveUser, adminApproveUser,
adminListStatuses, adminListStatuses,
adminChangeStatusScope, adminChangeStatusScope,
adminRequirePasswordChange
} }
export default apiService export default apiService