fix(settings): Allow searching for groups in user row

Signed-off-by: Christopher Ng <chrng8@gmail.com>
pull/51782/head
Christopher Ng 2025-03-25 14:31:44 +07:00 committed by backportbot[bot]
parent f664eb8313
commit 86e521d032
4 changed files with 81 additions and 60 deletions

@ -34,8 +34,6 @@
users,
settings,
hasObfuscated,
groups,
subAdminsGroups,
quotaOptions,
languages,
externalActions,
@ -179,11 +177,6 @@ export default {
.sort((a, b) => a.name.localeCompare(b.name))
},
subAdminsGroups() {
// data provided php side
return this.$store.getters.getSubadminGroups
},
quotaOptions() {
// convert the preset array into objects
const quotaPreset = this.settings.quotaPreset.reduce((acc, cur) => acc.concat({

@ -117,6 +117,8 @@
label="name"
:no-wrap="true"
:create-option="(value) => ({ name: value, isCreating: true })"
@open="loadGroupDetails"
@search="searchGroups"
@option:created="createGroup"
@option:selected="options => addUserGroup(options.at(-1))"
@option:deselected="removeUserGroup" />
@ -127,10 +129,10 @@
</span>
</td>
<td v-if="subAdminsGroups.length > 0 && (settings.isAdmin || settings.isDelegatedAdmin)"
<td v-if="userSubAdminGroups.length > 0 && (settings.isAdmin || settings.isDelegatedAdmin)"
data-cy-user-list-cell-subadmins
class="row__cell row__cell--large row__cell--multiline">
<template v-if="editing && (settings.isAdmin || settings.isDelegatedAdmin) && subAdminsGroups.length > 0">
<template v-if="editing && (settings.isAdmin || settings.isDelegatedAdmin) && userSubAdminGroups.length > 0">
<label class="hidden-visually"
:for="'subadmins' + uniqueId">
{{ t('settings', 'Set account as admin for') }}
@ -145,15 +147,17 @@
:append-to-body="false"
:multiple="true"
:no-wrap="true"
:options="subAdminsGroups"
:options="availableSubAdminGroups"
:placeholder="t('settings', 'Set account as admin for')"
:value="userSubAdminsGroups"
:value="userSubAdminGroups"
@open="loadGroupDetails"
@search="searchGroups"
@option:deselected="removeUserSubAdmin"
@option:selected="options => addUserSubAdmin(options.at(-1))" />
</template>
<span v-else-if="!isObfuscated"
:title="userSubAdminsGroupsLabels?.length > 40 ? userSubAdminsGroupsLabels : null">
{{ userSubAdminsGroupsLabels }}
:title="userSubAdminGroupsLabels?.length > 40 ? userSubAdminGroupsLabels : null">
{{ userSubAdminGroupsLabels }}
</span>
</td>
@ -290,6 +294,8 @@ import UserRowActions from './UserRowActions.vue'
import UserRowMixin from '../../mixins/UserRowMixin.js'
import { isObfuscated, unlimitedQuota } from '../../utils/userUtils.ts'
import { formatGroup } from '../../utils/groups.ts'
import logger from '../../logger.ts'
export default {
name: 'UserRow',
@ -324,14 +330,6 @@ export default {
type: Boolean,
required: true,
},
groups: {
type: Array,
default: () => [],
},
subAdminsGroups: {
type: Array,
required: true,
},
quotaOptions: {
type: Array,
required: true,
@ -375,6 +373,8 @@ export default {
editedDisplayName: this.user.displayname,
editedPassword: '',
editedMail: this.user.email ?? '',
// Cancelable promise for search groups request
promise: null,
}
},
@ -406,13 +406,13 @@ export default {
userGroupsLabels() {
return this.userGroups
.map(group => group.name)
.map(group => group.name ?? group.id)
.join(', ')
},
userSubAdminsGroupsLabels() {
return this.userSubAdminsGroups
.map(group => group.name)
userSubAdminGroupsLabels() {
return this.userSubAdminGroups
.map(group => group.name ?? group.id)
.join(', ')
},
@ -548,6 +548,46 @@ export default {
this.loadingPossibleManagers = false
},
async loadGroupDetails() {
this.loading.groups = true
try {
const { data } = await this.$store.dispatch('getUserGroups', {
userId: this.user.id,
})
const groups = data.ocs?.data?.groups
if (!groups) {
logger.error(t('settings', 'Failed to load groups with details'))
return
}
this.availableGroups = this.availableGroups.map(availableGroup => groups.find(group => group.id === availableGroup.id) ?? availableGroup)
} catch (error) {
logger.error(t('settings', 'Failed to load groups with details'), { error })
}
this.loading.groups = false
},
async searchGroups(query, toggleLoading) {
if (this.promise) {
this.promise.cancel()
}
this.loading.groups = true
toggleLoading(true)
try {
this.promise = await searchGroups({
search: query,
offset: 0,
limit: 25,
})
const groups = (await this.promise).data.ocs?.data?.groups ?? []
this.availableGroups = groups.map(formatGroup)
} catch (error) {
logger.error(t('settings', 'Failed to search groups'), { error })
}
this.promise = null
this.loading.groups = false
toggleLoading(false)
},
async searchUserManager(query) {
await this.$store.dispatch('searchUsers', { offset: 0, limit: 10, search: query }).then(response => {
const users = response?.data ? this.filterManagers(Object.values(response?.data.ocs.data.users)) : []
@ -703,6 +743,7 @@ export default {
this.loading = { groups: true, subadmins: true }
try {
await this.$store.dispatch('addGroup', gid)
this.availableGroups.push({ id: gid, name: gid })
const userid = this.user.id
await this.$store.dispatch('addUserGroup', { userid, gid })
} catch (error) {
@ -732,6 +773,7 @@ export default {
}
try {
await this.$store.dispatch('addUserGroup', { userid, gid })
this.userGroups.push({ id: group.id, name: group.id })
} catch (error) {
console.error(error)
} finally {
@ -756,6 +798,7 @@ export default {
userid,
gid,
})
this.userGroups = this.userGroups.filter(group => group.id !== gid)
this.loading.groups = false
// remove user from current list if current list is the removed group
if (this.$route.params.selectedGroup === gid) {
@ -780,6 +823,7 @@ export default {
userid,
gid,
})
this.userSubAdminGroups.push({ id: group.id, name: group.id })
this.loading.subadmins = false
} catch (error) {
console.error(error)
@ -801,6 +845,7 @@ export default {
userid,
gid,
})
this.userSubAdminGroups = this.userSubAdminGroups.filter(group => group.id !== gid)
} catch (error) {
console.error(error)
} finally {

@ -13,14 +13,6 @@ export default {
type: Object,
default: () => ({}),
},
groups: {
type: Array,
default: () => [],
},
subAdminsGroups: {
type: Array,
default: () => [],
},
quotaOptions: {
type: Array,
default: () => [],
@ -39,33 +31,6 @@ export default {
return this.$store.getters.getShowConfig
},
/* GROUPS MANAGEMENT */
userGroups() {
const userGroups = this.groups.filter(group => this.user.groups.includes(group.id))
return userGroups
},
userSubAdminsGroups() {
const userSubAdminsGroups = this.subAdminsGroups.filter(group => this.user.subadmin.includes(group.id))
return userSubAdminsGroups
},
availableGroups() {
return this.groups.map((group) => {
// clone object because we don't want
// to edit the original groups
const groupClone = Object.assign({}, group)
// two settings here:
// 1. user NOT in group but no permission to add
// 2. user is in group but no permission to remove
groupClone.$isDisabled
= (group.canAdd === false
&& !this.user.groups.includes(group.id))
|| (group.canRemove === false
&& this.user.groups.includes(group.id))
return groupClone
})
},
/* QUOTA MANAGEMENT */
usedSpace() {
if (this.user.quota.used) {

@ -463,6 +463,24 @@ const actions = {
.catch((error) => context.commit('API_FAILURE', error))
},
/**
* Get user groups
*
* @param {object} context store context
* @param {object} options destructuring object
* @param {number} options.userId User id
* @return {Promise}
*/
async getUserGroups(context, { userId }) {
const url = generateOcsUrl('cloud/users/{userId}/groups/details', { userId })
try {
const response = await api.get(url)
return response
} catch (error) {
context.commit('API_FAILURE', error)
}
},
/**
* Get all users with full details
*