From ce43c81ce81518dc2b91860c4a534f90163aec33 Mon Sep 17 00:00:00 2001
From: luce
Date: Tue, 15 Jul 2025 14:30:46 +0200
Subject: [PATCH 001/337] list users, fetch updates, make them clickable
---
.../selectable_list/selectable_list.js | 4 +
.../selectable_list/selectable_list.vue | 21 ++++
.../settings_modal/admin_tabs/admin_card.js | 25 +++++
.../settings_modal/admin_tabs/admin_card.vue | 45 ++++++++
.../settings_modal/admin_tabs/users_tab.js | 100 ++++++++++++++++++
.../settings_modal/admin_tabs/users_tab.vue | 14 +++
.../settings_modal_admin_content.js | 2 +
.../settings_modal_admin_content.vue | 8 ++
src/i18n/en.json | 13 +++
src/modules/adminSettings.js | 3 +
src/services/api/api.service.js | 10 +-
11 files changed, 244 insertions(+), 1 deletion(-)
create mode 100644 src/components/settings_modal/admin_tabs/admin_card.js
create mode 100644 src/components/settings_modal/admin_tabs/admin_card.vue
create mode 100644 src/components/settings_modal/admin_tabs/users_tab.js
create mode 100644 src/components/settings_modal/admin_tabs/users_tab.vue
diff --git a/src/components/selectable_list/selectable_list.js b/src/components/selectable_list/selectable_list.js
index 10980d46a..b097e8927 100644
--- a/src/components/selectable_list/selectable_list.js
+++ b/src/components/selectable_list/selectable_list.js
@@ -7,6 +7,10 @@ const SelectableList = {
Checkbox
},
props: {
+ boxOnly: {
+ type: Boolean,
+ default: false
+ },
items: {
type: Array,
default: () => []
diff --git a/src/components/selectable_list/selectable_list.vue b/src/components/selectable_list/selectable_list.vue
index 3d3a5ff04..eeea348ee 100644
--- a/src/components/selectable_list/selectable_list.vue
+++ b/src/components/selectable_list/selectable_list.vue
@@ -27,6 +27,7 @@
>
+
+
+ toggle(checked, item)"
+ @click.stop
+ />
+
+
+
diff --git a/src/components/settings_modal/admin_tabs/admin_card.js b/src/components/settings_modal/admin_tabs/admin_card.js
new file mode 100644
index 000000000..7339e420e
--- /dev/null
+++ b/src/components/settings_modal/admin_tabs/admin_card.js
@@ -0,0 +1,25 @@
+import BasicUserCard from '../../basic_user_card/basic_user_card.vue'
+
+const AdminCard = {
+ props: ['userId'],
+ data () {
+ return {
+ progress: false
+ }
+ },
+ computed: {
+ user () {
+ return this.$store.getters.findUser(this.userId)
+ },
+ relationship () {
+ return this.$store.getters.relationship(this.userId)
+ },
+ },
+ components: {
+ BasicUserCard
+ },
+ methods: {
+ }
+}
+
+export default AdminCard
diff --git a/src/components/settings_modal/admin_tabs/admin_card.vue b/src/components/settings_modal/admin_tabs/admin_card.vue
new file mode 100644
index 000000000..cc7b7b4f5
--- /dev/null
+++ b/src/components/settings_modal/admin_tabs/admin_card.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/settings_modal/admin_tabs/users_tab.js b/src/components/settings_modal/admin_tabs/users_tab.js
new file mode 100644
index 000000000..4b875cc93
--- /dev/null
+++ b/src/components/settings_modal/admin_tabs/users_tab.js
@@ -0,0 +1,100 @@
+//import get from 'lodash/get'
+import Checkbox from 'src/components/checkbox/checkbox.vue'
+import Select from 'src/components/select/select.vue'
+import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
+import withLoadMore from 'src/components/../hocs/with_load_more/with_load_more'
+import SelectableList from 'src/components/selectable_list/selectable_list.vue'
+import ProgressButton from 'src/components/progress_button/progress_button.vue'
+import AdminCard from 'src/components/settings_modal/admin_tabs/admin_card.vue'
+//import { ref } from 'vue'
+
+const UserList = withLoadMore({
+ fetch: (props, $store) => {
+ console.log('fetch', props)
+ return $store.dispatch('fetchAdminUsers')
+ },
+ select: (props, $store) => {
+ console.log('select', props)
+ const filterMethod = typeof props.filterMethod === 'function' ? props.filterMethod : () => true
+ const users = $store.state.users.users.filter(user => user.id !== $store.state.users.currentUser.id && filterMethod(user))
+ console.log('users', users)
+ return users
+ },
+ destroy: () => {},
+ childPropName: 'items'
+})(SelectableList)
+
+const UserSortStrategy = Object.freeze({
+ NONE: 0,
+ NICKNAME: 1,
+ DISPLAYNAME: 2,
+ JOINED: 3
+})
+
+const UsersTab = {
+ provide () {
+ return {
+ defaultDraftMode: true,
+ defaultSource: 'admin'
+ }
+ },
+ data() {
+ return {
+ filterTerms: [],
+ users: [],
+ sortStrategy: UserSortStrategy.NONE,
+ sortAscending: true,
+ expandedUser: null,
+ filterActive: 'active_only',
+ filterLocal: 'local_only',
+ loading: false
+ }
+ },
+ components: {
+ Checkbox,
+ Select,
+ BasicUserCard,
+ UserList,
+ ProgressButton,
+ AdminCard,
+ },
+ computed: {
+ knownDomains () {
+ return this.$store.state.instance.knownDomains
+ },
+ user () {
+ return this.$store.state.users.currentUser
+ }
+ },
+ methods: {
+ },
+ mounted() {
+ console.log("mounted")
+ /*const store = this.$store;
+ const moduleTree = buildModuleTree(store._modules.root);
+ console.log(JSON.stringify(moduleTree, null, 2));*/
+ }
+}
+/*function buildModuleTree(module, path = []) {
+ const fullPath = path.join('/') || 'root';
+
+ const moduleInfo = {
+ path: fullPath,
+ namespaced: module.namespaced,
+ state: Object.keys(module.state),
+ actions: module._rawModule.actions ? Object.keys(module._rawModule.actions) : [],
+ mutations: module._rawModule.mutations ? Object.keys(module._rawModule.mutations) : [],
+ getters: module._rawModule.getters ? Object.keys(module._rawModule.getters) : [],
+ modules: []
+ };
+
+ if (module._children) {
+ for (const key in module._children) {
+ const child = module._children[key];
+ moduleInfo.modules.push(buildModuleTree(child, path.concat(key)));
+ }
+ }
+
+ return moduleInfo;
+}*/
+export default UsersTab
diff --git a/src/components/settings_modal/admin_tabs/users_tab.vue b/src/components/settings_modal/admin_tabs/users_tab.vue
new file mode 100644
index 000000000..0f9d681bf
--- /dev/null
+++ b/src/components/settings_modal/admin_tabs/users_tab.vue
@@ -0,0 +1,14 @@
+
+
+
+
diff --git a/src/components/settings_modal/settings_modal_admin_content.js b/src/components/settings_modal/settings_modal_admin_content.js
index 593318ec4..0102bbc8f 100644
--- a/src/components/settings_modal/settings_modal_admin_content.js
+++ b/src/components/settings_modal/settings_modal_admin_content.js
@@ -1,6 +1,7 @@
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
import InstanceTab from './admin_tabs/instance_tab.vue'
+import UsersTab from './admin_tabs/users_tab.vue'
import LimitsTab from './admin_tabs/limits_tab.vue'
import FrontendsTab from './admin_tabs/frontends_tab.vue'
import EmojiTab from './admin_tabs/emoji_tab.vue'
@@ -34,6 +35,7 @@ const SettingsModalAdminContent = {
TabSwitcher,
InstanceTab,
+ UsersTab,
LimitsTab,
FrontendsTab,
EmojiTab
diff --git a/src/components/settings_modal/settings_modal_admin_content.vue b/src/components/settings_modal/settings_modal_admin_content.vue
index 39ef74f64..c56f11548 100644
--- a/src/components/settings_modal/settings_modal_admin_content.vue
+++ b/src/components/settings_modal/settings_modal_admin_content.vue
@@ -48,6 +48,14 @@
>
+
+
+
users.forEach(user => store.dispatch('fetchUserIfMissing', user.id)))
+ },
loadFrontendsStuff ({ rootState, commit }) {
rootState.api.backendInteractor.fetchAvailableFrontends()
.then(frontends => commit('setAvailableFrontends', { frontends }))
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index 117c621d9..f88c3a6e5 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -115,6 +115,7 @@ const PLEROMA_ADMIN_CONFIG_URL = '/api/pleroma/admin/config'
const PLEROMA_ADMIN_DESCRIPTIONS_URL = '/api/pleroma/admin/config/descriptions'
const PLEROMA_ADMIN_FRONTENDS_URL = '/api/pleroma/admin/frontends'
const PLEROMA_ADMIN_FRONTENDS_INSTALL_URL = '/api/pleroma/admin/frontends/install'
+const PLEROMA_ADMIN_USERS_URL = '/api/v1/pleroma/admin/users'
const PLEROMA_EMOJI_RELOAD_URL = '/api/pleroma/admin/reload_emoji'
const PLEROMA_EMOJI_IMPORT_FS_URL = '/api/pleroma/emoji/packs/import'
@@ -1440,6 +1441,12 @@ const dismissAnnouncement = ({ id, credentials }) => {
})
}
+const adminListUsers = ({ credentials }) => {
+ // the reported list is hardly useful because standards are for dating i guess,
+ // so make sure to fetchIfMissing right afterward using this call
+ return promisedRequest({ url: PLEROMA_ADMIN_USERS_URL, credentials }).then((data) => data.users.map(parseUser))
+}
+
const announcementToPayload = ({ content, startsAt, endsAt, allDay }) => {
const payload = { content }
@@ -2111,7 +2118,8 @@ const apiService = {
fetchBookmarkFolders,
createBookmarkFolder,
updateBookmarkFolder,
- deleteBookmarkFolder
+ deleteBookmarkFolder,
+ adminListUsers,
}
export default apiService
From 4a00d8949422f7e7dbd8f5ccb680e3b38a9804e7 Mon Sep 17 00:00:00 2001
From: luce
Date: Sat, 19 Jul 2025 14:11:12 +0200
Subject: [PATCH 002/337] page list instead of hoc withloadmore
---
src/components/page_list/page_list.js | 50 ++++++++++
src/components/page_list/page_list.vue | 58 +++++++++++
.../settings_modal/admin_tabs/admin_card.js | 3 +
.../settings_modal/admin_tabs/admin_card.vue | 9 ++
.../settings_modal/admin_tabs/users_tab.js | 95 +++++++-----------
.../settings_modal/admin_tabs/users_tab.vue | 97 ++++++++++++++++++-
src/i18n/en.json | 8 +-
src/modules/adminSettings.js | 8 +-
src/services/api/api.service.js | 71 +++++++++++++-
9 files changed, 327 insertions(+), 72 deletions(-)
create mode 100644 src/components/page_list/page_list.js
create mode 100644 src/components/page_list/page_list.vue
diff --git a/src/components/page_list/page_list.js b/src/components/page_list/page_list.js
new file mode 100644
index 000000000..fb99dc2f2
--- /dev/null
+++ b/src/components/page_list/page_list.js
@@ -0,0 +1,50 @@
+//import Checkbox from 'src/components/checkbox/checkbox.vue'
+import SelectableList from 'src/components/selectable_list/selectable_list.vue'
+
+const PageList = {
+ components: {
+ SelectableList
+ },
+ props: {
+ boxOnly: {
+ type: Boolean,
+ default: false
+ },
+ pageSize: {
+ type: Number,
+ default: 50
+ },
+ fetchPage: {
+ type: Function,
+ default: async () => []
+ }
+ },
+ data () {
+ return {
+ pageIndex: 1,
+ items: [],
+ selected: [],
+ canLoadMore: true
+ }
+ },
+ methods: {
+ reset () {
+ this.canLoadMore = true
+ this.pageIndex = 1
+ this.items = []
+ this.loadMore() // load one page
+ },
+ loadMore () {
+ this.fetchPage(this.$store, {
+ page: this.pageIndex++,
+ pageSize: this.pageSize
+ }).then((items) => this.items = [...this.items, ...items])
+ // fetch page, add to items
+ //this.$forceUpdate()
+ }
+ },
+ mounted () {
+ this.reset()
+ }
+}
+export default PageList
diff --git a/src/components/page_list/page_list.vue b/src/components/page_list/page_list.vue
new file mode 100644
index 000000000..3de52579c
--- /dev/null
+++ b/src/components/page_list/page_list.vue
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
prev first next
+
+
+
+
+
+
diff --git a/src/components/settings_modal/admin_tabs/admin_card.js b/src/components/settings_modal/admin_tabs/admin_card.js
index 7339e420e..c9bf8147e 100644
--- a/src/components/settings_modal/admin_tabs/admin_card.js
+++ b/src/components/settings_modal/admin_tabs/admin_card.js
@@ -8,6 +8,9 @@ const AdminCard = {
}
},
computed: {
+ isLoaded () {
+ return typeof(this.user) !== 'undefined'
+ },
user () {
return this.$store.getters.findUser(this.userId)
},
diff --git a/src/components/settings_modal/admin_tabs/admin_card.vue b/src/components/settings_modal/admin_tabs/admin_card.vue
index cc7b7b4f5..c54cdcf1f 100644
--- a/src/components/settings_modal/admin_tabs/admin_card.vue
+++ b/src/components/settings_modal/admin_tabs/admin_card.vue
@@ -1,4 +1,12 @@
+
+ loading user...
+
+
diff --git a/src/components/settings_modal/admin_tabs/users_tab.js b/src/components/settings_modal/admin_tabs/users_tab.js
index 4b875cc93..03274533d 100644
--- a/src/components/settings_modal/admin_tabs/users_tab.js
+++ b/src/components/settings_modal/admin_tabs/users_tab.js
@@ -2,34 +2,11 @@
import Checkbox from 'src/components/checkbox/checkbox.vue'
import Select from 'src/components/select/select.vue'
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
-import withLoadMore from 'src/components/../hocs/with_load_more/with_load_more'
-import SelectableList from 'src/components/selectable_list/selectable_list.vue'
import ProgressButton from 'src/components/progress_button/progress_button.vue'
import AdminCard from 'src/components/settings_modal/admin_tabs/admin_card.vue'
-//import { ref } from 'vue'
+import PageList from 'src/components/page_list/page_list.vue'
-const UserList = withLoadMore({
- fetch: (props, $store) => {
- console.log('fetch', props)
- return $store.dispatch('fetchAdminUsers')
- },
- select: (props, $store) => {
- console.log('select', props)
- const filterMethod = typeof props.filterMethod === 'function' ? props.filterMethod : () => true
- const users = $store.state.users.users.filter(user => user.id !== $store.state.users.currentUser.id && filterMethod(user))
- console.log('users', users)
- return users
- },
- destroy: () => {},
- childPropName: 'items'
-})(SelectableList)
-const UserSortStrategy = Object.freeze({
- NONE: 0,
- NICKNAME: 1,
- DISPLAYNAME: 2,
- JOINED: 3
-})
const UsersTab = {
provide () {
@@ -40,13 +17,17 @@ const UsersTab = {
},
data() {
return {
- filterTerms: [],
- users: [],
- sortStrategy: UserSortStrategy.NONE,
- sortAscending: true,
+ filters: {
+ local: false,
+ external: false,
+ active: false,
+ need_approval: false,
+ unconfirmed: false,
+ deactivated: false,
+ is_admin: false,
+ is_moderator: false
+ },
expandedUser: null,
- filterActive: 'active_only',
- filterLocal: 'local_only',
loading: false
}
},
@@ -54,47 +35,39 @@ const UsersTab = {
Checkbox,
Select,
BasicUserCard,
- UserList,
+ PageList,
ProgressButton,
AdminCard,
},
computed: {
- knownDomains () {
- return this.$store.state.instance.knownDomains
- },
- user () {
- return this.$store.state.users.currentUser
- }
},
methods: {
+ delete_selection () {
+ console.log('delete selection')
+ },
+ delete_user () {},
+ fetch_page (store, opts) {
+ opts.query = ""
+ console.log('current filters:', this.filters)
+ opts.filters = this.filters
+ opts.name = ""
+ opts.email = ""
+ const users = store.dispatch('fetchAdminUsers', opts)
+ console.log('users', users)
+ return users
+ },
+ reset () {
+ this.$refs.userList.reset()
+ },
+ toggleLocal () {
+ console.log('toggle local')
+ this.filters.local = !this.filters.local
+ this.reset()
+ }
},
mounted() {
console.log("mounted")
- /*const store = this.$store;
- const moduleTree = buildModuleTree(store._modules.root);
- console.log(JSON.stringify(moduleTree, null, 2));*/
}
}
-/*function buildModuleTree(module, path = []) {
- const fullPath = path.join('/') || 'root';
- const moduleInfo = {
- path: fullPath,
- namespaced: module.namespaced,
- state: Object.keys(module.state),
- actions: module._rawModule.actions ? Object.keys(module._rawModule.actions) : [],
- mutations: module._rawModule.mutations ? Object.keys(module._rawModule.mutations) : [],
- getters: module._rawModule.getters ? Object.keys(module._rawModule.getters) : [],
- modules: []
- };
-
- if (module._children) {
- for (const key in module._children) {
- const child = module._children[key];
- moduleInfo.modules.push(buildModuleTree(child, path.concat(key)));
- }
- }
-
- return moduleInfo;
-}*/
export default UsersTab
diff --git a/src/components/settings_modal/admin_tabs/users_tab.vue b/src/components/settings_modal/admin_tabs/users_tab.vue
index 0f9d681bf..d60cd99a3 100644
--- a/src/components/settings_modal/admin_tabs/users_tab.vue
+++ b/src/components/settings_modal/admin_tabs/users_tab.vue
@@ -1,14 +1,101 @@
-
-
+ {filters.local = v; reset();}"
+ >
+ only local
+
+ {filters.external = v; reset();}"
+ >
+ only external
+
+ {filters.active = v; reset();}"
+ >
+ only active
+
+ {filters.need_approval = v; reset();}"
+ >
+ only unconfirmed
+
+ {filters.deactivated = v; reset();}"
+ >
+ only deactivated
+
+ {filters.is_admin = v; reset();}"
+ >
+ only if admin
+
+ {filters.is_moderator = v; reset();}"
+ >
+ only if moderator
+
+
+
+
+
-
+
+
+
+
-
+
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 05aa63c5b..e8bdd53c3 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -376,6 +376,9 @@
"selectable_list": {
"select_all": "Select all"
},
+ "page_list": {
+ "load_more": "Load more"
+ },
"settings": {
"add_language": "Add fallback language",
"remove_language": "Remove",
@@ -1138,7 +1141,10 @@
"local_only": "local only",
"external_only": "external only"
},
- "loading": "Loading..."
+ "loading": "Loading...",
+ "deactivate": "deactivate",
+ "activate": "activate",
+ "delete": "delete"
},
"limits": {
"arbitrary_limits": "Arbitrary limits",
diff --git a/src/modules/adminSettings.js b/src/modules/adminSettings.js
index 260b2d8b6..a55a48951 100644
--- a/src/modules/adminSettings.js
+++ b/src/modules/adminSettings.js
@@ -60,8 +60,12 @@ const adminSettingsStorage = {
}
},
actions: {
- fetchAdminUsers (store) {
- store.rootState.api.backendInteractor.adminListUsers().then((users) => users.forEach(user => store.dispatch('fetchUserIfMissing', user.id)))
+ async fetchAdminUsers (store, opts) {
+ const users = await store.rootState.api.backendInteractor.adminListUsers({opts})
+ //await Promise.all(
+ users.map(user => store.dispatch('fetchUserIfMissing', user.id))
+ //)
+ return users
},
loadFrontendsStuff ({ rootState, commit }) {
rootState.api.backendInteractor.fetchAvailableFrontends()
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index f88c3a6e5..acb014a53 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -115,7 +115,30 @@ const PLEROMA_ADMIN_CONFIG_URL = '/api/pleroma/admin/config'
const PLEROMA_ADMIN_DESCRIPTIONS_URL = '/api/pleroma/admin/config/descriptions'
const PLEROMA_ADMIN_FRONTENDS_URL = '/api/pleroma/admin/frontends'
const PLEROMA_ADMIN_FRONTENDS_INSTALL_URL = '/api/pleroma/admin/frontends/install'
-const PLEROMA_ADMIN_USERS_URL = '/api/v1/pleroma/admin/users'
+const PLEROMA_ADMIN_USERS_URL = ({page, pageSize, filters = {}, query = '', name = '', email = ''}) => {
+ const {
+ local = false,
+ external = false,
+ active = false,
+ need_approval = false,
+ unconfirmed = false,
+ deactivated = false,
+ is_admin = true,
+ is_moderator = true,
+ } = filters
+ const filters_str = (local ? 'local,' : '')
+ + (external ? 'external,' : '')
+ + (active ? 'active,' : '')
+ + (need_approval ? 'need_approval,' : '')
+ + (unconfirmed ? 'unconfirmed,' : '')
+ + (deactivated ? 'deactivated,' : '')
+ + (is_admin ? 'is_admin,' : '')
+ + (is_moderator ? 'is_moderator,' : '')
+ return `/api/v1/pleroma/admin/users?page=${page}&page_size=${pageSize}&filters=${filters_str}&query=${query}&name=${name}&email=${email}`
+}
+const PLEROMA_ADMIN_DELETE_USERS_URL = '/api/v1/pleroma/admin/users'
+const PLEROMA_ADMIN_ACTIVATE_USER_URL = '/api/v1/pleroma/admin/users/activate'
+const PLEROMA_ADMIN_DEACTIVATE_USER_URL = '/api/v1/pleroma/admin/users/deactivate'
const PLEROMA_EMOJI_RELOAD_URL = '/api/pleroma/admin/reload_emoji'
const PLEROMA_EMOJI_IMPORT_FS_URL = '/api/pleroma/emoji/packs/import'
@@ -1441,10 +1464,49 @@ const dismissAnnouncement = ({ id, credentials }) => {
})
}
-const adminListUsers = ({ 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
- return promisedRequest({ url: PLEROMA_ADMIN_USERS_URL, credentials }).then((data) => data.users.map(parseUser))
+ const url = PLEROMA_ADMIN_USERS_URL(opts)
+ console.log('admin api: ', url)
+ return promisedRequest({ url: url,
+ credentials,
+ method: 'GET'
+ }).then((data) => data.users.map(parseUser))
+}
+
+const adminDeleteUser = ({ nicknames, credentials }) => {
+ return promisedRequest({
+ url: PLEROMA_ADMIN_DELETE_USERS_URL,
+ credentials,
+ method: 'DELETE',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({'nicknames': nicknames})
+ })
+}
+
+const adminActivateUser = ({ nicknames, credentials }) => {
+ return promisedRequest({ url: PLEROMA_ADMIN_ACTIVATE_USER_URL,
+ credentials,
+ method: 'PATCH',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({'nicknames': nicknames})
+ })
+}
+
+const adminDeactivateUser = ({ nicknames, credentials }) => {
+ return promisedRequest({ url: PLEROMA_ADMIN_DEACTIVATE_USER_URL,
+ credentials,
+ method: 'PATCH',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({'nicknames': nicknames})
+ })
}
const announcementToPayload = ({ content, startsAt, endsAt, allDay }) => {
@@ -2120,6 +2182,9 @@ const apiService = {
updateBookmarkFolder,
deleteBookmarkFolder,
adminListUsers,
+ adminDeleteUser,
+ adminActivateUser,
+ adminDeactivateUser,
}
export default apiService
From a022ba942f4bef972dd58ff67e4188c603cbfed3 Mon Sep 17 00:00:00 2001
From: luce
Date: Sun, 20 Jul 2025 18:59:05 +0200
Subject: [PATCH 003/337] adding more things for managing users
---
src/components/page_list/page_list.vue | 3 +
.../settings_modal/admin_tabs/admin_card.js | 124 ++++++++--
.../settings_modal/admin_tabs/admin_card.vue | 152 +++++++++---
.../settings_modal/admin_tabs/users_tab.js | 75 +++++-
.../settings_modal/admin_tabs/users_tab.scss | 3 +
.../settings_modal/admin_tabs/users_tab.vue | 225 ++++++++++--------
src/i18n/en.json | 3 +-
src/modules/adminSettings.js | 30 ++-
src/services/api/api.service.js | 50 ++--
9 files changed, 476 insertions(+), 189 deletions(-)
create mode 100644 src/components/settings_modal/admin_tabs/users_tab.scss
diff --git a/src/components/page_list/page_list.vue b/src/components/page_list/page_list.vue
index 3de52579c..a0219c7e5 100644
--- a/src/components/page_list/page_list.vue
+++ b/src/components/page_list/page_list.vue
@@ -5,6 +5,9 @@
:get-key="i => i"
:items="items"
>
+
+
+
diff --git a/src/components/settings_modal/admin_tabs/admin_card.js b/src/components/settings_modal/admin_tabs/admin_card.js
index c9bf8147e..ea145c0c6 100644
--- a/src/components/settings_modal/admin_tabs/admin_card.js
+++ b/src/components/settings_modal/admin_tabs/admin_card.js
@@ -1,28 +1,108 @@
import BasicUserCard from '../../basic_user_card/basic_user_card.vue'
+import Checkbox from 'src/components/checkbox/checkbox.vue'
const AdminCard = {
- props: ['userId'],
- data () {
- return {
- progress: false
- }
- },
- computed: {
- isLoaded () {
- return typeof(this.user) !== 'undefined'
- },
- user () {
- return this.$store.getters.findUser(this.userId)
- },
- relationship () {
- return this.$store.getters.relationship(this.userId)
- },
- },
- components: {
- BasicUserCard
- },
- methods: {
- }
+ props: ['userDetails'],
+ data () {
+ return {
+ progress: false,
+ top_level_expanded: false,
+ json_expanded: false,
+ just_approved: false,
+ just_confirmed: false,
+ just_deleted: false,
+ }
+ },
+ mounted () {
+ },
+ computed: {
+ isLoaded () {
+ return typeof(this.user) !== 'undefined'
+ },
+ user () {
+ return this.$store.getters.findUser(this.userDetails.id)
+ },
+ relationship () {
+ return this.$store.getters.relationship(this.userDetails.id)
+ },
+ is_local () {
+ const u = this.$store.getters.findUser(this.userDetails.id)
+ if (typeof(u) !== 'undefined') {
+ return u.is_local === true
+ }
+ return false
+ },
+ is_admin () {
+ const u = this.$store.getters.findUser(this.userDetails.id)
+ if (typeof(u) !== 'undefined') {
+ return u.rights.admin === true
+ }
+ return false
+ },
+ is_moderator () {
+ const u = this.$store.getters.findUser(this.userDetails.id)
+ if (typeof(u) !== 'undefined') {
+ return u.rights.moderator === true
+ }
+ return false
+ },
+ is_activated () {
+ const u = this.$store.getters.findUser(this.userDetails.id)
+ if (typeof(u) !== 'undefined') {
+ return u.deactivated === false
+ }
+ return false
+ },
+ is_confirmed () {
+ return (this.userDetails.is_confirmed === false) || (this.just_confirmed === true)
+ },
+ is_approved () {
+ return (this.userDetails.is_approved === false) || (this.just_approved === true)
+ }
+ },
+ components: {
+ BasicUserCard,
+ Checkbox
+ },
+ methods: {
+ toggle_admin (v) {
+ const u = this.$store.getters.findUser(this.userDetails.id)
+ console.log('user', u)
+ if (v === true) {
+ this.$store.dispatch('adminAddUserToAdminGroup', u).then(res => console.log("res: ", res))
+ } else {
+ this.$store.dispatch('adminRemoveUserFromAdminGroup', u)
+ }
+ },
+ toggle_moderator (v) {
+ const u = this.$store.getters.findUser(this.userDetails.id)
+ if (v === true) {
+ this.$store.dispatch('adminAddUserToModeratorGroup', u)
+ } else {
+ this.$store.dispatch('adminRemoveUserFromModeratorGroup', u)
+ }
+ },
+ toggle_activation (v) {
+ const u = this.$store.getters.findUser(this.userDetails.id)
+ if (v === true) {
+ this.$store.dispatch('adminActivateUser', u)
+ } else {
+ this.$store.dispatch('adminDeactivateUser', u)
+ }
+ },
+ toggle_confirmation () {},
+ toggle_approval () {},
+ force_update_user () {
+ this.$store.dispatch('fetchUser', this.userDetails.id)
+ },
+ delete_user () {
+ if (!this.just_deleted) {
+ const u = this.$store.getters.findUser(this.userDetails.id)
+ this.$store.dispatch('adminDeleteUser', u)
+ this.just_deleted = true
+ }
+ }
+ }
}
export default AdminCard
diff --git a/src/components/settings_modal/admin_tabs/admin_card.vue b/src/components/settings_modal/admin_tabs/admin_card.vue
index c54cdcf1f..d2d434b3d 100644
--- a/src/components/settings_modal/admin_tabs/admin_card.vue
+++ b/src/components/settings_modal/admin_tabs/admin_card.vue
@@ -1,42 +1,126 @@
+
+
- loading user...
+ v-if="!isLoaded"
+ >
+ loading user...
-
-
-
-
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+ toggle_admin(v)"
+ >
+ is admin
+
+ toggle_moderator(v)"
+ >
+ is moderator
+
+ toggle_confirmation(v)"
+ >
+ is confirmed
+
+ toggle_approval(v)"
+ >
+ is approved
+
+
+
toggle_activation(v)"
+ >
+ is active
+
+
+
+
+
+
+
+
+
database
+
{{ JSON.stringify(user, null, 2) }}
+
details
+
{{ JSON.stringify(this.userDetails, null, 2) }}
+
+
-
diff --git a/src/components/settings_modal/admin_tabs/users_tab.js b/src/components/settings_modal/admin_tabs/users_tab.js
index 03274533d..5a59f308d 100644
--- a/src/components/settings_modal/admin_tabs/users_tab.js
+++ b/src/components/settings_modal/admin_tabs/users_tab.js
@@ -5,8 +5,7 @@ import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
import ProgressButton from 'src/components/progress_button/progress_button.vue'
import AdminCard from 'src/components/settings_modal/admin_tabs/admin_card.vue'
import PageList from 'src/components/page_list/page_list.vue'
-
-
+import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
const UsersTab = {
provide () {
@@ -17,8 +16,14 @@ const UsersTab = {
},
data() {
return {
+ /* filters must match the filter options below initially, or the ui is gonna have a computer moment
+ * no, i won't fix this
+ * */
+ filters_origin: "local",
+ filters_activity: "all",
+ filters_permission: "all",
filters: {
- local: false,
+ local: true,
external: false,
active: false,
need_approval: false,
@@ -38,35 +43,89 @@ const UsersTab = {
PageList,
ProgressButton,
AdminCard,
+ TabSwitcher,
},
computed: {
},
methods: {
+ update_origin (v) {
+ switch (v) {
+ case 'local':
+ this.filters.local = true
+ this.filters.external = false
+ break;
+ case 'external':
+ this.filters.local = false
+ this.filters.external = true
+ break;
+ default:
+ case 'all':
+ this.filters.local = false
+ this.filters.external = false
+ break;
+ }
+ this.reset()
+ },
+ update_activity (v) {
+ switch (v) {
+ case 'active':
+ this.filters.active = true
+ this.filters.deactivated = false
+ break;
+ case 'deactivated':
+ this.filters.active = false
+ this.filters.deactivated = true
+ break;
+ default:
+ case 'all':
+ this.filters.active = false
+ this.filters.deactivated = false
+ break;
+ }
+ this.reset()
+ },
+ update_permission (v) {
+ switch (v) {
+ case 'admin':
+ this.filters.is_admin = true
+ this.filters.is_moderator = false
+ break;
+ case 'moderator':
+ this.filters.is_admin = false
+ this.filters.is_moderator = true
+ break;
+ case 'modsnadmins':
+ this.filters.is_admin = true
+ this.filters.is_moderator = true
+ break;
+ default:
+ case 'all':
+ this.filters.is_admin = false
+ this.filters.is_moderator = false
+ break;
+ }
+ this.reset()
+ },
delete_selection () {
- console.log('delete selection')
},
delete_user () {},
fetch_page (store, opts) {
opts.query = ""
- console.log('current filters:', this.filters)
opts.filters = this.filters
opts.name = ""
opts.email = ""
const users = store.dispatch('fetchAdminUsers', opts)
- console.log('users', users)
return users
},
reset () {
this.$refs.userList.reset()
},
toggleLocal () {
- console.log('toggle local')
this.filters.local = !this.filters.local
this.reset()
}
},
mounted() {
- console.log("mounted")
}
}
diff --git a/src/components/settings_modal/admin_tabs/users_tab.scss b/src/components/settings_modal/admin_tabs/users_tab.scss
new file mode 100644
index 000000000..a42308167
--- /dev/null
+++ b/src/components/settings_modal/admin_tabs/users_tab.scss
@@ -0,0 +1,3 @@
+.user-tab {
+ height: 100%;
+}
diff --git a/src/components/settings_modal/admin_tabs/users_tab.vue b/src/components/settings_modal/admin_tabs/users_tab.vue
index d60cd99a3..a5f42af9a 100644
--- a/src/components/settings_modal/admin_tabs/users_tab.vue
+++ b/src/components/settings_modal/admin_tabs/users_tab.vue
@@ -1,101 +1,130 @@
+
-
-
{filters.local = v; reset();}"
- >
- only local
-
-
{filters.external = v; reset();}"
- >
- only external
-
-
{filters.active = v; reset();}"
- >
- only active
-
-
{filters.need_approval = v; reset();}"
- >
- only unconfirmed
-
-
{filters.deactivated = v; reset();}"
- >
- only deactivated
-
-
{filters.is_admin = v; reset();}"
- >
- only if admin
-
-
{filters.is_moderator = v; reset();}"
- >
- only if moderator
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
filter user search
+ todo: query, name and email input
+
+
+
+ {filters.need_approval = v; reset();}"
+ >
+ only unapproved
+
+ {filters.unconfirmed = v; reset();}"
+ >
+ only unconfirmed
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TBC
+
+
+
diff --git a/src/i18n/en.json b/src/i18n/en.json
index e8bdd53c3..0af0d37aa 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -1132,7 +1132,8 @@
}
},
"users": {
- "users": "User Management",
+ "management": "Management",
+ "invitations": "Invitations",
"search_users": "Search for users...",
"filters": {
"show_all": "show all",
diff --git a/src/modules/adminSettings.js b/src/modules/adminSettings.js
index a55a48951..05aa030aa 100644
--- a/src/modules/adminSettings.js
+++ b/src/modules/adminSettings.js
@@ -57,7 +57,7 @@ const adminSettingsStorage = {
},
resetAdminDraft (state) {
state.draft = cloneDeep(state.config)
- }
+ },
},
actions: {
async fetchAdminUsers (store, opts) {
@@ -67,6 +67,34 @@ const adminSettingsStorage = {
//)
return users
},
+ adminAddUserToAdminGroup (store, user) {
+ store.rootState.api.backendInteractor.adminAddUserToAdminGroup({ user })
+ .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 }))
+ },
+ 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 }))
+ },
+ adminActivateUser (store, user) {
+ return store.rootState.api.backendInteractor.activateUser({ user })
+ .then(res => console.log(res))
+ },
+ adminDeactivateUser (store, user) {
+ return store.rootState.api.backendInteractor.deactivateUser({ user })
+ .then(res => console.log(res))
+ },
+ adminDeleteUser (store, user) {
+ return store.rootState.api.backendInteractor.deleteUser({ user })
+ .then(res => console.log(res))
+ },
loadFrontendsStuff ({ rootState, commit }) {
rootState.api.backendInteractor.fetchAvailableFrontends()
.then(frontends => commit('setAvailableFrontends', { frontends }))
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index acb014a53..94cb9966a 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -136,6 +136,7 @@ const PLEROMA_ADMIN_USERS_URL = ({page, pageSize, filters = {}, query = '', name
+ (is_moderator ? 'is_moderator,' : '')
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_DELETE_USERS_URL = '/api/v1/pleroma/admin/users'
const PLEROMA_ADMIN_ACTIVATE_USER_URL = '/api/v1/pleroma/admin/users/activate'
const PLEROMA_ADMIN_DEACTIVATE_USER_URL = '/api/v1/pleroma/admin/users/deactivate'
@@ -1475,37 +1476,35 @@ const adminListUsers = ({ opts, credentials }) => {
}).then((data) => data.users.map(parseUser))
}
-const adminDeleteUser = ({ nicknames, credentials }) => {
- return promisedRequest({
- url: PLEROMA_ADMIN_DELETE_USERS_URL,
+const adminAddUserToAdminGroup = ({ user, credentials }) => {
+ const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'admin')
+ return promisedRequest({ url: url,
credentials,
- method: 'DELETE',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({'nicknames': nicknames})
+ method: 'POST'
})
}
-const adminActivateUser = ({ nicknames, credentials }) => {
- return promisedRequest({ url: PLEROMA_ADMIN_ACTIVATE_USER_URL,
+const adminRemoveUserFromAdminGroup = ({ user, credentials }) => {
+ const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'admin')
+ return promisedRequest({ url: url,
credentials,
- method: 'PATCH',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({'nicknames': nicknames})
+ method: 'DELETE'
})
}
-const adminDeactivateUser = ({ nicknames, credentials }) => {
- return promisedRequest({ url: PLEROMA_ADMIN_DEACTIVATE_USER_URL,
+const adminAddUserToModeratorGroup = ({ user, credentials }) => {
+ const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'moderator')
+ return promisedRequest({ url: url,
credentials,
- method: 'PATCH',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({'nicknames': nicknames})
+ method: 'POST'
+ })
+}
+
+const adminRemoveUserFromModeratorGroup = ({ user, credentials }) => {
+ const url = PLEROMA_ADMIN_MODIFY_GROUP_URL(user.name_html, 'moderator')
+ return promisedRequest({ url: url,
+ credentials,
+ method: 'DELETE'
})
}
@@ -2182,9 +2181,10 @@ const apiService = {
updateBookmarkFolder,
deleteBookmarkFolder,
adminListUsers,
- adminDeleteUser,
- adminActivateUser,
- adminDeactivateUser,
+ adminAddUserToAdminGroup,
+ adminRemoveUserFromAdminGroup,
+ adminAddUserToModeratorGroup,
+ adminRemoveUserFromModeratorGroup,
}
export default apiService
From 064093c6709eaad1b69781148b9df17344f07ac3 Mon Sep 17 00:00:00 2001
From: luce
Date: Mon, 21 Jul 2025 00:32:49 +0200
Subject: [PATCH 004/337] more stuff added
---
.../settings_modal/admin_tabs/admin_card.js | 16 ++++++++--
.../settings_modal/admin_tabs/admin_card.vue | 18 ++++++++---
.../settings_modal/admin_tabs/users_tab.js | 23 +++++++++++---
.../settings_modal/admin_tabs/users_tab.vue | 21 +++++++++++--
src/modules/adminSettings.js | 12 +++++--
src/services/api/api.service.js | 31 +++++++++++++++++--
6 files changed, 101 insertions(+), 20 deletions(-)
diff --git a/src/components/settings_modal/admin_tabs/admin_card.js b/src/components/settings_modal/admin_tabs/admin_card.js
index ea145c0c6..b82ec6866 100644
--- a/src/components/settings_modal/admin_tabs/admin_card.js
+++ b/src/components/settings_modal/admin_tabs/admin_card.js
@@ -54,10 +54,12 @@ const AdminCard = {
return false
},
is_confirmed () {
- return (this.userDetails.is_confirmed === false) || (this.just_confirmed === true)
+ const u = this.$store.getters.findUser(this.userDetails.id)
+ return (u._original.pleroma.is_confirmed === false) || (this.just_confirmed === true)
},
is_approved () {
- return (this.userDetails.is_approved === false) || (this.just_approved === true)
+ const u = this.$store.getters.findUser(this.userDetails.id)
+ return (u._original.pleroma.is_approved === false) || (this.just_approved === true)
}
},
components: {
@@ -90,7 +92,15 @@ const AdminCard = {
this.$store.dispatch('adminDeactivateUser', u)
}
},
- toggle_confirmation () {},
+ confirm_user () {
+ const u = this.$store.getters.findUser(this.userDetails.id)
+ this.$store.dispatch('adminConfirmUser', u)
+ this.just_confirmed = true
+ },
+ resend_confirmation_email () {
+ const u = this.$store.getters.findUser(this.userDetails.id)
+ this.$store.dispatch('adminResendConfirmationEmail', u)
+ },
toggle_approval () {},
force_update_user () {
this.$store.dispatch('fetchUser', this.userDetails.id)
diff --git a/src/components/settings_modal/admin_tabs/admin_card.vue b/src/components/settings_modal/admin_tabs/admin_card.vue
index d2d434b3d..0875c50f5 100644
--- a/src/components/settings_modal/admin_tabs/admin_card.vue
+++ b/src/components/settings_modal/admin_tabs/admin_card.vue
@@ -70,12 +70,20 @@
>
is moderator
- toggle_confirmation(v)"
- >
+
+
+
+
toggle_approval(v)"
diff --git a/src/components/settings_modal/admin_tabs/users_tab.js b/src/components/settings_modal/admin_tabs/users_tab.js
index 5a59f308d..9bbaadfd3 100644
--- a/src/components/settings_modal/admin_tabs/users_tab.js
+++ b/src/components/settings_modal/admin_tabs/users_tab.js
@@ -22,6 +22,9 @@ const UsersTab = {
filters_origin: "local",
filters_activity: "all",
filters_permission: "all",
+ filters_query: '',
+ filters_name: '',
+ filters_email: '',
filters: {
local: true,
external: false,
@@ -30,7 +33,7 @@ const UsersTab = {
unconfirmed: false,
deactivated: false,
is_admin: false,
- is_moderator: false
+ is_moderator: false,
},
expandedUser: null,
loading: false
@@ -106,14 +109,26 @@ const UsersTab = {
}
this.reset()
},
+ update_query (v) {
+ this.filters_query = v
+ this.reset()
+ },
+ update_name (v) {
+ this.filters_name = v
+ this.reset()
+ },
+ update_email (v) {
+ this.filters_email = v
+ this.reset()
+ },
delete_selection () {
},
delete_user () {},
fetch_page (store, opts) {
- opts.query = ""
+ opts.query = this.filters_query
opts.filters = this.filters
- opts.name = ""
- opts.email = ""
+ opts.name = this.filters_name
+ opts.email = this.filters_email
const users = store.dispatch('fetchAdminUsers', opts)
return users
},
diff --git a/src/components/settings_modal/admin_tabs/users_tab.vue b/src/components/settings_modal/admin_tabs/users_tab.vue
index a5f42af9a..ed6f2e743 100644
--- a/src/components/settings_modal/admin_tabs/users_tab.vue
+++ b/src/components/settings_modal/admin_tabs/users_tab.vue
@@ -8,9 +8,24 @@
filter user search
todo: query, name and email input
+
update_query(v.target.value)"
+ />
+
update_name(v.target.value)"
+ />
+
update_email(v.target.value)"
+ />
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 @@
-
-
-
-
- {{ $t('admin_dash.users.actions.title') }}
-
-
-
-
-
-
+
+
-
+
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) {
From 01e2e0139af311011d420b81c0c1083569a59c90 Mon Sep 17 00:00:00 2001
From: Henry Jameson
Date: Wed, 10 Jun 2026 02:12:03 +0300
Subject: [PATCH 152/337] prettier confirmation dialog
---
src/components/confirm_modal/confirm_modal.js | 5 +++
.../confirm_modal/confirm_modal.vue | 41 ++++++++++++++++++-
2 files changed, 45 insertions(+), 1 deletion(-)
diff --git a/src/components/confirm_modal/confirm_modal.js b/src/components/confirm_modal/confirm_modal.js
index 08761177b..dbabb4b45 100644
--- a/src/components/confirm_modal/confirm_modal.js
+++ b/src/components/confirm_modal/confirm_modal.js
@@ -1,5 +1,10 @@
import DialogModal from 'src/components/dialog_modal/dialog_modal.vue'
+import { library } from '@fortawesome/fontawesome-svg-core'
+import { faCircleQuestion } from '@fortawesome/free-solid-svg-icons'
+
+library.add(faCircleQuestion)
+
/**
* This component emits the following events:
* cancelled, emitted when the action should not be performed;
diff --git a/src/components/confirm_modal/confirm_modal.vue b/src/components/confirm_modal/confirm_modal.vue
index 7e1a7ff68..239d8aacf 100644
--- a/src/components/confirm_modal/confirm_modal.vue
+++ b/src/components/confirm_modal/confirm_modal.vue
@@ -8,7 +8,16 @@
-
+
@@ -29,3 +38,33 @@
+
From f49259ac1f8a86f743ce1714579537446be95bbc Mon Sep 17 00:00:00 2001
From: Henry Jameson
Date: Wed, 10 Jun 2026 02:16:59 +0300
Subject: [PATCH 153/337] more swag
---
src/components/confirm_modal/confirm_modal.vue | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/src/components/confirm_modal/confirm_modal.vue b/src/components/confirm_modal/confirm_modal.vue
index 239d8aacf..1a8e0ad4e 100644
--- a/src/components/confirm_modal/confirm_modal.vue
+++ b/src/components/confirm_modal/confirm_modal.vue
@@ -40,18 +40,16 @@
+
diff --git a/src/stores/admin_settings.js b/src/stores/admin_settings.js
index 590ff4bef..55f6eca69 100644
--- a/src/stores/admin_settings.js
+++ b/src/stores/admin_settings.js
@@ -312,7 +312,7 @@ export const useAdminSettingsStore = defineStore('adminSettings', {
},
// Users stuff
- async fetchAdminUsers(opts) {
+ async fetchUsers(opts) {
const adminData = await this.backendInteractor.adminListUsers({
opts,
})
From aa0cef12b13cfa34ee45ae0dbf2acbd9f130cbc8 Mon Sep 17 00:00:00 2001
From: Henry Jameson
Date: Wed, 10 Jun 2026 15:49:51 +0300
Subject: [PATCH 164/337] initial implementation of godmode
---
src/boot/routes.js | 7 ++
.../user_profile/user_profile_admin_view.js | 66 +++++++++++++++++++
.../user_profile/user_profile_admin_view.vue | 30 +++++++++
src/stores/admin_settings.js | 15 +++--
4 files changed, 113 insertions(+), 5 deletions(-)
create mode 100644 src/components/user_profile/user_profile_admin_view.js
create mode 100644 src/components/user_profile/user_profile_admin_view.vue
diff --git a/src/boot/routes.js b/src/boot/routes.js
index 4a075e955..515a08a96 100644
--- a/src/boot/routes.js
+++ b/src/boot/routes.js
@@ -84,6 +84,13 @@ export default (store) => {
() => import('src/components/user_profile/user_profile.vue'),
),
},
+ {
+ name: 'user-profile-admin-view',
+ path: '/admin/users/$:id',
+ component: defineAsyncComponent(
+ () => import('src/components/user_profile/user_profile_admin_view.vue'),
+ ),
+ },
{
name: 'interactions',
path: '/users/:username/interactions',
diff --git a/src/components/user_profile/user_profile_admin_view.js b/src/components/user_profile/user_profile_admin_view.js
new file mode 100644
index 000000000..a243dcb20
--- /dev/null
+++ b/src/components/user_profile/user_profile_admin_view.js
@@ -0,0 +1,66 @@
+import { get } from 'lodash'
+import { mapState } from 'pinia'
+
+import Conversation from 'src/components/conversation/conversation.vue'
+import List from 'src/components/list/list.vue'
+import UserCard from 'src/components/user_card/user_card.vue'
+
+import { useInterfaceStore } from 'src/stores/interface.js'
+import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
+
+import { library } from '@fortawesome/fontawesome-svg-core'
+import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
+
+library.add(faCircleNotch)
+
+const defaultTabKey = 'statuses'
+
+const UserProfileAdminView = {
+ data() {
+ return {
+ userId: null,
+ godmode: false,
+ }
+ },
+ created() {
+ this.userId = this.$route.params.id
+ console.log(this.userId)
+ useInterfaceStore().setForeignProfileBackground(this.user?.background_image)
+ },
+ updated() {
+ useInterfaceStore().setForeignProfileBackground(this.user?.background_image)
+ },
+ unmounted() {
+ useInterfaceStore().setForeignProfileBackground(null)
+ },
+ computed: {
+ fetchOptions() {
+ return {
+ pageSize: 20,
+ godmode: this.godmode,
+ userId: this.userId,
+ withReblogs: false
+ }
+ },
+ user() {
+ return this.$store.getters.findUser(this.userId)
+ },
+ },
+ methods: {
+ fetchStatuses(page) {
+ return useAdminSettingsStore()
+ .fetchStatuses({
+ ...this.fetchOptions,
+ page,
+ })
+ .then(({ count, users }) => ({ count, items: users }))
+ },
+ },
+ components: {
+ UserCard,
+ List,
+ Conversation,
+ },
+}
+
+export default UserProfileAdminView
diff --git a/src/components/user_profile/user_profile_admin_view.vue b/src/components/user_profile/user_profile_admin_view.vue
new file mode 100644
index 000000000..ea72a7e62
--- /dev/null
+++ b/src/components/user_profile/user_profile_admin_view.vue
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
diff --git a/src/stores/admin_settings.js b/src/stores/admin_settings.js
index 55f6eca69..c3dd7b5a8 100644
--- a/src/stores/admin_settings.js
+++ b/src/stores/admin_settings.js
@@ -1,6 +1,8 @@
import { cloneDeep, differenceWith, flatten, get, isEqual, set } from 'lodash'
import { defineStore } from 'pinia'
+import { parseStatus } from 'src/services/entity_normalizer/entity_normalizer.service.js'
+
export const defaultState = {
frontends: [],
loaded: false,
@@ -299,11 +301,14 @@ export const useAdminSettingsStore = defineStore('adminSettings', {
},
// Statuses stuff
- listStatuses({ userId, opts }) {
- return this.backendInteractor.adminListStatuses({
- userId,
- opts,
- })
+ fetchStatuses(opts) {
+ return this
+ .backendInteractor
+ .adminListStatuses(opts)
+ .then(({ total, activities }) => ({
+ count: total,
+ items: activities.map(parseStatus)
+ })
},
changeStatusScope({ opts }) {
return this.backendInteractor.adminChangeStatusScope({
From 19d88751962528bce38336f52b00a4bada94e24c Mon Sep 17 00:00:00 2001
From: Henry Jameson
Date: Wed, 10 Jun 2026 17:19:45 +0300
Subject: [PATCH 165/337] implemented status visibility change
---
.../basic_user_card/basic_user_card.js | 2 +-
.../moderation_tools/moderation_tools.js | 22 ++++----
.../admin_tabs/admin_status_card.js | 22 --------
.../status_action_buttons/action_button.js | 2 +
.../action_button_container.js | 49 ++++++++++++++++-
.../action_button_container.vue | 52 +++++++++++++++++++
.../buttons_definitions.js | 20 +++++++
src/components/user_card/user_card.js | 6 +++
src/components/user_card/user_card.vue | 4 +-
src/components/user_profile/user_profile.scss | 15 ++++++
.../user_profile/user_profile_admin_view.js | 23 ++++----
.../user_profile/user_profile_admin_view.vue | 11 ++--
src/i18n/en.json | 5 +-
src/services/api/api.service.js | 38 ++++++--------
.../entity_normalizer.service.js | 5 +-
src/stores/admin_settings.js | 47 ++++++++++-------
16 files changed, 225 insertions(+), 98 deletions(-)
diff --git a/src/components/basic_user_card/basic_user_card.js b/src/components/basic_user_card/basic_user_card.js
index 1e807eca6..ae1f1c9c6 100644
--- a/src/components/basic_user_card/basic_user_card.js
+++ b/src/components/basic_user_card/basic_user_card.js
@@ -15,7 +15,7 @@ const BasicUserCard = {
showLineLabels: {
type: Boolean,
default: false,
- }
+ },
},
components: {
UserPopover,
diff --git a/src/components/moderation_tools/moderation_tools.js b/src/components/moderation_tools/moderation_tools.js
index b75d85a1a..7ee049a7f 100644
--- a/src/components/moderation_tools/moderation_tools.js
+++ b/src/components/moderation_tools/moderation_tools.js
@@ -185,16 +185,15 @@ const ModerationTools = {
entries() {
return ENTRIES.map(({ check, label, separator, conditions }) => {
if (separator) return 'separator'
- const [, negateToken, group, name] = /^([!~]?)([a-z-_]+):([a-z-_]+)$/.exec(
- check,
- )
+ const [, negateToken, group, name] =
+ /^([!~]?)([a-z-_]+):([a-z-_]+)$/.exec(check)
const hasTag = this.tagsSet.has(`${group}:${name}`)
const noTag = this.tagsSet.has(`!${group}:${name}`)
const maybeTag = this.tagsSet.has(`~${group}:${name}`)
// We are checking for condition to show element, i.e. only show "activate" if user is "deactivated"
- const checkNegated = (negateToken === '!' || negateToken === '~')
+ const checkNegated = negateToken === '!' || negateToken === '~'
// Naturally, new value should also be the same
const value = checkNegated
@@ -442,7 +441,11 @@ const ModerationTools = {
return this.$store.state.users.currentUser.privileges.has(privilege)
},
setTag(tag, value) {
- useAdminSettingsStore().setUsersTags({ users: this.users, value, tags: [tag] })
+ useAdminSettingsStore().setUsersTags({
+ users: this.users,
+ value,
+ tags: [tag],
+ })
},
setRight(right, value) {
useAdminSettingsStore().setUsersRight({ users: this.users, value, right })
@@ -542,9 +545,7 @@ const ModerationTools = {
)
this.confirmDialogContent =
'user_card.admin_menu.confirm_modal.disable_mfa_content'
- this.confirmDialogConfirm = this.$t(
- 'settings.confirm'
- )
+ this.confirmDialogConfirm = this.$t('settings.confirm')
break
}
case 'require_password_change': {
@@ -554,12 +555,11 @@ const ModerationTools = {
)
this.confirmDialogContent =
'user_card.admin_menu.confirm_modal.require_password_change_content'
- this.confirmDialogConfirm = this.$t(
- 'settings.confirm'
- )
+ this.confirmDialogConfirm = this.$t('settings.confirm')
break
}
}
+ break
}
case 'state': {
switch (name) {
diff --git a/src/components/settings_modal/admin_tabs/admin_status_card.js b/src/components/settings_modal/admin_tabs/admin_status_card.js
index 467f96472..ff078a709 100644
--- a/src/components/settings_modal/admin_tabs/admin_status_card.js
+++ b/src/components/settings_modal/admin_tabs/admin_status_card.js
@@ -6,19 +6,9 @@ import { parseStatus } from 'src/services/entity_normalizer/entity_normalizer.se
const AdminStatusCard = {
props: {
- /**
- * minimal status info
- * @type {import('vue').PropType<{
- * id: string
- * }>}
- */
statusDetails: {
type: Object,
required: true,
- /**
- * @param {any} u
- * @returns {u is { id: string }}
- */
validator(u) {
return typeof u.id === 'string'
},
@@ -31,23 +21,14 @@ const AdminStatusCard = {
}
},
computed: {
- /**
- * @returns {boolean} is this status sensitive?
- */
isSensitive() {
return this.statusDetails.sensitive === true
},
- /**
- * @returns {'public' | 'unlisted' | 'private' | 'direct'} status visibility
- */
visibility() {
return this.statusDetails.visibility
},
},
methods: {
- /**
- * @param {boolean} v set sensitive
- */
changeSensitivity(v) {
this.$store
.dispatch('adminChangeStatusScope', {
@@ -56,9 +37,6 @@ const AdminStatusCard = {
.then((res) => parseStatus(res))
.then((s) => (this.statusCache = s))
},
- /**
- * @param {boolean} v set visible
- */
changeVisibility(v) {
this.$store
.dispatch('adminChangeStatusScope', {
diff --git a/src/components/status_action_buttons/action_button.js b/src/components/status_action_buttons/action_button.js
index 661d1befd..e3cdf1fb1 100644
--- a/src/components/status_action_buttons/action_button.js
+++ b/src/components/status_action_buttons/action_button.js
@@ -19,6 +19,7 @@ import {
faChevronDown,
faChevronRight,
faExternalLinkAlt,
+ faEye,
faEyeSlash,
faHistory,
faMinus,
@@ -51,6 +52,7 @@ library.add(
faBookmark,
faBookmarkRegular,
faEyeSlash,
+ faEye,
faThumbtack,
faShareAlt,
faExternalLinkAlt,
diff --git a/src/components/status_action_buttons/action_button_container.js b/src/components/status_action_buttons/action_button_container.js
index 012844dc8..1f4cfc3f6 100644
--- a/src/components/status_action_buttons/action_button_container.js
+++ b/src/components/status_action_buttons/action_button_container.js
@@ -3,14 +3,30 @@ import { defineAsyncComponent } from 'vue'
import Popover from 'src/components/popover/popover.vue'
import ActionButton from './action_button.vue'
+import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
+
import { library } from '@fortawesome/fontawesome-svg-core'
import {
+ faEnvelope,
+ faEye,
+ faEyeSlash,
faFolderTree,
faGlobe,
+ faLock,
+ faLockOpen,
faUser,
} from '@fortawesome/free-solid-svg-icons'
-library.add(faUser, faGlobe, faFolderTree)
+library.add(
+ faUser,
+ faGlobe,
+ faFolderTree,
+ faEye,
+ faEyeSlash,
+ faLock,
+ faLockOpen,
+ faEnvelope,
+)
export default {
components: {
@@ -61,8 +77,27 @@ export default {
this.domain,
)
},
+ availableScopes() {
+ return ['private', 'unlisted', 'direct', 'public'].filter((scope) => {
+ return scope !== this.status.visibility
+ })
+ },
},
methods: {
+ visibilityIcon(visibility) {
+ switch (visibility) {
+ case 'private':
+ return 'lock'
+ case 'unlisted':
+ return 'lock-open'
+ case 'direct':
+ return 'envelope'
+ case 'local':
+ return 'igloo'
+ default:
+ return 'globe'
+ }
+ },
unmuteUser() {
return this.$store.dispatch('unmuteUser', this.user.id)
},
@@ -79,6 +114,18 @@ export default {
this.$refs.confirmUser.optionallyPrompt()
}
},
+ setScope(visibility) {
+ return useAdminSettingsStore().changeStatusScope({
+ id: this.status.id,
+ visibility,
+ })
+ },
+ setSensitive(sensitive) {
+ useAdminSettingsStore().changeStatusScope({
+ id: this.status.id,
+ sensitive,
+ })
+ },
toggleConversationMute() {
if (this.conversationIsMuted) {
this.unmuteConversation()
diff --git a/src/components/status_action_buttons/action_button_container.vue b/src/components/status_action_buttons/action_button_container.vue
index da0beed79..919207622 100644
--- a/src/components/status_action_buttons/action_button_container.vue
+++ b/src/components/status_action_buttons/action_button_container.vue
@@ -14,6 +14,58 @@
/>
+
@@ -228,7 +228,7 @@
diff --git a/src/components/user_profile/user_profile.scss b/src/components/user_profile/user_profile.scss
index 55caa21d9..dbbf8008d 100644
--- a/src/components/user_profile/user_profile.scss
+++ b/src/components/user_profile/user_profile.scss
@@ -24,6 +24,21 @@
.user-info {
margin: 1.2em;
}
+
+ &.-admin-view {
+ .list-item {
+ padding: 0;
+ border-bottom: 1px solid var(--border);
+
+ .admin-actions {
+ background: var(--background);
+ }
+
+ .Status{
+ width: 100%
+ }
+ }
+ }
}
.user-profile-placeholder {
diff --git a/src/components/user_profile/user_profile_admin_view.js b/src/components/user_profile/user_profile_admin_view.js
index a243dcb20..8a3a80a36 100644
--- a/src/components/user_profile/user_profile_admin_view.js
+++ b/src/components/user_profile/user_profile_admin_view.js
@@ -1,20 +1,18 @@
import { get } from 'lodash'
import { mapState } from 'pinia'
-import Conversation from 'src/components/conversation/conversation.vue'
import List from 'src/components/list/list.vue'
+import Status from 'src/components/status/status.vue'
import UserCard from 'src/components/user_card/user_card.vue'
-import { useInterfaceStore } from 'src/stores/interface.js'
import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
+import { useInterfaceStore } from 'src/stores/interface.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
library.add(faCircleNotch)
-const defaultTabKey = 'statuses'
-
const UserProfileAdminView = {
data() {
return {
@@ -24,7 +22,6 @@ const UserProfileAdminView = {
},
created() {
this.userId = this.$route.params.id
- console.log(this.userId)
useInterfaceStore().setForeignProfileBackground(this.user?.background_image)
},
updated() {
@@ -38,8 +35,8 @@ const UserProfileAdminView = {
return {
pageSize: 20,
godmode: this.godmode,
- userId: this.userId,
- withReblogs: false
+ id: this.userId,
+ withReblogs: false,
}
},
user() {
@@ -48,18 +45,16 @@ const UserProfileAdminView = {
},
methods: {
fetchStatuses(page) {
- return useAdminSettingsStore()
- .fetchStatuses({
- ...this.fetchOptions,
- page,
- })
- .then(({ count, users }) => ({ count, items: users }))
+ return useAdminSettingsStore().fetchStatuses({
+ ...this.fetchOptions,
+ page,
+ })
},
},
components: {
UserCard,
List,
- Conversation,
+ Status,
},
}
diff --git a/src/components/user_profile/user_profile_admin_view.vue b/src/components/user_profile/user_profile_admin_view.vue
index ea72a7e62..f841e57c8 100644
--- a/src/components/user_profile/user_profile_admin_view.vue
+++ b/src/components/user_profile/user_profile_admin_view.vue
@@ -1,14 +1,15 @@
-
diff --git a/src/i18n/en.json b/src/i18n/en.json
index c602a2a03..2d8223e85 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -1689,7 +1689,10 @@
"invisible_quote": "Quoted status unavailable: {link}",
"more_actions": "More actions on this status",
"loading": "Loading...",
- "load_error": "Unable to load status: {error}"
+ "load_error": "Unable to load status: {error}",
+ "admin_change_scope": "Change visibility",
+ "mark_as_sensitive": "Sensitive",
+ "mark_as_non-sensitive": "Non-sensitive"
},
"user_card": {
"approve": "Approve",
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index 629f05c0c..85fc84ff9 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -186,7 +186,12 @@ 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_LIST_STATUSES_URL = (id, pageSize, godmode, 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}`
@@ -1700,14 +1705,12 @@ 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_LIST(opts)
+
return promisedRequest({
- url: url,
+ url,
credentials,
method: 'GET',
- }).then((data) => ({
- ...data,
- users: data.users,
- }))
+ })
}
const adminResendConfirmationEmail = ({
@@ -1752,20 +1755,14 @@ const adminDisableMFA = ({ screen_name: nickname, credentials }) => {
})
}
-const adminListStatuses = ({
- userId,
- pageSize = 20,
- godmode,
- withReblogs,
- credentials,
-}) => {
- const url = PLEROMA_ADMIN_LIST_STATUSES_URL(
- userId,
- pageSize,
- godmode,
- withReblogs,
- )
- return promisedRequest({ url, credentials, method: 'GET' })
+const adminListStatuses = ({ opts, credentials }) => {
+ const url = PLEROMA_ADMIN_LIST_STATUSES_URL(opts)
+
+ return promisedRequest({
+ url,
+ credentials,
+ method: 'GET',
+ })
}
const adminChangeStatusScope = ({
@@ -2260,7 +2257,6 @@ const listEmojiPacks = ({ page, pageSize }) => {
}
const listRemoteEmojiPacks = ({ instance, page, pageSize }) => {
- console.log(instance)
if (!instance.startsWith('http')) {
instance = 'https://' + instance
}
diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index 79cd35f9e..94a5681be 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -150,7 +150,10 @@ 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 = typeof data.source.pleroma.show_role === 'boolean' ? data.source.pleroma.show_role : true
+ 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
diff --git a/src/stores/admin_settings.js b/src/stores/admin_settings.js
index c3dd7b5a8..04c89da22 100644
--- a/src/stores/admin_settings.js
+++ b/src/stores/admin_settings.js
@@ -301,37 +301,46 @@ export const useAdminSettingsStore = defineStore('adminSettings', {
},
// Statuses stuff
- fetchStatuses(opts) {
- return this
- .backendInteractor
- .adminListStatuses(opts)
- .then(({ total, activities }) => ({
- count: total,
- items: activities.map(parseStatus)
+ async fetchStatuses(opts) {
+ const { total, activities } =
+ await this.backendInteractor.adminListStatuses({
+ opts,
})
+
+ return {
+ items: activities.map(parseStatus),
+ count: total,
+ }
},
- changeStatusScope({ opts }) {
- return this.backendInteractor.adminChangeStatusScope({
+ async changeStatusScope(opts) {
+ const raw = await this.backendInteractor.adminChangeStatusScope({
opts,
})
+ const status = parseStatus(raw)
+
+ await window.vuex.dispatch('addNewStatuses', {
+ statuses: [status],
+ userId: false,
+ })
},
// Users stuff
async fetchUsers(opts) {
- const adminData = await this.backendInteractor.adminListUsers({
+ const { users, count } = await this.backendInteractor.adminListUsers({
opts,
})
- adminData.users = await Promise.all(
- adminData.users.map(
- async (userAdminData) =>
- await window.vuex.dispatch('updateUserAdminData', {
- userAdminData,
- }),
+ return {
+ items: await Promise.all(
+ users.map(
+ async (userAdminData) =>
+ await window.vuex.dispatch('updateUserAdminData', {
+ userAdminData,
+ }),
+ ),
),
- )
-
- return adminData
+ count,
+ }
},
async getUserData({ user }) {
const api = this.backendInteractor.adminGetUserData
From 9fbc832de9541bd842d9f68de910962fe5146221 Mon Sep 17 00:00:00 2001
From: Henry Jameson
Date: Wed, 10 Jun 2026 17:38:31 +0300
Subject: [PATCH 166/337] godmode implemented
---
src/boot/routes.js | 2 +-
src/components/moderation_tools/moderation_tools.js | 10 ++++++++++
src/components/user_profile/user_profile.scss | 12 ++++++++----
.../user_profile/user_profile_admin_view.js | 8 ++++++++
.../user_profile/user_profile_admin_view.vue | 9 +++++----
src/i18n/en.json | 2 ++
6 files changed, 34 insertions(+), 9 deletions(-)
diff --git a/src/boot/routes.js b/src/boot/routes.js
index 515a08a96..9b00a8004 100644
--- a/src/boot/routes.js
+++ b/src/boot/routes.js
@@ -86,7 +86,7 @@ export default (store) => {
},
{
name: 'user-profile-admin-view',
- path: '/admin/users/$:id',
+ path: '/users/$:id/admin_view',
component: defineAsyncComponent(
() => import('src/components/user_profile/user_profile_admin_view.vue'),
),
diff --git a/src/components/moderation_tools/moderation_tools.js b/src/components/moderation_tools/moderation_tools.js
index 7ee049a7f..82d24e4e4 100644
--- a/src/components/moderation_tools/moderation_tools.js
+++ b/src/components/moderation_tools/moderation_tools.js
@@ -77,6 +77,13 @@ const ENTRIES = [
{
separator: true,
},
+ {
+ check: 'action:statuses',
+ label: 'user_card.admin_menu.show_statuses',
+ },
+ {
+ separator: true,
+ },
{
check: 'action:disable_mfa',
label: 'user_card.admin_menu.disable_mfa',
@@ -217,6 +224,9 @@ const ModerationTools = {
case 'disable_mfa': {
return () => this.disableMFA()
}
+ case 'statuses': {
+ return () => this.$router.push(`/users/\$${this.users[0].id}/admin_view`)
+ }
case 'require_password_change': {
return () => this.requirePasswordChange()
}
diff --git a/src/components/user_profile/user_profile.scss b/src/components/user_profile/user_profile.scss
index dbbf8008d..35aa49ca9 100644
--- a/src/components/user_profile/user_profile.scss
+++ b/src/components/user_profile/user_profile.scss
@@ -26,18 +26,22 @@
}
&.-admin-view {
+ .godmode {
+ padding: 1em;
+ }
+
.list-item {
padding: 0;
border-bottom: 1px solid var(--border);
- .admin-actions {
- background: var(--background);
- }
-
.Status{
width: 100%
}
}
+
+ .footer {
+ background: var(--background);
+ }
}
}
diff --git a/src/components/user_profile/user_profile_admin_view.js b/src/components/user_profile/user_profile_admin_view.js
index 8a3a80a36..1ad849902 100644
--- a/src/components/user_profile/user_profile_admin_view.js
+++ b/src/components/user_profile/user_profile_admin_view.js
@@ -3,6 +3,7 @@ import { mapState } from 'pinia'
import List from 'src/components/list/list.vue'
import Status from 'src/components/status/status.vue'
+import Checkbox from 'src/components/checkbox/checkbox.vue'
import UserCard from 'src/components/user_card/user_card.vue'
import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
@@ -22,6 +23,7 @@ const UserProfileAdminView = {
},
created() {
this.userId = this.$route.params.id
+ this.$store.dispatch('fetchUserIfMissing', this.userId)
useInterfaceStore().setForeignProfileBackground(this.user?.background_image)
},
updated() {
@@ -55,7 +57,13 @@ const UserProfileAdminView = {
UserCard,
List,
Status,
+ Checkbox,
},
+ watch: {
+ godmode() {
+ this.$refs.list.reset()
+ }
+ }
}
export default UserProfileAdminView
diff --git a/src/components/user_profile/user_profile_admin_view.vue b/src/components/user_profile/user_profile_admin_view.vue
index f841e57c8..992ce3b4b 100644
--- a/src/components/user_profile/user_profile_admin_view.vue
+++ b/src/components/user_profile/user_profile_admin_view.vue
@@ -7,20 +7,21 @@
+
+ {{ $t('admin_dash.users.godmode') }}
+
-
+
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 2d8223e85..e7c03d673 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -1298,6 +1298,7 @@
"users": {
"title": "Users",
"local_id": "Local ID",
+ "godmode": "Show direct messages",
"labels": {
"query": "Search",
"name": "Name",
@@ -1798,6 +1799,7 @@
"remove_suggested_account": "Remove from suggested",
"approve_account": "Approve",
"confirm_account": "Confirm",
+ "show_statuses": "Show all posts",
"disable_mfa": "Disable MFA",
From 7a8da94723ddf2167b4302cf50d6cbfb14ec5a7d Mon Sep 17 00:00:00 2001
From: Henry Jameson
Date: Wed, 10 Jun 2026 17:39:53 +0300
Subject: [PATCH 167/337] lint
---
src/components/moderation_tools/moderation_tools.js | 3 ++-
src/components/user_profile/user_profile_admin_view.js | 6 +++---
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/components/moderation_tools/moderation_tools.js b/src/components/moderation_tools/moderation_tools.js
index 82d24e4e4..6e44bf97b 100644
--- a/src/components/moderation_tools/moderation_tools.js
+++ b/src/components/moderation_tools/moderation_tools.js
@@ -225,7 +225,8 @@ const ModerationTools = {
return () => this.disableMFA()
}
case 'statuses': {
- return () => this.$router.push(`/users/\$${this.users[0].id}/admin_view`)
+ return () =>
+ this.$router.push(`/users/\$${this.users[0].id}/admin_view`)
}
case 'require_password_change': {
return () => this.requirePasswordChange()
diff --git a/src/components/user_profile/user_profile_admin_view.js b/src/components/user_profile/user_profile_admin_view.js
index 1ad849902..73f9d83d2 100644
--- a/src/components/user_profile/user_profile_admin_view.js
+++ b/src/components/user_profile/user_profile_admin_view.js
@@ -1,9 +1,9 @@
import { get } from 'lodash'
import { mapState } from 'pinia'
+import Checkbox from 'src/components/checkbox/checkbox.vue'
import List from 'src/components/list/list.vue'
import Status from 'src/components/status/status.vue'
-import Checkbox from 'src/components/checkbox/checkbox.vue'
import UserCard from 'src/components/user_card/user_card.vue'
import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
@@ -62,8 +62,8 @@ const UserProfileAdminView = {
watch: {
godmode() {
this.$refs.list.reset()
- }
- }
+ },
+ },
}
export default UserProfileAdminView
From 82b9b005c7a2204bff0596ae0f166f44a6da629d Mon Sep 17 00:00:00 2001
From: Henry Jameson
Date: Wed, 10 Jun 2026 17:57:25 +0300
Subject: [PATCH 168/337] update changelog
---
changelog.d/user-management.add | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/changelog.d/user-management.add b/changelog.d/user-management.add
index 28dfc2220..ccd217f19 100644
--- a/changelog.d/user-management.add
+++ b/changelog.d/user-management.add
@@ -1 +1 @@
-user management (view and modify user info, view and modify user statuses)
+User administration + post scope/sensitivity admin change support
From 03c0c1664af4212d6cf78f120936e3506f89d088 Mon Sep 17 00:00:00 2001
From: Henry Jameson
Date: Wed, 10 Jun 2026 18:09:20 +0300
Subject: [PATCH 169/337] list styles
---
src/App.scss | 43 ++----------------------------------
src/components/list/list.css | 16 ++++++++++++++
2 files changed, 18 insertions(+), 41 deletions(-)
diff --git a/src/App.scss b/src/App.scss
index 952af5b47..3e1e7e2f2 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -418,45 +418,6 @@ nav {
}
}
-.list-item {
- border-color: var(--border);
- border-style: solid;
- border-width: 0;
- border-top-width: 1px;
-
- &.-active,
- &:hover {
- border-top-width: 1px;
- border-bottom-width: 1px;
- }
-
- &.-active + &,
- &:hover + & {
- border-top-width: 0;
- }
-
- &:hover + .menu-item-collapsible:not(.-expanded) + &,
- &.-active + .menu-item-collapsible:not(.-expanded) + & {
- border-top-width: 0;
- }
-
- &[aria-expanded="true"] {
- border-bottom-width: 1px;
- }
-
- &:first-child {
- border-top-right-radius: var(--roundness);
- border-top-left-radius: var(--roundness);
- border-top-width: 0;
- }
-
- &:last-child {
- border-bottom-right-radius: var(--roundness);
- border-bottom-left-radius: var(--roundness);
- border-bottom-width: 0;
- }
-}
-
.menu-item,
.list-item {
display: block;
@@ -480,8 +441,8 @@ nav {
cursor: auto;
}
- a,
- button:not(.button-default) {
+ > a,
+ > button:not(.button-default) {
text-align: initial;
padding: 0;
background: none;
diff --git a/src/components/list/list.css b/src/components/list/list.css
index 84b737ed2..c478e9bc4 100644
--- a/src/components/list/list.css
+++ b/src/components/list/list.css
@@ -14,6 +14,22 @@
display: flex;
align-items: center;
+ &[aria-expanded="true"] {
+ border-bottom-width: 1px;
+ }
+
+ &:first-child {
+ border-top-right-radius: var(--roundness);
+ border-top-left-radius: var(--roundness);
+ border-top-width: 0;
+ }
+
+ &:last-child {
+ border-bottom-right-radius: var(--roundness);
+ border-bottom-left-radius: var(--roundness);
+ border-bottom-width: 0;
+ }
+
&:not(:last-child) {
border-bottom: 1px dotted var(--border);
}
From f932006c0e68dfd195023c62af1437ddebce68b8 Mon Sep 17 00:00:00 2001
From: Henry Jameson
Date: Wed, 10 Jun 2026 18:09:49 +0300
Subject: [PATCH 170/337] fix user settings
---
src/components/settings_modal/tabs/notifications_tab.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/settings_modal/tabs/notifications_tab.js b/src/components/settings_modal/tabs/notifications_tab.js
index c863a7b9f..5114012a8 100644
--- a/src/components/settings_modal/tabs/notifications_tab.js
+++ b/src/components/settings_modal/tabs/notifications_tab.js
@@ -21,7 +21,7 @@ const NotificationsTab = {
if (!this.user) {
return false
}
- return this.user.privileges.includes('reports_manage_reports')
+ return this.user.privileges.has('reports_manage_reports')
},
...SharedComputedObject(),
},
From 5a7e6b8f6e9b648d4b9a4d09f1e2a87d598ce6c3 Mon Sep 17 00:00:00 2001
From: Henry Jameson
Date: Wed, 10 Jun 2026 18:18:53 +0300
Subject: [PATCH 171/337] fix pagination for user admin view
---
src/services/api/api.service.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index 85fc84ff9..5ab1fa18e 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -188,11 +188,12 @@ const PLEROMA_ADMIN_RESEND_CONFIRMATION_EMAIL_URL =
'/api/v1/pleroma/admin/users/resend_confirmation_email'
const PLEROMA_ADMIN_LIST_STATUSES_URL = ({
id,
+ page,
pageSize,
godmode,
withReblogs,
}) =>
- `/api/v1/pleroma/admin/users/${id}/statuses?page_size=${pageSize}&godmode=${godmode}&with_reblogs=${withReblogs}`
+ `/api/v1/pleroma/admin/users/${id}/statuses?page_size=${pageSize}&page=${page}&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 =
From 22789bb7db493965a5dc50a9008efff856722aaf Mon Sep 17 00:00:00 2001
From: Henry Jameson
Date: Wed, 10 Jun 2026 18:34:43 +0300
Subject: [PATCH 172/337] remove generic_confirm
---
.../confirm_modal/generic_confirm.js | 40 -------------------
.../confirm_modal/generic_confirm.vue | 23 -----------
2 files changed, 63 deletions(-)
delete mode 100644 src/components/confirm_modal/generic_confirm.js
delete 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
deleted file mode 100644
index 82bb0e54e..000000000
--- a/src/components/confirm_modal/generic_confirm.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import ConfirmModal from './confirm_modal.vue'
-//import Select from 'src/components/select/select.vue'
-
-export default {
- props: {
- title: {
- type: String,
- },
- message: {
- type: String,
- },
- cancelText: {
- type: String,
- },
- confirmText: {
- type: String,
- },
- },
- emits: ['hide', 'show', 'action'],
- data: () => ({
- showing: false,
- }),
- components: {
- ConfirmModal,
- },
- methods: {
- show() {
- this.showing = true
- this.$emit('show')
- },
- hide() {
- this.showing = false
- this.$emit('hide')
- },
- doGeneric() {
- this.$emit('action')
- this.hide()
- },
- },
-}
diff --git a/src/components/confirm_modal/generic_confirm.vue b/src/components/confirm_modal/generic_confirm.vue
deleted file mode 100644
index d223d6ea9..000000000
--- a/src/components/confirm_modal/generic_confirm.vue
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
From 041fcc94ed29b8c00efe0cd8da54b8cf501f02f8 Mon Sep 17 00:00:00 2001
From: Henry Jameson
Date: Wed, 10 Jun 2026 18:40:57 +0300
Subject: [PATCH 173/337] cleanup
---
src/components/confirm_modal/text_confirm.js | 41 -------------------
src/components/confirm_modal/text_confirm.vue | 27 ------------
2 files changed, 68 deletions(-)
delete mode 100644 src/components/confirm_modal/text_confirm.js
delete mode 100644 src/components/confirm_modal/text_confirm.vue
diff --git a/src/components/confirm_modal/text_confirm.js b/src/components/confirm_modal/text_confirm.js
deleted file mode 100644
index b27bd78f0..000000000
--- a/src/components/confirm_modal/text_confirm.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import ConfirmModal from './confirm_modal.vue'
-//import Select from 'src/components/select/select.vue'
-
-export default {
- props: {
- title: {
- type: String,
- },
- message: {
- type: String,
- },
- cancelText: {
- type: String,
- },
- confirmText: {
- type: String,
- },
- },
- emits: ['hide', 'show', 'action'],
- data: () => ({
- showing: false,
- text: '',
- }),
- components: {
- ConfirmModal,
- },
- methods: {
- show() {
- this.showing = true
- this.$emit('show')
- },
- hide() {
- this.showing = false
- this.$emit('hide')
- },
- doWithText() {
- this.$emit('action', this.text)
- this.hide()
- },
- },
-}
diff --git a/src/components/confirm_modal/text_confirm.vue b/src/components/confirm_modal/text_confirm.vue
deleted file mode 100644
index b4770c686..000000000
--- a/src/components/confirm_modal/text_confirm.vue
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
From 210b1f5ee442fa60c9561ab93784e8492a519362 Mon Sep 17 00:00:00 2001
From: Henry Jameson
Date: Wed, 10 Jun 2026 18:41:01 +0300
Subject: [PATCH 174/337] confirm swag
---
.../confirm_modal/confirm_modal.vue | 8 +-
.../user_timed_filter_modal.vue | 106 +++++++++---------
2 files changed, 61 insertions(+), 53 deletions(-)
diff --git a/src/components/confirm_modal/confirm_modal.vue b/src/components/confirm_modal/confirm_modal.vue
index 1a8e0ad4e..dbd5d8697 100644
--- a/src/components/confirm_modal/confirm_modal.vue
+++ b/src/components/confirm_modal/confirm_modal.vue
@@ -18,7 +18,9 @@
-
+
+
+
{{ $t(isMute ? 'user_card.expire_mute_message' : 'user_card.expire_block_message', [user.screen_name]) }}
-
+