Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mandatory filtering implementation #10099

Merged
merged 15 commits into from
Dec 5, 2023
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Enhancement: Support mandatory filter while listing users

We've added the configuration option `WEB_OPTION_USER_LIST_REQUIRES_FILTER`.
If set to true, an active filter is necessary to list users, as well users won't be listed initially.
This might be necessary for big instances with a lot of users, where the server response might take very long
or time out while requesting users.

https://github.com/owncloud/web/pull/10099
https://github.com/owncloud/web/issues/10088
1 change: 1 addition & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Depending on the backend you are using, there are sample config files provided i
- `options.imprintUrl` Specifies the target URL for the imprint link valid for the ocis instance in the account menu.
- `options.privacyUrl` Specifies the target URL for the privacy link valid for the ocis instance in the account menu.
- `options.ocm.openRemotely` Specifies whether opening files in remote shares in their original ocm instance should be enabled. Defaults to `false`.
- `options.userListRequiresFilter` Defines whether one or more filters must be set in order to list users in the Web admin settings. Set this option to 'true' if running in an environment with a lot of users and listing all users could slow down performance. Defaults to `false`.

#### Scripts and Styles

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ export default defineComponent({
unref(markInstance)?.unmark()
unref(markInstance)?.mark(unref(filterTerm), {
element: 'span',
className: 'highlight-mark'
className: 'mark-highlight'
})
})

