mirror of https://github.com/nextcloud/server.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
394 lines
10 KiB
Vue
394 lines
10 KiB
Vue
<!--
|
|
- @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
|
|
-
|
|
- @author John Molakvoæ <skjnldsv@protonmail.com>
|
|
-
|
|
- @license GNU AGPL version 3 or any later version
|
|
-
|
|
- This program is free software: you can redistribute it and/or modify
|
|
- it under the terms of the GNU Affero General Public License as
|
|
- published by the Free Software Foundation, either version 3 of the
|
|
- License, or (at your option) any later version.
|
|
-
|
|
- This program is distributed in the hope that it will be useful,
|
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- GNU Affero General Public License for more details.
|
|
-
|
|
- You should have received a copy of the GNU Affero General Public License
|
|
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
-
|
|
-->
|
|
|
|
<template>
|
|
<Fragment>
|
|
<NcContent app-name="settings">
|
|
<NcAppNavigation :aria-label="t('settings', 'Account management')">
|
|
<NcAppNavigationNew button-id="new-user-button"
|
|
:text="t('settings','New account')"
|
|
@click="showNewUserMenu"
|
|
@keyup.enter="showNewUserMenu"
|
|
@keyup.space="showNewUserMenu">
|
|
<template #icon>
|
|
<Plus :size="20" />
|
|
</template>
|
|
</NcAppNavigationNew>
|
|
|
|
<template #list>
|
|
<NcAppNavigationItem id="everyone"
|
|
:exact="true"
|
|
:name="t('settings', 'Active accounts')"
|
|
:to="{ name: 'users' }">
|
|
<template #icon>
|
|
<AccountGroup :size="20" />
|
|
</template>
|
|
<template #counter>
|
|
<NcCounterBubble v-if="userCount" :type="!selectedGroupDecoded ? 'highlighted' : undefined">
|
|
{{ userCount }}
|
|
</NcCounterBubble>
|
|
</template>
|
|
</NcAppNavigationItem>
|
|
|
|
<NcAppNavigationItem v-if="settings.isAdmin"
|
|
id="admin"
|
|
:exact="true"
|
|
:name="t('settings', 'Admins')"
|
|
:to="{ name: 'group', params: { selectedGroup: 'admin' } }">
|
|
<template #icon>
|
|
<ShieldAccount :size="20" />
|
|
</template>
|
|
<template v-if="adminGroupMenu.count > 0" #counter>
|
|
<NcCounterBubble :type="selectedGroupDecoded === 'admin' ? 'highlighted' : undefined">
|
|
{{ adminGroupMenu.count }}
|
|
</NcCounterBubble>
|
|
</template>
|
|
</NcAppNavigationItem>
|
|
|
|
<!-- Hide the disabled if none, if we don't have the data (-1) show it -->
|
|
<NcAppNavigationItem v-if="disabledGroupMenu.usercount > 0 || disabledGroupMenu.usercount === -1"
|
|
id="disabled"
|
|
:exact="true"
|
|
:name="t('settings', 'Disabled users')"
|
|
:to="{ name: 'group', params: { selectedGroup: 'disabled' } }">
|
|
<template #icon>
|
|
<AccountOff :size="20" />
|
|
</template>
|
|
<template v-if="disabledGroupMenu.usercount > 0" #counter>
|
|
<NcCounterBubble :type="selectedGroupDecoded === 'disabled' ? 'highlighted' : undefined">
|
|
{{ disabledGroupMenu.usercount }}
|
|
</NcCounterBubble>
|
|
</template>
|
|
</NcAppNavigationItem>
|
|
|
|
<NcAppNavigationCaption :name="t('settings', 'Groups')"
|
|
:disabled="loadingAddGroup"
|
|
:aria-label="loadingAddGroup ? t('settings', 'Creating group …') : t('settings', 'Create group')"
|
|
force-menu
|
|
:open.sync="isAddGroupOpen">
|
|
<template #actionsTriggerIcon>
|
|
<NcLoadingIcon v-if="loadingAddGroup" />
|
|
<Plus v-else :size="20" />
|
|
</template>
|
|
<template #actions>
|
|
<NcActionText>
|
|
<template #icon>
|
|
<AccountGroup :size="20" />
|
|
</template>
|
|
{{ t('settings', 'Create group') }}
|
|
</NcActionText>
|
|
<NcActionInput :label="t('settings', 'Group name')"
|
|
data-cy-settings-new-group-name
|
|
:label-outside="false"
|
|
:disabled="loadingAddGroup"
|
|
:value.sync="newGroupName"
|
|
:error="hasAddGroupError"
|
|
:helper-text="hasAddGroupError ? t('settings', 'Please enter a valid group name') : ''"
|
|
@submit="createGroup" />
|
|
</template>
|
|
</NcAppNavigationCaption>
|
|
|
|
<GroupListItem v-for="group in groupList"
|
|
:id="group.id"
|
|
:key="group.id"
|
|
:active="selectedGroupDecoded === group.id"
|
|
:name="group.title"
|
|
:count="group.count" />
|
|
</template>
|
|
|
|
<template #footer>
|
|
<ul class="app-navigation-entry__settings">
|
|
<NcAppNavigationItem :name="t('settings', 'Account management settings')"
|
|
@click="isDialogOpen = true">
|
|
<template #icon>
|
|
<Cog :size="20" />
|
|
</template>
|
|
</NcAppNavigationItem>
|
|
</ul>
|
|
</template>
|
|
</NcAppNavigation>
|
|
|
|
<NcAppContent :page-heading="pageHeading">
|
|
<UserList :selected-group="selectedGroupDecoded"
|
|
:external-actions="externalActions" />
|
|
</NcAppContent>
|
|
</NcContent>
|
|
|
|
<UserSettingsDialog :open.sync="isDialogOpen" />
|
|
</Fragment>
|
|
</template>
|
|
|
|
<script>
|
|
import Vue from 'vue'
|
|
import VueLocalStorage from 'vue-localstorage'
|
|
import { Fragment } from 'vue-frag'
|
|
import { translate as t } from '@nextcloud/l10n'
|
|
import { showError } from '@nextcloud/dialogs'
|
|
|
|
import NcActionInput from '@nextcloud/vue/dist/Components/NcActionInput.js'
|
|
import NcActionText from '@nextcloud/vue/dist/Components/NcActionText.js'
|
|
import NcAppContent from '@nextcloud/vue/dist/Components/NcAppContent.js'
|
|
import NcAppNavigation from '@nextcloud/vue/dist/Components/NcAppNavigation.js'
|
|
import NcAppNavigationCaption from '@nextcloud/vue/dist/Components/NcAppNavigationCaption.js'
|
|
import NcAppNavigationItem from '@nextcloud/vue/dist/Components/NcAppNavigationItem.js'
|
|
import NcAppNavigationNew from '@nextcloud/vue/dist/Components/NcAppNavigationNew.js'
|
|
import NcContent from '@nextcloud/vue/dist/Components/NcContent.js'
|
|
import NcCounterBubble from '@nextcloud/vue/dist/Components/NcCounterBubble.js'
|
|
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
|
|
|
|
import AccountGroup from 'vue-material-design-icons/AccountGroup.vue'
|
|
import AccountOff from 'vue-material-design-icons/AccountOff.vue'
|
|
import Cog from 'vue-material-design-icons/Cog.vue'
|
|
import Plus from 'vue-material-design-icons/Plus.vue'
|
|
import ShieldAccount from 'vue-material-design-icons/ShieldAccount.vue'
|
|
|
|
import GroupListItem from '../components/GroupListItem.vue'
|
|
import UserList from '../components/UserList.vue'
|
|
import UserSettingsDialog from '../components/Users/UserSettingsDialog.vue'
|
|
|
|
Vue.use(VueLocalStorage)
|
|
|
|
export default {
|
|
name: 'Users',
|
|
|
|
components: {
|
|
AccountGroup,
|
|
AccountOff,
|
|
Cog,
|
|
Fragment,
|
|
GroupListItem,
|
|
NcActionInput,
|
|
NcActionText,
|
|
NcAppContent,
|
|
NcAppNavigation,
|
|
NcAppNavigationCaption,
|
|
NcAppNavigationItem,
|
|
NcAppNavigationNew,
|
|
NcContent,
|
|
NcCounterBubble,
|
|
NcLoadingIcon,
|
|
Plus,
|
|
ShieldAccount,
|
|
UserList,
|
|
UserSettingsDialog,
|
|
},
|
|
|
|
props: {
|
|
selectedGroup: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
// temporary value used for multiselect change
|
|
externalActions: [],
|
|
newGroupName: '',
|
|
isAddGroupOpen: false,
|
|
loadingAddGroup: false,
|
|
hasAddGroupError: false,
|
|
isDialogOpen: false,
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
pageHeading() {
|
|
if (this.selectedGroupDecoded === null) {
|
|
return t('settings', 'Active accounts')
|
|
}
|
|
const matchHeading = {
|
|
admin: t('settings', 'Admins'),
|
|
disabled: t('settings', 'Disabled accounts'),
|
|
}
|
|
return matchHeading[this.selectedGroupDecoded] ?? t('settings', 'Account group: {group}', { group: this.selectedGroupDecoded })
|
|
},
|
|
|
|
showConfig() {
|
|
return this.$store.getters.getShowConfig
|
|
},
|
|
|
|
selectedGroupDecoded() {
|
|
return this.selectedGroup ? decodeURIComponent(this.selectedGroup) : null
|
|
},
|
|
|
|
users() {
|
|
return this.$store.getters.getUsers
|
|
},
|
|
|
|
groups() {
|
|
return this.$store.getters.getGroups
|
|
},
|
|
|
|
usersOffset() {
|
|
return this.$store.getters.getUsersOffset
|
|
},
|
|
|
|
usersLimit() {
|
|
return this.$store.getters.getUsersLimit
|
|
},
|
|
|
|
userCount() {
|
|
return this.$store.getters.getUserCount
|
|
},
|
|
|
|
settings() {
|
|
return this.$store.getters.getServerData
|
|
},
|
|
|
|
groupList() {
|
|
const groups = Array.isArray(this.groups) ? this.groups : []
|
|
|
|
return groups
|
|
// filter out disabled and admin
|
|
.filter(group => group.id !== 'disabled' && group.id !== 'admin')
|
|
.map(group => this.formatGroupMenu(group))
|
|
},
|
|
|
|
adminGroupMenu() {
|
|
return this.formatGroupMenu(this.groups.find(group => group.id === 'admin'))
|
|
},
|
|
|
|
disabledGroupMenu() {
|
|
return this.formatGroupMenu(this.groups.find(group => group.id === 'disabled'))
|
|
},
|
|
},
|
|
|
|
beforeMount() {
|
|
this.$store.commit('initGroups', {
|
|
groups: this.$store.getters.getServerData.groups,
|
|
orderBy: this.$store.getters.getServerData.sortGroups,
|
|
userCount: this.$store.getters.getServerData.userCount,
|
|
})
|
|
this.$store.dispatch('getPasswordPolicyMinLength')
|
|
},
|
|
|
|
created() {
|
|
// init the OCA.Settings.UserList object
|
|
// and add the registerAction method
|
|
Object.assign(OCA, {
|
|
Settings: {
|
|
UserList: {
|
|
registerAction: this.registerAction,
|
|
},
|
|
},
|
|
})
|
|
},
|
|
|
|
methods: {
|
|
t,
|
|
|
|
showNewUserMenu() {
|
|
this.$store.commit('setShowConfig', {
|
|
key: 'showNewUserForm',
|
|
value: true,
|
|
})
|
|
},
|
|
|
|
/**
|
|
* Register a new action for the user menu
|
|
*
|
|
* @param {string} icon the icon class
|
|
* @param {string} text the text to display
|
|
* @param {Function} action the function to run
|
|
* @return {Array}
|
|
*/
|
|
registerAction(icon, text, action) {
|
|
this.externalActions.push({
|
|
icon,
|
|
text,
|
|
action,
|
|
})
|
|
return this.externalActions
|
|
},
|
|
|
|
/**
|
|
* Create a new group
|
|
*/
|
|
async createGroup() {
|
|
this.hasAddGroupError = false
|
|
const groupId = this.newGroupName.trim()
|
|
if (groupId === '') {
|
|
this.hasAddGroupError = true
|
|
return
|
|
}
|
|
|
|
this.isAddGroupOpen = false
|
|
this.loadingAddGroup = true
|
|
try {
|
|
await this.$store.dispatch('addGroup', groupId)
|
|
await this.$router.push({
|
|
name: 'group',
|
|
params: {
|
|
selectedGroup: encodeURIComponent(groupId),
|
|
},
|
|
})
|
|
this.newGroupName = ''
|
|
} catch {
|
|
showError(t('settings', 'Failed to create group'))
|
|
}
|
|
this.loadingAddGroup = false
|
|
},
|
|
|
|
/**
|
|
* Format a group to a menu entry
|
|
*
|
|
* @param {object} group the group
|
|
* @return {object}
|
|
*/
|
|
formatGroupMenu(group) {
|
|
const item = {}
|
|
if (typeof group === 'undefined') {
|
|
return {}
|
|
}
|
|
|
|
item.id = group.id
|
|
item.title = group.name
|
|
item.usercount = group.usercount
|
|
|
|
// users count for all groups
|
|
if (group.usercount - group.disabled > 0) {
|
|
item.count = group.usercount - group.disabled
|
|
}
|
|
|
|
return item
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.app-content {
|
|
// Virtual list needs to be full height and is scrollable
|
|
display: flex;
|
|
overflow: hidden;
|
|
flex-direction: column;
|
|
max-height: 100%;
|
|
}
|
|
|
|
.app-navigation-entry__settings {
|
|
height: auto !important;
|
|
// Prevent shrinking or growing
|
|
flex: 0 0 auto;
|
|
}
|
|
</style>
|