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.
970 lines
26 KiB
Vue
970 lines
26 KiB
Vue
<!--
|
|
- @copyright Copyright (c) 2019 Gary Kim <gary@garykim.dev>
|
|
- @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
|
|
-
|
|
- @author Christopher Ng <chrng8@gmail.com>
|
|
- @author Gary Kim <gary@garykim.dev>
|
|
- @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>
|
|
<tr class="user-list__row"
|
|
:data-cy-user-row="user.id">
|
|
<td class="row__cell row__cell--avatar" data-cy-user-list-cell-avatar>
|
|
<NcLoadingIcon v-if="isLoadingUser"
|
|
:name="t('settings', 'Loading account …')"
|
|
:size="32" />
|
|
<NcAvatar v-else-if="visible"
|
|
disable-menu
|
|
:show-user-status="false"
|
|
:user="user.id" />
|
|
</td>
|
|
|
|
<td class="row__cell row__cell--displayname" data-cy-user-list-cell-displayname>
|
|
<template v-if="editing && user.backendCapabilities.setDisplayName">
|
|
<NcTextField ref="displayNameField"
|
|
class="user-row-text-field"
|
|
data-cy-user-list-input-displayname
|
|
:data-loading="loading.displayName || undefined"
|
|
:trailing-button-label="t('settings', 'Submit')"
|
|
:class="{ 'icon-loading-small': loading.displayName }"
|
|
:show-trailing-button="true"
|
|
:disabled="loading.displayName || isLoadingField"
|
|
:label="t('settings', 'Change display name')"
|
|
trailing-button-icon="arrowRight"
|
|
:value.sync="editedDisplayName"
|
|
autocapitalize="off"
|
|
autocomplete="off"
|
|
spellcheck="false"
|
|
@trailing-button-click="updateDisplayName" />
|
|
</template>
|
|
<template v-else>
|
|
<strong v-if="!isObfuscated"
|
|
:title="user.displayname?.length > 20 ? user.displayname : null">
|
|
{{ user.displayname }}
|
|
</strong>
|
|
<span class="row__subtitle">{{ user.id }}</span>
|
|
</template>
|
|
</td>
|
|
|
|
<td data-cy-user-list-cell-password
|
|
class="row__cell"
|
|
:class="{ 'row__cell--obfuscated': hasObfuscated }">
|
|
<template v-if="editing && settings.canChangePassword && user.backendCapabilities.setPassword">
|
|
<NcTextField class="user-row-text-field"
|
|
data-cy-user-list-input-password
|
|
:data-loading="loading.password || undefined"
|
|
:trailing-button-label="t('settings', 'Submit')"
|
|
:class="{'icon-loading-small': loading.password}"
|
|
:show-trailing-button="true"
|
|
:disabled="loading.password || isLoadingField"
|
|
:minlength="minPasswordLength"
|
|
maxlength="469"
|
|
:label="t('settings', 'Set new password')"
|
|
trailing-button-icon="arrowRight"
|
|
:value.sync="editedPassword"
|
|
autocapitalize="off"
|
|
autocomplete="new-password"
|
|
required
|
|
spellcheck="false"
|
|
type="password"
|
|
@trailing-button-click="updatePassword" />
|
|
</template>
|
|
<span v-else-if="isObfuscated">
|
|
{{ t('settings', 'You do not have permissions to see the details of this account') }}
|
|
</span>
|
|
</td>
|
|
|
|
<td class="row__cell" data-cy-user-list-cell-email>
|
|
<template v-if="editing">
|
|
<NcTextField class="user-row-text-field"
|
|
:class="{'icon-loading-small': loading.mailAddress}"
|
|
data-cy-user-list-input-email
|
|
:data-loading="loading.mailAddress || undefined"
|
|
:show-trailing-button="true"
|
|
:trailing-button-label="t('settings', 'Submit')"
|
|
:label="t('settings', 'Set new email address')"
|
|
:disabled="loading.mailAddress || isLoadingField"
|
|
trailing-button-icon="arrowRight"
|
|
:value.sync="editedMail"
|
|
autocapitalize="off"
|
|
autocomplete="email"
|
|
spellcheck="false"
|
|
type="email"
|
|
@trailing-button-click="updateEmail" />
|
|
</template>
|
|
<span v-else-if="!isObfuscated"
|
|
:title="user.email?.length > 20 ? user.email : null">
|
|
{{ user.email }}
|
|
</span>
|
|
</td>
|
|
|
|
<td class="row__cell row__cell--large row__cell--multiline" data-cy-user-list-cell-groups>
|
|
<template v-if="editing">
|
|
<label class="hidden-visually"
|
|
:for="'groups' + uniqueId">
|
|
{{ t('settings', 'Add user to group') }}
|
|
</label>
|
|
<NcSelect data-cy-user-list-input-groups
|
|
:data-loading="loading.groups || undefined"
|
|
:input-id="'groups' + uniqueId"
|
|
:close-on-select="false"
|
|
:disabled="isLoadingField"
|
|
:loading="loading.groups"
|
|
:multiple="true"
|
|
:append-to-body="false"
|
|
:options="availableGroups"
|
|
:placeholder="t('settings', 'Add account to group')"
|
|
:taggable="settings.isAdmin"
|
|
:value="userGroups"
|
|
label="name"
|
|
:no-wrap="true"
|
|
:create-option="(value) => ({ name: value, isCreating: true })"
|
|
@option:created="createGroup"
|
|
@option:selected="options => addUserGroup(options.at(-1))"
|
|
@option:deselected="removeUserGroup" />
|
|
</template>
|
|
<span v-else-if="!isObfuscated"
|
|
:title="userGroupsLabels?.length > 40 ? userGroupsLabels : null">
|
|
{{ userGroupsLabels }}
|
|
</span>
|
|
</td>
|
|
|
|
<td v-if="subAdminsGroups.length > 0 && settings.isAdmin"
|
|
data-cy-user-list-cell-subadmins
|
|
class="row__cell row__cell--large row__cell--multiline">
|
|
<template v-if="editing && settings.isAdmin && subAdminsGroups.length > 0">
|
|
<label class="hidden-visually"
|
|
:for="'subadmins' + uniqueId">
|
|
{{ t('settings', 'Set account as admin for') }}
|
|
</label>
|
|
<NcSelect data-cy-user-list-input-subadmins
|
|
:data-loading="loading.subadmins || undefined"
|
|
:input-id="'subadmins' + uniqueId"
|
|
:close-on-select="false"
|
|
:disabled="isLoadingField"
|
|
:loading="loading.subadmins"
|
|
label="name"
|
|
:append-to-body="false"
|
|
:multiple="true"
|
|
:no-wrap="true"
|
|
:options="subAdminsGroups"
|
|
:placeholder="t('settings', 'Set account as admin for')"
|
|
:value="userSubAdminsGroups"
|
|
@option:deselected="removeUserSubAdmin"
|
|
@option:selected="options => addUserSubAdmin(options.at(-1))" />
|
|
</template>
|
|
<span v-else-if="!isObfuscated"
|
|
:title="userSubAdminsGroupsLabels?.length > 40 ? userSubAdminsGroupsLabels : null">
|
|
{{ userSubAdminsGroupsLabels }}
|
|
</span>
|
|
</td>
|
|
|
|
<td class="row__cell" data-cy-user-list-cell-quota>
|
|
<template v-if="editing">
|
|
<label class="hidden-visually"
|
|
:for="'quota' + uniqueId">
|
|
{{ t('settings', 'Select account quota') }}
|
|
</label>
|
|
<NcSelect v-model="editedUserQuota"
|
|
:close-on-select="true"
|
|
:create-option="validateQuota"
|
|
data-cy-user-list-input-quota
|
|
:data-loading="loading.quota || undefined"
|
|
:disabled="isLoadingField"
|
|
:loading="loading.quota"
|
|
:append-to-body="false"
|
|
:clearable="false"
|
|
:input-id="'quota' + uniqueId"
|
|
:options="quotaOptions"
|
|
:placeholder="t('settings', 'Select account quota')"
|
|
:taggable="true"
|
|
@option:selected="setUserQuota" />
|
|
</template>
|
|
<template v-else-if="!isObfuscated">
|
|
<span :id="'quota-progress' + uniqueId">{{ userQuota }} ({{ usedSpace }})</span>
|
|
<NcProgressBar :aria-labelledby="'quota-progress' + uniqueId"
|
|
class="row__progress"
|
|
:class="{
|
|
'row__progress--warn': usedQuota > 80,
|
|
}"
|
|
:value="usedQuota" />
|
|
</template>
|
|
</td>
|
|
|
|
<td v-if="showConfig.showLanguages"
|
|
class="row__cell row__cell--large"
|
|
data-cy-user-list-cell-language>
|
|
<template v-if="editing">
|
|
<label class="hidden-visually"
|
|
:for="'language' + uniqueId">
|
|
{{ t('settings', 'Set the language') }}
|
|
</label>
|
|
<NcSelect :id="'language' + uniqueId"
|
|
data-cy-user-list-input-language
|
|
:data-loading="loading.languages || undefined"
|
|
:allow-empty="false"
|
|
:disabled="isLoadingField"
|
|
:loading="loading.languages"
|
|
:clearable="false"
|
|
:append-to-body="false"
|
|
:options="availableLanguages"
|
|
:placeholder="t('settings', 'No language set')"
|
|
:value="userLanguage"
|
|
label="name"
|
|
@input="setUserLanguage" />
|
|
</template>
|
|
<span v-else-if="!isObfuscated">
|
|
{{ userLanguage.name }}
|
|
</span>
|
|
</td>
|
|
|
|
<td v-if="showConfig.showUserBackend || showConfig.showStoragePath"
|
|
data-cy-user-list-cell-storage-location
|
|
class="row__cell row__cell--large">
|
|
<template v-if="!isObfuscated">
|
|
<span v-if="showConfig.showUserBackend">{{ user.backend }}</span>
|
|
<span v-if="showConfig.showStoragePath"
|
|
:title="user.storageLocation"
|
|
class="row__subtitle">
|
|
{{ user.storageLocation }}
|
|
</span>
|
|
</template>
|
|
</td>
|
|
|
|
<td v-if="showConfig.showLastLogin"
|
|
:title="userLastLoginTooltip"
|
|
class="row__cell"
|
|
data-cy-user-list-cell-last-login>
|
|
<span v-if="!isObfuscated">{{ userLastLogin }}</span>
|
|
</td>
|
|
|
|
<td class="row__cell row__cell--large row__cell--fill" data-cy-user-list-cell-manager>
|
|
<template v-if="editing">
|
|
<label class="hidden-visually"
|
|
:for="'manager' + uniqueId">
|
|
{{ managerLabel }}
|
|
</label>
|
|
<NcSelect v-model="currentManager"
|
|
class="select--fill"
|
|
data-cy-user-list-input-manager
|
|
:data-loading="loading.manager || undefined"
|
|
:input-id="'manager' + uniqueId"
|
|
:close-on-select="true"
|
|
:disabled="isLoadingField"
|
|
:append-to-body="false"
|
|
:loading="loadingPossibleManagers || loading.manager"
|
|
label="displayname"
|
|
:options="possibleManagers"
|
|
:placeholder="managerLabel"
|
|
@open="searchInitialUserManager"
|
|
@search="searchUserManager"
|
|
@option:selected="updateUserManager" />
|
|
</template>
|
|
<span v-else-if="!isObfuscated">
|
|
{{ user.manager }}
|
|
</span>
|
|
</td>
|
|
|
|
<td class="row__cell row__cell--actions" data-cy-user-list-cell-actions>
|
|
<UserRowActions v-if="visible && !isObfuscated && canEdit && !loading.all"
|
|
:actions="userActions"
|
|
:disabled="isLoadingField"
|
|
:edit="editing"
|
|
:user="user"
|
|
@update:edit="toggleEdit" />
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
|
|
<script>
|
|
import { formatFileSize, parseFileSize } from '@nextcloud/files'
|
|
import { getCurrentUser } from '@nextcloud/auth'
|
|
import { showSuccess, showError } from '@nextcloud/dialogs'
|
|
|
|
import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js'
|
|
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
|
|
import NcProgressBar from '@nextcloud/vue/dist/Components/NcProgressBar.js'
|
|
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
|
|
import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
|
|
|
|
import UserRowActions from './UserRowActions.vue'
|
|
|
|
import UserRowMixin from '../../mixins/UserRowMixin.js'
|
|
import { isObfuscated, unlimitedQuota } from '../../utils/userUtils.ts';
|
|
|
|
export default {
|
|
name: 'UserRow',
|
|
|
|
components: {
|
|
NcAvatar,
|
|
NcLoadingIcon,
|
|
NcProgressBar,
|
|
NcSelect,
|
|
NcTextField,
|
|
UserRowActions,
|
|
},
|
|
|
|
mixins: [
|
|
UserRowMixin,
|
|
],
|
|
|
|
props: {
|
|
user: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
visible: {
|
|
type: Boolean,
|
|
required: true,
|
|
},
|
|
users: {
|
|
type: Array,
|
|
required: true,
|
|
},
|
|
hasObfuscated: {
|
|
type: Boolean,
|
|
required: true,
|
|
},
|
|
groups: {
|
|
type: Array,
|
|
default: () => [],
|
|
},
|
|
subAdminsGroups: {
|
|
type: Array,
|
|
required: true,
|
|
},
|
|
quotaOptions: {
|
|
type: Array,
|
|
required: true,
|
|
},
|
|
languages: {
|
|
type: Array,
|
|
required: true,
|
|
},
|
|
settings: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
externalActions: {
|
|
type: Array,
|
|
default: () => [],
|
|
},
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
selectedQuota: false,
|
|
rand: Math.random().toString(36).substring(2),
|
|
loadingPossibleManagers: false,
|
|
possibleManagers: [],
|
|
currentManager: '',
|
|
editing: false,
|
|
loading: {
|
|
all: false,
|
|
displayName: false,
|
|
password: false,
|
|
mailAddress: false,
|
|
groups: false,
|
|
subadmins: false,
|
|
quota: false,
|
|
delete: false,
|
|
disable: false,
|
|
languages: false,
|
|
wipe: false,
|
|
manager: false,
|
|
},
|
|
editedDisplayName: this.user.displayname,
|
|
editedPassword: '',
|
|
editedMail: this.user.email ?? '',
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
managerLabel() {
|
|
// TRANSLATORS This string describes a person's manager in the context of an organization
|
|
return t('settings', 'Set line manager')
|
|
},
|
|
|
|
isObfuscated() {
|
|
return isObfuscated(this.user)
|
|
},
|
|
|
|
showConfig() {
|
|
return this.$store.getters.getShowConfig
|
|
},
|
|
|
|
isLoadingUser() {
|
|
return this.loading.delete || this.loading.disable || this.loading.wipe
|
|
},
|
|
|
|
isLoadingField() {
|
|
return this.loading.delete || this.loading.disable || this.loading.all
|
|
},
|
|
|
|
uniqueId() {
|
|
return encodeURIComponent(this.user.id + this.rand)
|
|
},
|
|
|
|
userGroupsLabels() {
|
|
return this.userGroups
|
|
.map(group => group.name)
|
|
.join(', ')
|
|
},
|
|
|
|
userSubAdminsGroupsLabels() {
|
|
return this.userSubAdminsGroups
|
|
.map(group => group.name)
|
|
.join(', ')
|
|
},
|
|
|
|
usedSpace() {
|
|
if (this.user.quota?.used) {
|
|
return t('settings', '{size} used', { size: formatFileSize(this.user.quota?.used) })
|
|
}
|
|
return t('settings', '{size} used', { size: formatFileSize(0) })
|
|
},
|
|
|
|
canEdit() {
|
|
return getCurrentUser().uid !== this.user.id || this.settings.isAdmin
|
|
},
|
|
|
|
userQuota() {
|
|
let quota = this.user.quota?.quota
|
|
|
|
if (quota === 'default') {
|
|
quota = this.settings.defaultQuota
|
|
if (quota !== 'none') {
|
|
// convert to numeric value to match what the server would usually return
|
|
quota = parseFileSize(quota, true)
|
|
}
|
|
}
|
|
|
|
// when the default quota is unlimited, the server returns -3 here, map it to "none"
|
|
if (quota === 'none' || quota === -3) {
|
|
return t('settings', 'Unlimited')
|
|
} else if (quota >= 0) {
|
|
return formatFileSize(quota)
|
|
}
|
|
return formatFileSize(0)
|
|
},
|
|
|
|
userActions() {
|
|
const actions = [
|
|
{
|
|
icon: 'icon-delete',
|
|
text: t('settings', 'Delete account'),
|
|
action: this.deleteUser,
|
|
},
|
|
{
|
|
icon: 'icon-delete',
|
|
text: t('settings', 'Wipe all devices'),
|
|
action: this.wipeUserDevices,
|
|
},
|
|
{
|
|
icon: this.user.enabled ? 'icon-close' : 'icon-add',
|
|
text: this.user.enabled ? t('settings', 'Disable account') : t('settings', 'Enable account'),
|
|
action: this.enableDisableUser,
|
|
},
|
|
]
|
|
if (this.user.email !== null && this.user.email !== '') {
|
|
actions.push({
|
|
icon: 'icon-mail',
|
|
text: t('settings', 'Resend welcome email'),
|
|
action: this.sendWelcomeMail,
|
|
})
|
|
}
|
|
return actions.concat(this.externalActions)
|
|
},
|
|
|
|
// mapping saved values to objects
|
|
editedUserQuota: {
|
|
get() {
|
|
if (this.selectedQuota !== false) {
|
|
return this.selectedQuota
|
|
}
|
|
if (this.settings.defaultQuota !== unlimitedQuota.id && parseFileSize(this.settings.defaultQuota, true) >= 0) {
|
|
// if value is valid, let's map the quotaOptions or return custom quota
|
|
return { id: this.settings.defaultQuota, label: this.settings.defaultQuota }
|
|
}
|
|
return unlimitedQuota // unlimited
|
|
},
|
|
set(quota) {
|
|
this.selectedQuota = quota
|
|
},
|
|
},
|
|
|
|
availableLanguages() {
|
|
return this.languages[0].languages.concat(this.languages[1].languages)
|
|
},
|
|
},
|
|
|
|
async beforeMount() {
|
|
if (this.user.manager) {
|
|
await this.initManager(this.user.manager)
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
wipeUserDevices() {
|
|
const userid = this.user.id
|
|
OC.dialogs.confirmDestructive(
|
|
t('settings', 'In case of lost device or exiting the organization, this can remotely wipe the Nextcloud data from all devices associated with {userid}. Only works if the devices are connected to the internet.', { userid }),
|
|
t('settings', 'Remote wipe of devices'),
|
|
{
|
|
type: OC.dialogs.YES_NO_BUTTONS,
|
|
confirm: t('settings', 'Wipe {userid}\'s devices', { userid }),
|
|
confirmClasses: 'error',
|
|
cancel: t('settings', 'Cancel'),
|
|
},
|
|
(result) => {
|
|
if (result) {
|
|
this.loading.wipe = true
|
|
this.loading.all = true
|
|
this.$store.dispatch('wipeUserDevices', userid)
|
|
.then(() => showSuccess(t('settings', 'Wiped {userid}\'s devices', { userid })), { timeout: 2000 })
|
|
.finally(() => {
|
|
this.loading.wipe = false
|
|
this.loading.all = false
|
|
})
|
|
}
|
|
},
|
|
true,
|
|
)
|
|
},
|
|
|
|
filterManagers(managers) {
|
|
return managers.filter((manager) => manager.id !== this.user.id)
|
|
},
|
|
|
|
async initManager(userId) {
|
|
await this.$store.dispatch('getUser', userId).then(response => {
|
|
this.currentManager = response?.data.ocs.data
|
|
})
|
|
},
|
|
|
|
async searchInitialUserManager() {
|
|
this.loadingPossibleManagers = true
|
|
await this.searchUserManager()
|
|
this.loadingPossibleManagers = 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)) : []
|
|
if (users.length > 0) {
|
|
this.possibleManagers = users
|
|
}
|
|
})
|
|
},
|
|
|
|
async updateUserManager(manager) {
|
|
if (manager === null) {
|
|
this.currentManager = ''
|
|
}
|
|
this.loading.manager = true
|
|
try {
|
|
await this.$store.dispatch('setUserData', {
|
|
userid: this.user.id,
|
|
key: 'manager',
|
|
value: this.currentManager ? this.currentManager.id : '',
|
|
})
|
|
} catch (error) {
|
|
// TRANSLATORS This string describes a line manager in the context of an organization
|
|
showError(t('setting', 'Failed to update line manager'))
|
|
console.error(error)
|
|
} finally {
|
|
this.loading.manager = false
|
|
}
|
|
},
|
|
|
|
deleteUser() {
|
|
const userid = this.user.id
|
|
OC.dialogs.confirmDestructive(
|
|
t('settings', 'Fully delete {userid}\'s account including all their personal files, app data, etc.', { userid }),
|
|
t('settings', 'Account deletion'),
|
|
{
|
|
type: OC.dialogs.YES_NO_BUTTONS,
|
|
confirm: t('settings', 'Delete {userid}\'s account', { userid }),
|
|
confirmClasses: 'error',
|
|
cancel: t('settings', 'Cancel'),
|
|
},
|
|
(result) => {
|
|
if (result) {
|
|
this.loading.delete = true
|
|
this.loading.all = true
|
|
return this.$store.dispatch('deleteUser', userid)
|
|
.then(() => {
|
|
this.loading.delete = false
|
|
this.loading.all = false
|
|
})
|
|
}
|
|
},
|
|
true,
|
|
)
|
|
},
|
|
|
|
enableDisableUser() {
|
|
this.loading.delete = true
|
|
this.loading.all = true
|
|
const userid = this.user.id
|
|
const enabled = !this.user.enabled
|
|
return this.$store.dispatch('enableDisableUser', {
|
|
userid,
|
|
enabled,
|
|
})
|
|
.then(() => {
|
|
this.loading.delete = false
|
|
this.loading.all = false
|
|
})
|
|
},
|
|
|
|
/**
|
|
* Set user displayName
|
|
*
|
|
* @param {string} displayName The display name
|
|
*/
|
|
updateDisplayName() {
|
|
this.loading.displayName = true
|
|
this.$store.dispatch('setUserData', {
|
|
userid: this.user.id,
|
|
key: 'displayname',
|
|
value: this.editedDisplayName,
|
|
}).then(() => {
|
|
this.loading.displayName = false
|
|
if (this.editedDisplayName === this.user.displayname) {
|
|
showSuccess(t('setting', 'Display name was successfully changed'))
|
|
}
|
|
})
|
|
},
|
|
|
|
/**
|
|
* Set user password
|
|
*
|
|
* @param {string} password The email address
|
|
*/
|
|
updatePassword() {
|
|
this.loading.password = true
|
|
if (this.editedPassword.length === 0) {
|
|
showError(t('setting', "Password can't be empty"))
|
|
this.loading.password = false
|
|
} else {
|
|
this.$store.dispatch('setUserData', {
|
|
userid: this.user.id,
|
|
key: 'password',
|
|
value: this.editedPassword,
|
|
}).then(() => {
|
|
this.loading.password = false
|
|
this.editedPassword = ''
|
|
showSuccess(t('setting', 'Password was successfully changed'))
|
|
})
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Set user mailAddress
|
|
*
|
|
* @param {string} mailAddress The email address
|
|
*/
|
|
updateEmail() {
|
|
this.loading.mailAddress = true
|
|
if (this.editedMail === '') {
|
|
showError(t('setting', "Email can't be empty"))
|
|
this.loading.mailAddress = false
|
|
this.editedMail = this.user.email
|
|
} else {
|
|
this.$store.dispatch('setUserData', {
|
|
userid: this.user.id,
|
|
key: 'email',
|
|
value: this.editedMail,
|
|
}).then(() => {
|
|
this.loading.mailAddress = false
|
|
if (this.editedMail === this.user.email) {
|
|
showSuccess(t('setting', 'Email was successfully changed'))
|
|
}
|
|
})
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Create a new group and add user to it
|
|
*
|
|
* @param {string} gid Group id
|
|
*/
|
|
async createGroup({ name: gid }) {
|
|
this.loading = { groups: true, subadmins: true }
|
|
try {
|
|
await this.$store.dispatch('addGroup', gid)
|
|
const userid = this.user.id
|
|
await this.$store.dispatch('addUserGroup', { userid, gid })
|
|
} catch (error) {
|
|
console.error(error)
|
|
} finally {
|
|
this.loading = { groups: false, subadmins: false }
|
|
}
|
|
return this.$store.getters.getGroups[this.groups.length]
|
|
},
|
|
|
|
/**
|
|
* Add user to group
|
|
*
|
|
* @param {object} group Group object
|
|
*/
|
|
async addUserGroup(group) {
|
|
if (group.isCreating) {
|
|
// This is NcSelect's internal value for a new inputted group name
|
|
// Ignore
|
|
return
|
|
}
|
|
this.loading.groups = true
|
|
const userid = this.user.id
|
|
const gid = group.id
|
|
if (group.canAdd === false) {
|
|
return false
|
|
}
|
|
try {
|
|
await this.$store.dispatch('addUserGroup', { userid, gid })
|
|
} catch (error) {
|
|
console.error(error)
|
|
} finally {
|
|
this.loading.groups = false
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Remove user from group
|
|
*
|
|
* @param {object} group Group object
|
|
*/
|
|
async removeUserGroup(group) {
|
|
if (group.canRemove === false) {
|
|
return false
|
|
}
|
|
this.loading.groups = true
|
|
const userid = this.user.id
|
|
const gid = group.id
|
|
try {
|
|
await this.$store.dispatch('removeUserGroup', {
|
|
userid,
|
|
gid,
|
|
})
|
|
this.loading.groups = false
|
|
// remove user from current list if current list is the removed group
|
|
if (this.$route.params.selectedGroup === gid) {
|
|
this.$store.commit('deleteUser', userid)
|
|
}
|
|
} catch {
|
|
this.loading.groups = false
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Add user to group
|
|
*
|
|
* @param {object} group Group object
|
|
*/
|
|
async addUserSubAdmin(group) {
|
|
this.loading.subadmins = true
|
|
const userid = this.user.id
|
|
const gid = group.id
|
|
try {
|
|
await this.$store.dispatch('addUserSubAdmin', {
|
|
userid,
|
|
gid,
|
|
})
|
|
this.loading.subadmins = false
|
|
} catch (error) {
|
|
console.error(error)
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Remove user from group
|
|
*
|
|
* @param {object} group Group object
|
|
*/
|
|
async removeUserSubAdmin(group) {
|
|
this.loading.subadmins = true
|
|
const userid = this.user.id
|
|
const gid = group.id
|
|
|
|
try {
|
|
await this.$store.dispatch('removeUserSubAdmin', {
|
|
userid,
|
|
gid,
|
|
})
|
|
} catch (error) {
|
|
console.error(error)
|
|
} finally {
|
|
this.loading.subadmins = false
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Dispatch quota set request
|
|
*
|
|
* @param {string | object} quota Quota in readable format '5 GB' or Object {id: '5 GB', label: '5GB'}
|
|
* @return {string}
|
|
*/
|
|
async setUserQuota(quota = 'none') {
|
|
// Make sure correct label is set for unlimited quota
|
|
if (quota === 'none') {
|
|
quota = unlimitedQuota
|
|
}
|
|
this.loading.quota = true
|
|
|
|
// ensure we only send the preset id
|
|
quota = quota.id ? quota.id : quota
|
|
|
|
try {
|
|
// If human readable format, convert to raw float format
|
|
// Else just send the raw string
|
|
const value = (parseFileSize(quota, true) || quota).toString()
|
|
await this.$store.dispatch('setUserData', {
|
|
userid: this.user.id,
|
|
key: 'quota',
|
|
value,
|
|
})
|
|
} catch (error) {
|
|
console.error(error)
|
|
} finally {
|
|
this.loading.quota = false
|
|
}
|
|
return quota
|
|
},
|
|
|
|
/**
|
|
* Validate quota string to make sure it's a valid human file size
|
|
*
|
|
* @param {string | object} quota Quota in readable format '5 GB' or Object {id: '5 GB', label: '5GB'}
|
|
* @return {object} The validated quota object or unlimited quota if input is invalid
|
|
*/
|
|
validateQuota(quota) {
|
|
if (typeof quota === 'object') {
|
|
quota = quota?.id || quota.label
|
|
}
|
|
// only used for new presets sent through @Tag
|
|
const validQuota = parseFileSize(quota, true)
|
|
if (validQuota === null) {
|
|
return unlimitedQuota
|
|
} else {
|
|
// unify format output
|
|
quota = formatFileSize(parseFileSize(quota, true))
|
|
return { id: quota, label: quota }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Dispatch language set request
|
|
*
|
|
* @param {object} lang language object {code:'en', name:'English'}
|
|
* @return {object}
|
|
*/
|
|
async setUserLanguage(lang) {
|
|
this.loading.languages = true
|
|
// ensure we only send the preset id
|
|
try {
|
|
await this.$store.dispatch('setUserData', {
|
|
userid: this.user.id,
|
|
key: 'language',
|
|
value: lang.code,
|
|
})
|
|
this.loading.languages = false
|
|
} catch (error) {
|
|
console.error(error)
|
|
}
|
|
return lang
|
|
},
|
|
|
|
/**
|
|
* Dispatch new welcome mail request
|
|
*/
|
|
sendWelcomeMail() {
|
|
this.loading.all = true
|
|
this.$store.dispatch('sendWelcomeMail', this.user.id)
|
|
.then(() => showSuccess(t('setting', 'Welcome mail sent!'), { timeout: 2000 }))
|
|
.finally(() => {
|
|
this.loading.all = false
|
|
})
|
|
},
|
|
|
|
async toggleEdit() {
|
|
this.editing = !this.editing
|
|
if (this.editing) {
|
|
await this.$nextTick()
|
|
this.$refs.displayNameField?.$refs?.inputField?.$refs?.input?.focus()
|
|
}
|
|
if (this.editedDisplayName !== this.user.displayname) {
|
|
this.editedDisplayName = this.user.displayname
|
|
} else if (this.editedMail !== this.user.email) {
|
|
this.editedMail = this.user.email ?? ''
|
|
}
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@import './shared/styles.scss';
|
|
|
|
.user-list__row {
|
|
@include row;
|
|
|
|
&:hover {
|
|
background-color: var(--color-background-hover);
|
|
|
|
.row__cell:not(.row__cell--actions) {
|
|
background-color: var(--color-background-hover);
|
|
}
|
|
}
|
|
|
|
// Limit width of select in fill cell
|
|
.select--fill {
|
|
max-width: calc(var(--cell-width-large) - (2 * var(--cell-padding)));
|
|
}
|
|
}
|
|
|
|
.row {
|
|
@include cell;
|
|
|
|
&__cell {
|
|
border-bottom: 1px solid var(--color-border);
|
|
|
|
:deep {
|
|
.v-select.select {
|
|
min-width: var(--cell-min-width);
|
|
}
|
|
}
|
|
}
|
|
|
|
&__progress {
|
|
margin-top: 4px;
|
|
|
|
&--warn {
|
|
&::-moz-progress-bar {
|
|
background: var(--color-warning) !important;
|
|
}
|
|
&::-webkit-progress-value {
|
|
background: var(--color-warning) !important;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|