Expand Down Expand Up @@ -340,7 +340,8 @@ export default defineComponent({
name: 'displayName',
title: this.$gettext('Group name'),
type: 'slot',
sortable: true
sortable: true,
tdClass: 'mark-element'
},
{
name: 'members',
Expand Down Expand Up @@ -376,7 +377,7 @@ export default defineComponent({
},
mounted() {
this.$nextTick(() => {
this.markInstance = new Mark('td.oc-table-data-cell-displayName')
this.markInstance = new Mark('.mark-element')
})
},
methods: {
Expand All @@ -395,8 +396,4 @@ export default defineComponent({
#groups-filter {
width: 16rem;
}

.highlight-mark {
font-weight: 600;
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export default defineComponent({
const searchTermRegex = unref(filterTerm)
unref(markInstance).mark(searchTermRegex, {
element: 'span',
className: 'highlight-mark'
className: 'mark-highlight'
})
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export default defineComponent({
unref(markInstance).unmark()
unref(markInstance).mark(unref(filterTerm), {
element: 'span',
className: 'highlight-mark'
className: 'mark-highlight'
})
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,8 @@ export default defineComponent({
name: 'name',
title: $gettext('Name'),
type: 'slot',
sortable: true
sortable: true,
tdClass: 'mark-element'
},
{
name: 'manager',
Expand Down Expand Up @@ -385,7 +386,7 @@ export default defineComponent({

onMounted(() => {
nextTick(() => {
markInstance.value = new Mark('td.oc-table-data-cell-name')
markInstance.value = new Mark('.mark-element')
})
})

Expand All @@ -397,7 +398,7 @@ export default defineComponent({
unref(markInstance)?.unmark()
unref(markInstance)?.mark(unref(filterTerm), {
element: 'span',
className: 'highlight-mark'
className: 'mark-highlight'
})
})

Expand Down Expand Up @@ -521,10 +522,6 @@ export default defineComponent({
width: 16rem;
}

.highlight-mark {
font-weight: 600;
}

.spaces-table {
.oc-table-header-cell-actions,
.oc-table-data-cell-actions {
Expand Down
86 changes: 7 additions & 79 deletions packages/web-app-admin-settings/src/components/Users/UsersList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,8 @@
<div id="user-list">
<div class="user-filters oc-flex oc-flex-between oc-flex-wrap oc-flex-bottom oc-mx-m oc-mb-m">
<slot name="filter" />
<oc-text-input
id="users-filter"
v-model="filterTerm"
:label="$gettext('Search')"
autocomplete="off"
/>
</div>
<no-content-message v-if="!users.length" icon="user">
<template #message>
<span v-text="$gettext('No users in here')" />
</template>
</no-content-message>
<slot v-if="!users.length" name="noResults" />
<oc-table
v-else
class="users-table"
Expand Down Expand Up @@ -103,7 +93,6 @@
<pagination :pages="totalPages" :current-page="currentPage" />
<div class="oc-text-nowrap oc-text-center oc-width-1-1 oc-my-s">
<p class="oc-text-muted">{{ footerTextTotal }}</p>
<p v-if="filterTerm" class="oc-text-muted">{{ footerTextFilter }}</p>
</div>
</template>
</oc-table>
Expand All @@ -112,21 +101,11 @@

<script lang="ts">
import { useGettext } from 'vue3-gettext'
import { defineComponent, PropType, ref, unref, watch, nextTick, onMounted, computed } from 'vue'
import Fuse from 'fuse.js'
import Mark from 'mark.js'
import {
defaultFuseOptions,
displayPositionedDropdown,
eventBus,
SortDir,
useRoute,
useRouter
} from '@ownclouders/web-pkg'
import { defineComponent, PropType, ref, unref, computed } from 'vue'
import { displayPositionedDropdown, eventBus, SortDir } from '@ownclouders/web-pkg'
import { SideBarEventTopics } from '@ownclouders/web-pkg'
import { AppRole, User } from '@ownclouders/web-client/src/generated'
import { ContextMenuQuickAction } from '@ownclouders/web-pkg'
import { NoContentMessage } from '@ownclouders/web-pkg'
import { useFileListHeaderPosition, usePagination } from '@ownclouders/web-pkg'
import { Pagination } from '@ownclouders/web-pkg'
import { perPageDefault, perPageStoragePrefix } from 'web-app-admin-settings/src/defaults'
Expand All @@ -139,7 +118,7 @@ import { findIndex } from 'lodash-es'

export default defineComponent({
name: 'UsersList',
components: { ContextMenuQuickAction, NoContentMessage, Pagination },
components: { ContextMenuQuickAction, Pagination },
props: {
users: {
type: Array as PropType<User[]>,
Expand All @@ -159,13 +138,9 @@ export default defineComponent({
const { $gettext } = useGettext()

const contextMenuButtonRef = ref(undefined)
const filterTerm = ref<string>('')
const sortBy = ref<string>('onPremisesSamAccountName')
const sortDir = ref<string>(SortDir.Asc)
const { y: fileListHeaderY } = useFileListHeaderPosition('#admin-settings-app-bar')
const router = useRouter()
const route = useRoute()
const markInstance = ref(null)

const lastSelectedUserIndex = ref(0)
const lastSelectedUserId = ref(null)
Expand Down Expand Up @@ -245,18 +220,6 @@ export default defineComponent({
displayPositionedDropdown(dropdown._tippy, event, unref(contextMenuButtonRef))
}

const filter = (users: User[], filterTerm: string) => {
if (!(filterTerm || '').trim()) {
return users
}
const usersSearchEngine = new Fuse(users, {
...defaultFuseOptions,
keys: ['displayName']
})

return usersSearchEngine.search(filterTerm).map((r) => r.item)
}

const getRoleDisplayNameByUser = (user: User) => {
const assignedRole = user.appRoleAssignments[0]

Expand Down Expand Up @@ -290,11 +253,7 @@ export default defineComponent({
}

const items = computed(() => {
return orderBy(
filter(props.users, unref(filterTerm)),
unref(sortBy),
unref(sortDir) === SortDir.Desc
)
return orderBy(props.users, unref(sortBy), unref(sortDir) === SortDir.Desc)
})

const {
Expand All @@ -303,23 +262,6 @@ export default defineComponent({
total: totalPages
} = usePagination({ items, perPageDefault, perPageStoragePrefix })

onMounted(async () => {
await nextTick()
markInstance.value = new Mark('td.oc-table-data-cell-displayName')
})

watch(filterTerm, async () => {
await unref(router).push({ ...unref(route), query: { ...unref(route).query, page: '1' } })
})

watch([filterTerm, paginatedItems], () => {
unref(markInstance)?.unmark()
unref(markInstance)?.mark(unref(filterTerm), {
element: 'span',
className: 'highlight-mark'
})
})

const keyActions = useKeyboardActions()
useKeyboardTableNavigation(
keyActions,
Expand Down Expand Up @@ -356,13 +298,11 @@ export default defineComponent({
fileListHeaderY,
getRoleDisplayNameByUser,
items,
filterTerm,
sortBy,
sortDir,
paginatedItems,
currentPage,
totalPages,
filter,
orderBy
}
},
Expand All @@ -380,11 +320,6 @@ export default defineComponent({
userCount: this.users.length.toString()
})
},
footerTextFilter() {
return this.$gettext('%{userCount} matching users', {
userCount: this.items.length.toString()
})
},
fields() {
return [
{
Expand All @@ -408,7 +343,8 @@ export default defineComponent({
{
name: 'displayName',
title: this.$gettext('First and last name'),
sortable: true
sortable: true,
tdClass: 'mark-element'
},
{
name: 'mail',
Expand Down Expand Up @@ -453,14 +389,6 @@ export default defineComponent({
</script>

<style lang="scss">
#users-filter {
width: 16rem;
}

.highlight-mark {
font-weight: 600;
}

.users-table {
.oc-table-header-cell-actions,
.oc-table-data-cell-actions {
Expand Down
Loading