diff --git a/src/components/alert.style.js b/src/components/alert.style.js
index 8a6f842ed..6876faca8 100644
--- a/src/components/alert.style.js
+++ b/src/components/alert.style.js
@@ -4,6 +4,7 @@ export default {
validInnerComponents: ['Text', 'Icon', 'Link', 'Border', 'ButtonUnstyled'],
variants: {
normal: '.neutral',
+ info: '.info',
error: '.error',
warning: '.warning',
success: '.success',
@@ -47,5 +48,11 @@ export default {
background: '--cGreen',
},
},
+ {
+ variant: 'info',
+ directives: {
+ background: '--cBlue',
+ },
+ },
],
}
diff --git a/src/components/announcement/announcement.js b/src/components/announcement/announcement.js
index 7f559f8fe..ee427533f 100644
--- a/src/components/announcement/announcement.js
+++ b/src/components/announcement/announcement.js
@@ -31,9 +31,7 @@ const Announcement = {
canEditAnnouncement() {
return (
this.currentUser &&
- this.currentUser.privileges.includes(
- 'announcements_manage_announcements',
- )
+ this.currentUser.privileges.has('announcements_manage_announcements')
)
},
content() {
diff --git a/src/components/interactions/interactions.js b/src/components/interactions/interactions.js
index 142a5fc8a..5dc2a8714 100644
--- a/src/components/interactions/interactions.js
+++ b/src/components/interactions/interactions.js
@@ -17,7 +17,7 @@ const Interactions = {
allowFollowingMove:
this.$store.state.users.currentUser.allow_following_move,
filterMode: tabModeDict.mentions,
- canSeeReports: this.$store.state.users.currentUser.privileges.includes(
+ canSeeReports: this.$store.state.users.currentUser.has.has(
'reports_manage_reports',
),
}
diff --git a/src/components/list/list.js b/src/components/list/list.js
index d61f9694a..4ddc645e2 100644
--- a/src/components/list/list.js
+++ b/src/components/list/list.js
@@ -14,7 +14,7 @@ const List = {
},
getKey: {
type: Function,
- default: (item) => item,
+ default: (item) => item.id,
},
getClass: {
type: Function,
@@ -37,7 +37,7 @@ const List = {
default: null,
},
},
- emits: ['fetchRequested'],
+ emits: ['fetchRequested', 'select'],
components: {
Checkbox,
},
@@ -56,11 +56,14 @@ const List = {
allKeys() {
return new Set(this.finalItems.map(this.getKey))
},
- filteredSelected() {
- return [...this.allKeys.values().filter((key) => this.selected.has(key))]
+ selectedItems() {
+ return this.items.filter((item) => this.selected.has(this.getKey(item)))
},
allSelected() {
- return this.selected.size === this.finalItems.length
+ return (
+ this.selected.size !== 0 &&
+ this.selected.size === this.finalItems.length
+ )
},
noneSelected() {
return this.selected.size === 0
@@ -101,6 +104,7 @@ const List = {
.catch((error) => {
this.loading = false
this.error = error
+ console.error('Error loading list data:', error)
})
},
reset() {
@@ -124,19 +128,17 @@ const List = {
}
},
isSelected(item) {
- return this.filteredSelected.indexOf(this.getKey(item)) !== -1
+ return this.selected.has(this.getKey(item))
},
toggle(checked, item) {
const key = this.getKey(item)
- const oldChecked = this.isSelected(key)
- if (checked !== oldChecked) {
- if (checked) {
- this.selected.add(key)
- } else {
- this.selected.delete(key)
- }
+ if (checked) {
+ this.selected.add(key)
+ } else {
+ this.selected.delete(key)
}
- this.$emit('selected', this.selected)
+
+ this.$emit('select', this.selected)
},
toggleAll(value) {
if (value) {
@@ -144,7 +146,7 @@ const List = {
} else {
this.selected = new Set([])
}
- this.$emit('selected', this.selected)
+ this.$emit('select', this.selected)
},
},
}
diff --git a/src/components/list/list.vue b/src/components/list/list.vue
index ff6259422..317eb293f 100644
--- a/src/components/list/list.vue
+++ b/src/components/list/list.vue
@@ -19,7 +19,7 @@
-
+
diff --git a/src/components/popover/popover.scss b/src/components/popover/popover.scss
index a166e2196..7dfaa28a5 100644
--- a/src/components/popover/popover.scss
+++ b/src/components/popover/popover.scss
@@ -96,6 +96,11 @@
content: "✓";
}
+ &.menu-checkbox-indeterminate::after {
+ font-size: 1.25em;
+ content: "–";
+ }
+
&.-radio {
border-radius: 9999px;
@@ -103,6 +108,11 @@
font-size: 2em;
content: "•";
}
+
+ &.menu-checkbox-indeterminate::after {
+ font-size: 2em;
+ content: "–";
+ }
}
}
}
diff --git a/src/components/settings_modal/admin_tabs/admin_card.js b/src/components/settings_modal/admin_tabs/admin_card.js
index f41938f2c..6d9e17b36 100644
--- a/src/components/settings_modal/admin_tabs/admin_card.js
+++ b/src/components/settings_modal/admin_tabs/admin_card.js
@@ -1,8 +1,7 @@
import { defineAsyncComponent } from 'vue'
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
-
-import { useAdminUsersStore } from 'src/stores/adminUsers.js'
+import ModerationTools from 'src/components/moderation_tools/moderation_tools.vue'
const AdminCard = {
props: {
@@ -12,23 +11,15 @@ const AdminCard = {
},
components: {
BasicUserCard,
- ModerationTools: defineAsyncComponent(
- () => import('src/components/moderation_tools/moderation_tools.vue'),
- ),
+ ModerationTools,
},
computed: {
user() {
return this.$store.getters.findUser(this.userId)
},
- userAdminData() {
- return useAdminUsersStore().getUser(this.userId)
- },
relationship() {
return this.$store.getters.relationship(this.userId)
},
- isLocal() {
- return this.user.is_local
- },
isAdmin() {
return this.user.rights.admin
},
@@ -38,12 +29,6 @@ const AdminCard = {
isActivated() {
return !this.user.deactivated
},
- isApproved() {
- return this.userAdminData.is_approved
- },
- isConfirmed() {
- return this.userAdminData.is_confirmed
- },
},
}
diff --git a/src/components/settings_modal/admin_tabs/admin_card.vue b/src/components/settings_modal/admin_tabs/admin_card.vue
index c2ae295fd..9f459231d 100644
--- a/src/components/settings_modal/admin_tabs/admin_card.vue
+++ b/src/components/settings_modal/admin_tabs/admin_card.vue
@@ -26,7 +26,7 @@
@@ -37,14 +37,32 @@
{{ $t('admin_dash.users.indicator.deactivated') }}
+
+
+
diff --git a/src/components/settings_modal/admin_tabs/frontends_tab.js b/src/components/settings_modal/admin_tabs/frontends_tab.js
index f3348f9f4..ea7b970fb 100644
--- a/src/components/settings_modal/admin_tabs/frontends_tab.js
+++ b/src/components/settings_modal/admin_tabs/frontends_tab.js
@@ -38,7 +38,7 @@ const FrontendsTab = {
},
created() {
if (this.user.rights.admin) {
- this.$store.dispatch('loadFrontendsStuff')
+ useAdminSettingsStore().loadFrontendsStuff()
}
},
computed: {
@@ -77,7 +77,7 @@ const FrontendsTab = {
this.working = false
})
.then(async (response) => {
- this.$store.dispatch('loadFrontendsStuff')
+ useAdminSettingsStore().loadFrontendsStuff()
if (response.error) {
const reason = await response.error.json()
useInterfaceStore().pushGlobalNotice({
diff --git a/src/components/settings_modal/admin_tabs/users_tab.js b/src/components/settings_modal/admin_tabs/users_tab.js
index 4429f1fe3..7237c3a67 100644
--- a/src/components/settings_modal/admin_tabs/users_tab.js
+++ b/src/components/settings_modal/admin_tabs/users_tab.js
@@ -4,15 +4,26 @@ import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
import Checkbox from 'src/components/checkbox/checkbox.vue'
import GenericConfirm from 'src/components/confirm_modal/generic_confirm.vue'
import List from 'src/components/list/list.vue'
-import Popover from 'src/components/popover/popover.vue'
+import ModerationTools from 'src/components/moderation_tools/moderation_tools.vue'
import ProgressButton from 'src/components/progress_button/progress_button.vue'
import Select from 'src/components/select/select.vue'
import AdminCard from 'src/components/settings_modal/admin_tabs/admin_card.vue'
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
-import { useAdminUsersStore } from 'src/stores/adminUsers.js'
+import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
const UsersTab = {
+ components: {
+ Checkbox,
+ Select,
+ BasicUserCard,
+ List,
+ ProgressButton,
+ AdminCard,
+ TabSwitcher,
+ ModerationTools,
+ GenericConfirm,
+ },
provide() {
return {
defaultDraftMode: true,
@@ -90,7 +101,7 @@ const UsersTab = {
local: this.filtersLocal,
external: this.filtersExternal,
needApproval: this.filtersNeedApproval,
- unconfirmed: this.filtersUnconfirmeUnconfirmed,
+ unconfirmed: this.filtersUnconfirmed,
}
return {
@@ -102,51 +113,15 @@ const UsersTab = {
}
},
},
- components: {
- Checkbox,
- Select,
- BasicUserCard,
- List,
- ProgressButton,
- AdminCard,
- TabSwitcher,
- Popover,
- GenericConfirm,
- },
methods: {
fetchUsers(page) {
- return useAdminUsersStore()
+ return useAdminSettingsStore()
.fetchAdminUsers({
...this.fetchOptions,
page,
})
.then(({ count, users }) => ({ count, items: users }))
},
- /**
- * show the confirmation box for bulk actions.
- * @param {string} box ref name specified for the confirm component
- */
- confirmSelection(box) {
- this.$refs[box].show()
- this.$refs.dropdown.hidePopover()
- },
- /**
- * called when a bulk action was confirmed
- * @param {string} action
- */
- selectionConfirmed(action) {
- const restricted = []
- const s = this.$refs.userList.getSelected()
- s.forEach((u) => {
- if (
- restricted.includes(action) !== false ||
- u.id !== this.$store.state.users.currentUser.id
- ) {
- const uf = this.$store.getters.findUser(u.id)
- this.$store.dispatch(action, this.$store.getters.findUser(u.id))
- }
- })
- },
},
watch: {
fetchOptions() {
diff --git a/src/components/settings_modal/admin_tabs/users_tab.vue b/src/components/settings_modal/admin_tabs/users_tab.vue
index 45f69a721..1dbd7bcb0 100644
--- a/src/components/settings_modal/admin_tabs/users_tab.vue
+++ b/src/components/settings_modal/admin_tabs/users_tab.vue
@@ -115,7 +115,7 @@
-
+
{{ $t('admin_dash.users.options.only_unconfirmed') }}
@@ -123,130 +123,15 @@
-
-
-
-
-
-
-
-
-
+
+
-
+
loading
@@ -255,102 +140,6 @@
no users
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/components/settings_modal/helpers/shared_computed_object.js b/src/components/settings_modal/helpers/shared_computed_object.js
index 39e0b5b9c..4a3b9d4f8 100644
--- a/src/components/settings_modal/helpers/shared_computed_object.js
+++ b/src/components/settings_modal/helpers/shared_computed_object.js
@@ -1,6 +1,7 @@
import { mapState as mapPiniaState } from 'pinia'
import { mapState } from 'vuex'
+import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
const SharedComputedObject = () => ({
@@ -8,9 +9,11 @@ const SharedComputedObject = () => ({
...mapPiniaState(useMergedConfigStore, {
expertLevel: (store) => store.mergedConfig.expertLevel,
}),
+ ...mapPiniaState(useAdminSettingsStore, {
+ adminConfig: (store) => store.config,
+ adminDraft: (store) => store.draft,
+ }),
...mapState({
- adminConfig: (state) => state.adminSettings.config,
- adminDraft: (state) => state.adminSettings.draft,
user: (state) => state.users.currentUser,
}),
})
diff --git a/src/components/status_action_buttons/buttons_definitions.js b/src/components/status_action_buttons/buttons_definitions.js
index 9efc77bff..946c0cfd7 100644
--- a/src/components/status_action_buttons/buttons_definitions.js
+++ b/src/components/status_action_buttons/buttons_definitions.js
@@ -229,7 +229,7 @@ export const BUTTONS = [
return (
loggedIn &&
(status.user.id === currentUser.id ||
- currentUser.privileges.includes('messages_delete'))
+ currentUser.privileges.has('messages_delete'))
)
},
confirm: ({ getters }) => useMergedConfigStore().mergedConfig.modalOnDelete,
diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js
index d987695dd..1a1c92033 100644
--- a/src/components/user_card/user_card.js
+++ b/src/components/user_card/user_card.js
@@ -280,7 +280,7 @@ export default {
},
},
visibleRole() {
- if (!this.newShowRole) {
+ if (!this.user.show_role && !this.user.adminData) {
return
}
const rights = this.user.rights
diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue
index 3eb142954..cd2049173 100644
--- a/src/components/user_card/user_card.vue
+++ b/src/components/user_card/user_card.vue
@@ -291,8 +291,7 @@
{
export const mutations = {
tagUser(state, { user: { id }, tag }) {
const user = state.usersObject[id]
- const tags = user.tags || []
- const newTags = tags.concat([tag])
- user.tags = newTags
+ user.tags.add(tag)
},
untagUser(state, { user: { id }, tag }) {
const user = state.usersObject[id]
- const tags = user.tags || []
- const newTags = tags.filter((t) => t !== tag)
- user.tags = newTags
+ user.tags.delete(tag)
},
updateRight(state, { user: { id }, right, value }) {
const user = state.usersObject[id]
@@ -186,9 +182,12 @@ export const mutations = {
newRights[right] = value
user.rights = newRights
},
- updateActivationStatus(state, { user: { id }, deactivated }) {
- const user = state.usersObject[id]
- user.deactivated = deactivated
+ updateUserAdminData(state, { user }) {
+ const { id } = user
+ const localUser = state.usersObject[id]
+ localUser.adminData = user
+ localUser.deactivated = !user.is_active
+ localUser.tags = new Set(user.tags)
},
setCurrentUser(state, user) {
state.lastLoginName = user.screen_name
@@ -369,10 +368,22 @@ const users = {
getters,
actions: {
fetchUserIfMissing(store, id) {
- if (!store.getters.findUser(id)) {
- store.dispatch('fetchUser', id)
+ const user = store.getters.findUser(id)
+ if (!user) {
+ return store.dispatch('fetchUser', id)
+ } else {
+ return Promise.resolve(user)
}
},
+ updateUserAdminData(store, { userAdminData }) {
+ return store
+ .dispatch('fetchUserIfMissing', userAdminData.id)
+ .then((user) => {
+ user.adminData = userAdminData
+ store.commit('addNewUsers', [user])
+ return user
+ })
+ },
fetchUser(store, id) {
return store.rootState.api.backendInteractor
.fetchUser({ id })
@@ -541,15 +552,6 @@ const users = {
commit('updateUserRelationship', [relationship]),
)
},
- toggleActivationStatus({ rootState, commit }, { user }) {
- const api = user.deactivated
- ? rootState.api.backendInteractor.activateUser
- : rootState.api.backendInteractor.deactivateUser
- api({ user }).then((user) => {
- const deactivated = !user.is_active
- commit('updateActivationStatus', { user, deactivated })
- })
- },
registerPushNotifications(store) {
const token = store.state.currentUser.credentials
const vapidPublicKey = useInstanceStore().vapidPublicKey
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index 810baad82..87e477a62 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -20,12 +20,6 @@ const CHANGE_EMAIL_URL = '/api/pleroma/change_email'
const CHANGE_PASSWORD_URL = '/api/pleroma/change_password'
const MOVE_ACCOUNT_URL = '/api/pleroma/move_account'
const ALIASES_URL = '/api/pleroma/aliases'
-const TAG_USER_URL = '/api/pleroma/admin/users/tag'
-const PERMISSION_GROUP_URL = (screenName, right) =>
- `/api/pleroma/admin/users/${screenName}/permission_group/${right}`
-const ACTIVATE_USER_URL = '/api/pleroma/admin/users/activate'
-const DEACTIVATE_USER_URL = '/api/pleroma/admin/users/deactivate'
-const ADMIN_USERS_URL = '/api/v1/pleroma/admin/users'
const SUGGESTIONS_URL = '/api/v1/suggestions'
const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings'
const NOTIFICATION_READ_URL = '/api/v1/pleroma/notifications/read'
@@ -121,7 +115,6 @@ const PLEROMA_CHAT_MESSAGES_URL = (id) => `/api/v1/pleroma/chats/${id}/messages`
const PLEROMA_CHAT_READ_URL = (id) => `/api/v1/pleroma/chats/${id}/read`
const PLEROMA_DELETE_CHAT_MESSAGE_URL = (chatId, messageId) =>
`/api/v1/pleroma/chats/${chatId}/messages/${messageId}`
-const PLEROMA_ADMIN_REPORTS = '/api/v1/pleroma/admin/reports'
const PLEROMA_BACKUP_URL = '/api/v1/pleroma/backups'
const PLEROMA_ANNOUNCEMENTS_URL = '/api/v1/pleroma/admin/announcements'
const PLEROMA_POST_ANNOUNCEMENT_URL = '/api/v1/pleroma/admin/announcements'
@@ -138,6 +131,7 @@ const PLEROMA_BOOKMARK_FOLDERS_URL = '/api/v1/pleroma/bookmark_folders'
const PLEROMA_BOOKMARK_FOLDER_URL = (id) =>
`/api/v1/pleroma/bookmark_folders/${id}`
+const PLEROMA_ADMIN_REPORTS = '/api/v1/pleroma/admin/reports'
const PLEROMA_ADMIN_CONFIG_URL = '/api/v1/pleroma/admin/config'
const PLEROMA_ADMIN_DESCRIPTIONS_URL =
'/api/v1/pleroma/admin/config/descriptions'
@@ -145,7 +139,10 @@ const PLEROMA_ADMIN_FRONTENDS_URL = '/api/v1/pleroma/admin/frontends'
const PLEROMA_ADMIN_FRONTENDS_INSTALL_URL =
'/api/v1/pleroma/admin/frontends/install'
-const PLEROMA_ADMIN_USERS_URL = ({
+const PLEROMA_ADMIN_USERS_URL = '/api/v1/pleroma/admin/users'
+const PLEROMA_ADMIN_USERS_URL_SHOW = (nickname) =>
+ `/api/v1/pleroma/admin/users/${nickname}`
+const PLEROMA_ADMIN_USERS_URL_LIST = ({
page,
pageSize,
filters = {},
@@ -177,13 +174,18 @@ const PLEROMA_ADMIN_USERS_URL = ({
.join(',')
return `/api/v1/pleroma/admin/users?page=${page}&page_size=${pageSize}&filters=${filters_str}&query=${query}&name=${name}&email=${email}`
}
-const PLEROMA_ADMIN_MODIFY_GROUP_URL = (nickname, group) =>
- `/api/v1/pleroma/admin/users/${nickname}/permission_group/${group}`
-const PLEROMA_ADMIN_CONFIRM_USER_URL =
+const PLEROMA_ADMIN_TAG_USER_URL = '/api/pleroma/admin/users/tag'
+const PLEROMA_ADMIN_PERMISSION_GROUP_URL = (right) =>
+ `/api/pleroma/admin/users/permission_group/${right}`
+const PLEROMA_ADMIN_ACTIVATE_USERS_URL = '/api/pleroma/admin/users/activate'
+const PLEROMA_ADMIN_DEACTIVATE_USERS_URL = '/api/pleroma/admin/users/deactivate'
+const PLEROMA_ADMIN_SUGGEST_USERS_URL = '/api/pleroma/admin/users/suggest'
+const PLEROMA_ADMIN_UNSUGGEST_USERS_URL = '/api/pleroma/admin/users/unsuggest'
+const PLEROMA_ADMIN_APPROVE_USERS_URL = '/api/v1/pleroma/admin/users/approve'
+const PLEROMA_ADMIN_CONFIRM_USERS_URL =
'/api/v1/pleroma/admin/users/confirm_email'
const PLEROMA_ADMIN_RESEND_CONFIRMATION_EMAIL_URL =
'/api/v1/pleroma/admin/users/resend_confirmation_email'
-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) =>
@@ -251,8 +253,11 @@ const promisedRequest = ({
}
}
return fetch(url, options).then((response) => {
- return new Promise((resolve, reject) =>
- response
+ return new Promise((resolve, reject) => {
+ // 204 is "No content", which fails to parse json (as you'd might think)
+ if (response.ok && response.status === 204) resolve()
+
+ return response
.json()
.then((json) => {
if (!response.ok) {
@@ -276,8 +281,8 @@ const promisedRequest = ({
response,
),
)
- }),
- )
+ })
+ })
})
}
@@ -769,92 +774,118 @@ const fetchStatusHistory = ({ status, credentials }) => {
})
}
-const tagUser = ({ tag, credentials, user }) => {
- const screenName = user.screen_name
- const form = {
- nicknames: [screenName],
- tags: [tag],
- }
-
- const headers = authHeaders(credentials)
- headers['Content-Type'] = 'application/json'
-
- return fetch(TAG_USER_URL, {
- method: 'PUT',
- headers,
- body: JSON.stringify(form),
- })
-}
-
-const untagUser = ({ tag, credentials, user }) => {
- const screenName = user.screen_name
- const body = {
- nicknames: [screenName],
- tags: [tag],
- }
-
- const headers = authHeaders(credentials)
- headers['Content-Type'] = 'application/json'
-
- return fetch(TAG_USER_URL, {
- method: 'DELETE',
- headers,
- body: JSON.stringify(body),
- })
-}
-
-const addRight = ({ right, credentials, user }) => {
- const screenName = user.screen_name
-
- return fetch(PERMISSION_GROUP_URL(screenName, right), {
- method: 'POST',
- headers: authHeaders(credentials),
- body: {},
- })
-}
-
-const deleteRight = ({ right, credentials, user }) => {
- const screenName = user.screen_name
-
- return fetch(PERMISSION_GROUP_URL(screenName, right), {
- method: 'DELETE',
- headers: authHeaders(credentials),
- body: {},
- })
-}
-
-const activateUser = ({ credentials, user: { screen_name: nickname } }) => {
+const adminSetUsersTags = ({
+ tags,
+ credentials,
+ value,
+ screen_names: nicknames,
+}) => {
return promisedRequest({
- url: ACTIVATE_USER_URL,
+ url: PLEROMA_ADMIN_TAG_USER_URL,
+ method: value ? 'PUT' : 'DELETE',
+ credentials,
+ payload: {
+ nicknames,
+ tags,
+ },
+ })
+}
+
+const adminSetUsersRight = ({
+ right,
+ credentials,
+ value,
+ screen_names: nicknames,
+}) => {
+ return promisedRequest({
+ url: PLEROMA_ADMIN_PERMISSION_GROUP_URL(right),
+ method: value ? 'POST' : 'DELETE',
+ credentials,
+ payload: {
+ nicknames,
+ },
+ })
+}
+
+const adminSetUsersActivationStatus = ({
+ credentials,
+ screen_names: nicknames,
+ value,
+}) => {
+ return promisedRequest({
+ url: value
+ ? PLEROMA_ADMIN_ACTIVATE_USERS_URL
+ : PLEROMA_ADMIN_DEACTIVATE_USERS_URL,
method: 'PATCH',
credentials,
payload: {
- nicknames: [nickname],
+ nicknames,
},
- }).then((response) => response.users[0])
+ }).then((response) => response.users)
}
-const deactivateUser = ({ credentials, user: { screen_name: nickname } }) => {
+const adminSetUsersApprovalStatus = ({
+ credentials,
+ screen_names: nicknames,
+}) => {
return promisedRequest({
- url: DEACTIVATE_USER_URL,
+ url: PLEROMA_ADMIN_APPROVE_USERS_URL,
method: 'PATCH',
credentials,
payload: {
- nicknames: [nickname],
+ nicknames,
},
- }).then((response) => response.users[0])
+ }).then((response) => response.users)
}
-const deleteUser = ({ credentials, user: { screen_name: nickname } }) => {
- const r = promisedRequest({
- url: ADMIN_USERS_URL,
+const adminSetUsersConfirmationStatus = ({
+ credentials,
+ screen_names: nicknames,
+}) => {
+ return promisedRequest({
+ url: PLEROMA_ADMIN_CONFIRM_USERS_URL,
+ method: 'PATCH',
+ credentials,
+ payload: {
+ nicknames,
+ },
+ }).then((response) => response.users)
+}
+
+const adminSetUsersSuggestionStatus = ({
+ credentials,
+ screen_names: nicknames,
+ value,
+}) => {
+ return promisedRequest({
+ url: value
+ ? PLEROMA_ADMIN_SUGGEST_USERS_URL
+ : PLEROMA_ADMIN_UNSUGGEST_USERS_URL,
+ method: 'PATCH',
+ credentials,
+ payload: {
+ nicknames,
+ },
+ }).then((response) => response.users)
+}
+
+const adminGetUserData = ({ credentials, screen_name: nickname }) => {
+ return promisedRequest({
+ url: PLEROMA_ADMIN_USERS_URL_SHOW(nickname),
+ method: 'GET',
+ credentials,
+ })
+}
+
+const adminDeleteAccounts = ({ credentials, screen_names: nicknames }) => {
+ return promisedRequest({
+ url: PLEROMA_ADMIN_USERS_URL,
method: 'DELETE',
credentials,
payload: {
- nicknames: [nickname],
+ nicknames,
},
})
- return r.then((response) => response.users[0])
}
const fetchTimeline = ({
@@ -1668,75 +1699,57 @@ 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)
+ const url = PLEROMA_ADMIN_USERS_URL_LIST(opts)
return promisedRequest({
url: url,
credentials,
method: 'GET',
}).then((data) => ({
...data,
- users: data.users.map(parseUser),
+ users: data.users,
}))
}
-const adminAddUserToAdminGroup = ({ user, credentials }) => {
- 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 adminAddUserToModeratorGroup = ({ user, credentials }) => {
- 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 adminConfirmUser = ({ user: { screen_name: nickname }, credentials }) => {
- const url = PLEROMA_ADMIN_CONFIRM_USER_URL
- return promisedRequest({
- url: url,
- credentials,
- method: 'PATCH',
- payload: {
- nicknames: [nickname],
- },
- })
-}
-
const adminResendConfirmationEmail = ({
- user: { screen_name: nickname },
+ screen_names: nicknames,
credentials,
}) => {
const url = PLEROMA_ADMIN_RESEND_CONFIRMATION_EMAIL_URL
return promisedRequest({
- url: url,
+ url,
credentials,
method: 'PATCH',
payload: {
- nicknames: [nickname],
+ nicknames,
},
})
}
-const adminApproveUser = ({ user: { screen_name: nickname }, credentials }) => {
- const url = PLEROMA_ADMIN_APPROVE_URL
- const r = promisedRequest({
- url: url,
+const adminRequirePasswordChange = ({
+ user: { screen_names: nicknames },
+ credentials,
+}) => {
+ const url = PLEROMA_ADMIN_REQUIRE_PASSWORD_CHANGE_URL
+ return promisedRequest({
+ url,
credentials,
method: 'PATCH',
payload: {
- nicknames: [nickname],
+ nicknames,
+ },
+ })
+}
+
+const adminDisableMFA = ({ user: { screen_name: nickname }, credentials }) => {
+ const url = PLEROMA_ADMIN_DISABLE_MFA_URL
+ return promisedRequest({
+ url,
+ credentials,
+ method: 'PUT',
+ payload: {
+ nickname,
},
})
- return r
}
const adminListStatuses = ({
@@ -1775,33 +1788,6 @@ const adminChangeStatusScope = ({
})
}
-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 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 }
@@ -2438,13 +2424,6 @@ const apiService = {
fetchBlocks,
fetchOAuthTokens,
revokeOAuthToken,
- tagUser,
- untagUser,
- deleteUser,
- addRight,
- deleteRight,
- activateUser,
- deactivateUser,
register,
getCaptcha,
updateProfileImages,
@@ -2533,13 +2512,15 @@ const apiService = {
updateBookmarkFolder,
deleteBookmarkFolder,
adminListUsers,
- adminAddUserToAdminGroup,
- adminRemoveUserFromAdminGroup,
- adminAddUserToModeratorGroup,
- adminRemoveUserFromModeratorGroup,
- adminConfirmUser,
+ adminGetUserData,
adminResendConfirmationEmail,
- adminApproveUser,
+ adminDeleteAccounts,
+ adminSetUsersRight,
+ adminSetUsersTags,
+ adminSetUsersApprovalStatus,
+ adminSetUsersConfirmationStatus,
+ adminSetUsersActivationStatus,
+ adminSetUsersSuggestionStatus,
adminListStatuses,
adminChangeStatusScope,
adminRequirePasswordChange,
diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index f1a4d021e..79cd35f9e 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -17,12 +17,13 @@ import { isStatusNotification } from '../notification_utils/notification_utils.j
export const parseUser = (data) => {
const output = {}
+ output._original = data // used for server-side settings
+
// case for users in "mentions" property for statuses in MastoAPI
const mastoShort = !Object.hasOwn(data, 'avatar')
output.inLists = null
output.id = String(data.id)
- output._original = data // used for server-side settings
output.screen_name = data.acct
output.fqn = data.fqn
@@ -118,9 +119,9 @@ export const parseUser = (data) => {
output.birthday = data.pleroma.birthday
if (data.pleroma.privileges) {
- output.privileges = data.pleroma.privileges
+ output.privileges = new Set(data.pleroma.privileges)
} else if (data.pleroma.is_admin) {
- output.privileges = [
+ output.privileges = new Set([
'users_read',
'users_manage_invites',
'users_manage_activation_state',
@@ -135,11 +136,11 @@ export const parseUser = (data) => {
'announcements_manage_announcements',
'emoji_manage_emoji',
'statistics_read',
- ]
+ ])
} else if (data.pleroma.is_moderator) {
- output.privileges = ['messages_delete', 'reports_manage_reports']
+ output.privileges = new Set(['messages_delete', 'reports_manage_reports'])
} else {
- output.privileges = []
+ output.privileges = new Set()
}
}
@@ -149,7 +150,7 @@ export const parseUser = (data) => {
output.fields = data.source.fields
if (data.source.pleroma) {
output.no_rich_text = data.source.pleroma.no_rich_text
- output.show_role = data.source.pleroma.show_role
+ output.show_role = typeof data.source.pleroma.show_role === 'boolean' ? data.source.pleroma.show_role : true
output.discoverable = data.source.pleroma.discoverable
output.show_birthday = data.pleroma.show_birthday
output.actor_type = data.source.pleroma.actor_type
@@ -168,7 +169,7 @@ export const parseUser = (data) => {
if (data.pleroma) {
output.follow_request_count = data.pleroma.follow_request_count
- output.tags = data.pleroma.tags
+ output.tags = new Set(data.pleroma.tags)
// deactivated was changed to is_active in Pleroma 2.3.0
// so check if is_active is present
@@ -181,7 +182,7 @@ export const parseUser = (data) => {
output.unread_chat_count = data.pleroma.unread_chat_count
}
- output.tags = output.tags || []
+ output.tags = output.tags || new Set()
output.rights = output.rights || {}
output.notification_settings = output.notification_settings || {}
diff --git a/src/stores/admin_settings.js b/src/stores/admin_settings.js
index c2855d29c..d6bc4f9d3 100644
--- a/src/stores/admin_settings.js
+++ b/src/stores/admin_settings.js
@@ -19,23 +19,14 @@ export const newUserFlags = {
export const useAdminSettingsStore = defineStore('adminSettings', {
state: () => ({
...cloneDeep(defaultState),
+ backendInteractor: window.vuex.state.api.backendInteractor,
}),
actions: {
+ // Configuration Stuff
setInstanceAdminNoDbConfig() {
this.loaded = false
this.dbConfigEnabled = false
},
- setAvailableFrontends({ frontends }) {
- this.frontends = frontends.map((f) => {
- f.installedRefs = f.installed_refs
- if (f.name === 'pleroma-fe') {
- f.refs = ['master', 'develop']
- } else {
- f.refs = [f.ref]
- }
- return f
- })
- },
updateAdminSettings({ config, modifiedPaths }) {
this.loaded = true
this.dbConfigEnabled = true
@@ -59,144 +50,23 @@ export const useAdminSettingsStore = defineStore('adminSettings', {
resetAdminDraft() {
this.draft = cloneDeep(this.config)
},
- async fetchAdminUsers(opts) {
- const data = await window.vuex.state.api.backendInteractor.adminListUsers(
- {
- opts,
- },
- )
- data.users.forEach((user) =>
- window.vuex.dispatch('fetchUserIfMissing', user.id),
- )
-
- return data
- },
- adminAddUserToAdminGroup(user) {
- window.vuex.state.api.backendInteractor
- .adminAddUserToAdminGroup({ user })
- .then((res) =>
- window.vuex.commit('updateRight', {
- user,
- right: 'admin',
- value: res.is_admin,
- }),
- )
- },
- adminRemoveUserFromAdminGroup(user) {
- // prevent revokation of own rights
- if (user.id !== window.vuex.state.users.currentUser.id) {
- return window.vuex.state.api.backendInteractor
- .adminRemoveUserFromAdminGroup({ user })
- .then((res) =>
- window.vuex.commit('updateRight', {
- user,
- right: 'admin',
- value: res.is_admin,
- }),
- )
- }
- },
- adminAddUserToModeratorGroup(user) {
- return window.vuex.state.api.backendInteractor
- .adminAddUserToModeratorGroup({ user })
- .then((res) =>
- window.vuex.commit('updateRight', {
- user,
- right: 'moderator',
- value: res.is_moderator,
- }),
- )
- },
- adminRemoveUserFromModeratorGroup(user) {
- // prevent revokation of own rights
- if (user.id !== window.vuex.state.users.currentUser.id) {
- return window.vuex.state.api.backendInteractor
- .adminRemoveUserFromModeratorGroup({ user })
- .then((res) =>
- window.vuex.commit('updateRight', {
- user,
- right: 'moderator',
- value: res.is_moderator,
- }),
- )
- }
- },
- adminActivateUser(user) {
- return window.vuex.state.api.backendInteractor
- .activateUser({ user })
- .then((res) => {
- const deactivated = !res.is_active
- window.vuex.commit('updateActivationStatus', { user, deactivated })
- })
- },
- adminDeactivateUser(user) {
- return window.vuex.state.api.backendInteractor
- .deactivateUser({ user })
- .then((res) => {
- const deactivated = !res.is_active
- window.vuex.commit('updateActivationStatus', { user, deactivated })
- })
- },
- adminDeleteUser(user) {
- return window.vuex.state.api.backendInteractor.deleteUser({ user })
- },
- adminConfirmUser(user) {
- return window.vuex.state.api.backendInteractor
- .adminConfirmUser({ user })
- .then(() => window.vuex.dispatch('fetchUser', user.id))
- },
- adminResendConfirmationEmail(user) {
- return window.vuex.state.api.backendInteractor.adminResendConfirmationEmail(
- { user },
- )
- },
- adminApproveUser(user) {
- return window.vuex.state.api.backendInteractor.adminApproveUser({ user })
- },
- adminListStatuses({ userId, opts }) {
- return window.vuex.state.api.backendInteractor.adminListStatuses({
- userId,
- opts,
- })
- },
- adminChangeStatusScope({ opts }) {
- return window.vuex.state.api.backendInteractor.adminChangeStatusScope({
- opts,
- })
- },
- adminDisableMFA(user) {
- return window.vuex.state.api.backendInteractor.adminDisableMFA({ user })
- },
- adminTagUser({ user, tag }) {
- return window.vuex.state.api.backendInteractor.tagUser({ user, tag })
- },
- adminUntagUser({ user, tag }) {
- return window.vuex.state.api.backendInteractor.untagUser({ user, tag })
- },
- loadFrontendsStuff() {
- window.vuex.state.api.backendInteractor
- .fetchAvailableFrontends()
- .then((frontends) => this.setAvailableFrontends({ frontends }))
- },
loadAdminStuff() {
- window.vuex.state.api.backendInteractor
- .fetchInstanceDBConfig()
- .then((backendDbConfig) => {
- if (backendDbConfig.error) {
- if (backendDbConfig.error.status === 400) {
- backendDbConfig.error.json().then((errorJson) => {
- if (/configurable_from_database/.test(errorJson.error)) {
- this.setInstanceAdminNoDbConfig()
- }
- })
- }
- } else {
- this.setInstanceAdminSettings({ backendDbConfig })
+ this.backendInteractor.fetchInstanceDBConfig().then((backendDbConfig) => {
+ if (backendDbConfig.error) {
+ if (backendDbConfig.error.status === 400) {
+ backendDbConfig.error.json().then((errorJson) => {
+ if (/configurable_from_database/.test(errorJson.error)) {
+ this.setInstanceAdminNoDbConfig()
+ }
+ })
}
- })
+ } else {
+ this.setInstanceAdminSettings({ backendDbConfig })
+ }
+ })
if (this.descriptions === null) {
- window.vuex.state.api.backendInteractor
+ this.backendInteractor
.fetchInstanceConfigDescriptions()
.then((backendDescriptions) =>
this.setInstanceAdminDescriptions({ backendDescriptions }),
@@ -408,5 +278,172 @@ export const useAdminSettingsStore = defineStore('adminSettings', {
this.setInstanceAdminSettings({ backendDbConfig }),
)
},
+
+ // Frontends Stuff
+ loadFrontendsStuff() {
+ this.backendInteractor
+ .fetchAvailableFrontends()
+ .then((frontends) => this.setAvailableFrontends({ frontends }))
+ },
+
+ setAvailableFrontends({ frontends }) {
+ this.frontends = frontends.map((f) => {
+ f.installedRefs = f.installed_refs
+ if (f.name === 'pleroma-fe') {
+ f.refs = ['master', 'develop']
+ } else {
+ f.refs = [f.ref]
+ }
+ return f
+ })
+ },
+
+ // Statuses stuff
+ listStatuses({ userId, opts }) {
+ return this.backendInteractor.adminListStatuses({
+ userId,
+ opts,
+ })
+ },
+ changeStatusScope({ opts }) {
+ return this.backendInteractor.adminChangeStatusScope({
+ opts,
+ })
+ },
+
+ // Users stuff
+ async fetchAdminUsers(opts) {
+ const adminData = await this.backendInteractor.adminListUsers({
+ opts,
+ })
+
+ adminData.users = await Promise.all(
+ adminData.users.map(
+ async (userAdminData) =>
+ await window.vuex.dispatch('updateUserAdminData', {
+ userAdminData,
+ }),
+ ),
+ )
+
+ return adminData
+ },
+ async getUserData({ user }) {
+ const api = this.backendInteractor.adminGetUserData
+ const { screen_name } = user
+
+ const result = await api({ screen_name })
+ window.vuex.commit('updateUserAdminData', { user: result })
+ },
+ async deleteUsers({ users }) {
+ const screen_names = users.map((u) => u.screen_name)
+ const api = this.backendInteractor.adminDeleteAccounts
+
+ const resultUserIds = await api({ screen_names })
+
+ resultUserIds.forEach((userId) => {
+ window.vuex.dispatch(
+ 'markStatusesAsDeleted',
+ (status) => userId === status.user.id,
+ )
+ // TODO when migrated to pinia, also remove user
+ })
+
+ return resultUserIds
+ },
+ resendConfirmationEmail({ users }) {
+ const screen_names = users.map((u) => u.screen_name)
+
+ return this.backendInteractor.adminResendConfirmationEmail({
+ screen_names,
+ })
+ },
+ requirePasswordChange({ users }) {
+ const screen_names = users.map((u) => u.screen_name)
+
+ return this.backendInteractor.adminRequirePasswordChange({
+ screen_names,
+ })
+ },
+ // Singular only!
+ disableMFA({ user }) {
+ return this.backendInteractor.adminDisableMFA(user)
+ },
+ async setUsersTags({ users, tags, value }) {
+ const screen_names = users.map((u) => u.screen_name)
+ const api = this.backendInteractor.adminSetUsersTags
+
+ await api({
+ screen_names,
+ tags,
+ value,
+ })
+
+ users.forEach((user) => {
+ this.getUserData({ user })
+ })
+ },
+ async setUsersRight({ users, right, value }) {
+ const screen_names = users.map((u) => u.screen_name)
+ const api = this.backendInteractor.adminSetUsersRight
+
+ await api({
+ screen_names,
+ right,
+ value,
+ })
+
+ users.forEach((user) => {
+ window.vuex.commit('updateRight', { user, right, value })
+ })
+ },
+ async setUsersActivationStatus({ users, value }) {
+ const screen_names = users.map((u) => u.screen_name)
+ const api = this.backendInteractor.adminSetUsersActivationStatus
+
+ const resultUsers = await api({
+ screen_names,
+ value,
+ })
+
+ resultUsers.forEach((user) => {
+ window.vuex.commit('updateUserAdminData', { user })
+ })
+ },
+ async setUsersSuggestionStatus({ users, value }) {
+ const screen_names = users.map((u) => u.screen_name)
+ const api = this.backendInteractor.adminSetUsersSuggestionStatus
+
+ const resultUsers = await api({
+ screen_names,
+ value,
+ })
+
+ resultUsers.forEach((user) => {
+ window.vuex.commit('updateUserAdminData', { user })
+ })
+ },
+ async setUsersConfirmationStatus({ users }) {
+ const screen_names = users.map((u) => u.screen_name)
+ const api = this.backendInteractor.adminSetUsersConfirmationStatus
+
+ await api({ screen_names })
+
+ users.forEach((user) => {
+ this.getUserData({ user })
+ })
+ },
+ async setUsersApprovalStatus({ users }) {
+ const screen_names = users.map((u) => u.screen_name)
+ const api = this.backendInteractor.adminSetUsersApprovalStatus
+
+ const resultUsers = await api({
+ screen_names,
+ })
+
+ resultUsers.forEach((user) => {
+ window.vuex.commit('updateUserAdminData', { user })
+ })
+ },
},
})
diff --git a/src/stores/announcements.js b/src/stores/announcements.js
index bf88666c6..cb325dadd 100644
--- a/src/stores/announcements.js
+++ b/src/stores/announcements.js
@@ -29,7 +29,7 @@ export const useAnnouncementsStore = defineStore('announcements', {
const currentUser = window.vuex.state.users.currentUser
const isAdmin =
currentUser &&
- currentUser.privileges.includes('announcements_manage_announcements')
+ currentUser.privileges.has('announcements_manage_announcements')
const getAnnouncements = async () => {
if (!isAdmin) {