mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
355 lines
11 KiB
Vue
355 lines
11 KiB
Vue
<template>
|
|
<view-base :queries="queries" @queries-response="onQueriesResponse" skeleton="card-form-skeleton">
|
|
<card-form
|
|
:title="$t('user_username_edit', { name })" icon="user"
|
|
:validation="$v" :server-error="serverError"
|
|
@submit.prevent="onSubmit"
|
|
>
|
|
<!-- USERNAME (disabled) -->
|
|
<form-field v-bind="fields.username" />
|
|
|
|
<!-- USER FULLNAME (FIXME quite a mess, but will be removed)-->
|
|
<form-field v-bind="fields.fullname" :validation="$v.form.fullname">
|
|
<template #default="{ self }">
|
|
<b-input-group>
|
|
<template v-for="name_ in ['firstname', 'lastname']">
|
|
<b-input-group-prepend :key="name_ + 'prepend'">
|
|
<b-input-group-text :id="name_ + '-label'" tag="label">
|
|
{{ self[name_].label }}
|
|
</b-input-group-text>
|
|
</b-input-group-prepend>
|
|
|
|
<input-item
|
|
v-bind="self[name_]" v-model.trim="form.fullname[name_]" :key="name_ + 'input'"
|
|
:name="self[name_].id" :aria-labelledby="name_ + '-label'"
|
|
:state="$v.form.fullname[name_].$invalid && $v.form.fullname.$anyDirty ? false : null"
|
|
/>
|
|
</template>
|
|
</b-input-group>
|
|
</template>
|
|
</form-field>
|
|
<hr>
|
|
|
|
<!-- USER EMAIL -->
|
|
<form-field v-bind="fields.mail" :validation="$v.form.mail">
|
|
<template #default="{ self }">
|
|
<adress-input-select v-bind="self" v-model="form.mail" />
|
|
</template>
|
|
</form-field>
|
|
|
|
<!-- MAILBOX QUOTA -->
|
|
<form-field v-bind="fields.mailbox_quota" :validation="$v.form.mailbox_quota">
|
|
<template #default="{ self }">
|
|
<b-input-group append="M">
|
|
<input-item v-bind="self" v-model="form.mailbox_quota" />
|
|
</b-input-group>
|
|
</template>
|
|
</form-field>
|
|
<hr>
|
|
|
|
<!-- MAIL ALIASES -->
|
|
<form-field :label="$t('user_emailaliases')" id="mail-aliases">
|
|
<div
|
|
v-for="(mail, i) in form.mail_aliases" :key="i"
|
|
class="mail-list"
|
|
>
|
|
<form-field
|
|
v-bind="fields.mail_aliases"
|
|
:id="'mail_aliases' + i"
|
|
:validation="$v.form.mail_aliases.$each[i]"
|
|
>
|
|
<template #default="{ self }">
|
|
<adress-input-select v-bind="self" v-model="form.mail_aliases[i]" />
|
|
</template>
|
|
</form-field>
|
|
|
|
<b-button variant="danger" @click="removeEmailField('aliases', i)">
|
|
<icon :title="$t('delete')" iname="trash-o" />
|
|
<span class="sr-only">{{ $t('delete') }}</span>
|
|
</b-button>
|
|
</div>
|
|
|
|
<b-button variant="success" @click="addEmailField('aliases')">
|
|
<icon iname="plus" /> {{ $t('user_emailaliases_add') }}
|
|
</b-button>
|
|
</form-field>
|
|
|
|
<!-- MAIL FORWARD -->
|
|
<form-field :label="$t('user_emailforward')" id="mail-forward">
|
|
<div
|
|
v-for="(mail, i) in form.mail_forward" :key="i"
|
|
class="mail-list"
|
|
>
|
|
<form-field
|
|
v-bind="fields.mail_forward" v-model="form.mail_forward[i]"
|
|
:id="'mail-forward' + i"
|
|
:validation="$v.form.mail_forward.$each[i]"
|
|
/>
|
|
|
|
<b-button variant="danger" @click="removeEmailField('forward', i)">
|
|
<icon :title="$t('delete')" iname="trash-o" />
|
|
<span class="sr-only">{{ $t('delete') }}</span>
|
|
</b-button>
|
|
</div>
|
|
|
|
<b-button variant="success" @click="addEmailField('forward')">
|
|
<icon iname="plus" /> {{ $t('user_emailforward_add') }}
|
|
</b-button>
|
|
</form-field>
|
|
<hr>
|
|
|
|
<!-- USER PASSWORD -->
|
|
<form-field v-bind="fields.change_password" v-model="form.change_password" :validation="$v.form.change_password" />
|
|
|
|
<!-- USER PASSWORD CONFIRMATION -->
|
|
<form-field v-bind="fields.confirmation" v-model="form.confirmation" :validation="$v.form.confirmation" />
|
|
</card-form>
|
|
</view-base>
|
|
</template>
|
|
|
|
<script>
|
|
import { mapGetters } from 'vuex'
|
|
import { validationMixin } from 'vuelidate'
|
|
|
|
import api from '@/api'
|
|
import { arrayDiff } from '@/helpers/commons'
|
|
import { sizeToM, adressToFormValue, formatFormData } from '@/helpers/yunohostArguments'
|
|
import {
|
|
name, required, minLength, emailLocalPart, sameAs, integer, minValue, emailForward
|
|
} from '@/helpers/validators'
|
|
|
|
import AdressInputSelect from '@/components/AdressInputSelect'
|
|
|
|
|
|
export default {
|
|
name: 'UserEdit',
|
|
|
|
props: {
|
|
name: { type: String, required: true }
|
|
},
|
|
|
|
data () {
|
|
return {
|
|
queries: [
|
|
['GET', { uri: 'users', param: this.name, storeKey: 'users_details' }],
|
|
['GET', { uri: 'domains/main', storeKey: 'main_domain' }],
|
|
['GET', { uri: 'domains' }]
|
|
],
|
|
|
|
form: {
|
|
fullname: { firstname: '', lastname: '' },
|
|
mail: { localPart: '', separator: '@', domain: '' },
|
|
mailbox_quota: '',
|
|
mail_aliases: [],
|
|
mail_forward: [],
|
|
change_password: '',
|
|
confirmation: ''
|
|
},
|
|
|
|
serverError: '',
|
|
|
|
fields: {
|
|
username: {
|
|
label: this.$i18n.t('user_username'),
|
|
value: this.name,
|
|
props: { id: 'username', disabled: true }
|
|
},
|
|
|
|
fullname: {
|
|
label: this.$i18n.t('user_fullname'),
|
|
id: 'fullname',
|
|
props: {
|
|
firstname: {
|
|
id: 'firstname',
|
|
label: this.$i18n.t('common.firstname'),
|
|
placeholder: this.$i18n.t('placeholder.firstname')
|
|
},
|
|
lastname: {
|
|
id: 'lastname',
|
|
label: this.$i18n.t('common.lastname'),
|
|
placeholder: this.$i18n.t('placeholder.lastname')
|
|
}
|
|
}
|
|
},
|
|
|
|
mail: {
|
|
label: this.$i18n.t('user_email'),
|
|
props: { id: 'mail', choices: [] }
|
|
},
|
|
|
|
mailbox_quota: {
|
|
label: this.$i18n.t('user_mailbox_quota'),
|
|
description: this.$i18n.t('mailbox_quota_description'),
|
|
example: this.$i18n.t('mailbox_quota_example'),
|
|
props: {
|
|
id: 'mailbox-quota',
|
|
placeholder: this.$i18n.t('mailbox_quota_placeholder')
|
|
}
|
|
},
|
|
|
|
mail_aliases: {
|
|
props: {
|
|
placeholder: this.$i18n.t('placeholder.username'),
|
|
choices: []
|
|
}
|
|
},
|
|
|
|
mail_forward: {
|
|
props: {
|
|
placeholder: this.$i18n.t('user_new_forward'),
|
|
type: 'email'
|
|
}
|
|
},
|
|
|
|
change_password: {
|
|
label: this.$i18n.t('password'),
|
|
description: this.$i18n.t('good_practices_about_user_password'),
|
|
descriptionVariant: 'warning',
|
|
props: { id: 'change_password', type: 'password', placeholder: '••••••••', autocomplete: 'new-password' }
|
|
},
|
|
|
|
confirmation: {
|
|
label: this.$i18n.t('password_confirmation'),
|
|
props: { id: 'confirmation', type: 'password', placeholder: '••••••••', autocomplete: 'new-password' }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
computed: mapGetters(['user', 'domainsAsChoices', 'mainDomain']),
|
|
|
|
validations: {
|
|
form: {
|
|
fullname: {
|
|
firstname: { required, name },
|
|
lastname: { required, name }
|
|
},
|
|
mail: {
|
|
localPart: { required, email: emailLocalPart }
|
|
},
|
|
mailbox_quota: { integer, minValue: minValue(0) },
|
|
mail_aliases: {
|
|
$each: {
|
|
localPart: { required, email: emailLocalPart }
|
|
}
|
|
},
|
|
mail_forward: {
|
|
$each: { required, emailForward }
|
|
},
|
|
change_password: { passwordLenght: minLength(8) },
|
|
confirmation: { passwordMatch: sameAs('change_password') }
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
onQueriesResponse (user) {
|
|
this.fields.mail.props.choices = this.domainsAsChoices
|
|
this.fields.mail_aliases.props.choices = this.domainsAsChoices
|
|
|
|
this.form.fullname = {
|
|
// Copy value to avoid refering to the stored user data
|
|
firstname: user.firstname.valueOf(),
|
|
lastname: user.lastname.valueOf()
|
|
}
|
|
this.form.mail = adressToFormValue(user.mail)
|
|
if (user['mail-aliases']) {
|
|
this.form.mail_aliases = user['mail-aliases'].map(mail => adressToFormValue(mail))
|
|
}
|
|
if (user['mail-forward']) {
|
|
this.form.mail_forward = user['mail-forward'].slice() // Copy value
|
|
}
|
|
// mailbox-quota could be 'No quota' or 'Pas de quota'...
|
|
if (parseInt(user['mailbox-quota'].limit) > 0) {
|
|
this.form.mailbox_quota = sizeToM(user['mailbox-quota'].limit)
|
|
} else {
|
|
this.form.mailbox_quota = ''
|
|
}
|
|
},
|
|
|
|
onSubmit () {
|
|
const formData = formatFormData(this.form, { flatten: true })
|
|
const user = this.user(this.name)
|
|
const data = {}
|
|
if (!Object.prototype.hasOwnProperty.call(formData, 'mailbox_quota')) {
|
|
formData.mailbox_quota = ''
|
|
}
|
|
|
|
for (const key of ['mail_aliases', 'mail_forward']) {
|
|
const dashedKey = key.replace('_', '-')
|
|
const newKey = key.replace('_', '').replace('es', '')
|
|
const addDiff = arrayDiff(formData[key], user[dashedKey])
|
|
const rmDiff = arrayDiff(user[dashedKey], formData[key])
|
|
if (addDiff.length) data['add_' + newKey] = addDiff
|
|
if (rmDiff.length) data['remove_' + newKey] = rmDiff
|
|
}
|
|
|
|
for (const key in formData) {
|
|
if (key === 'mailbox_quota') {
|
|
const quota = parseInt(formData[key]) > 0 ? formData[key] + 'M' : 'No quota'
|
|
if (parseInt(quota) !== parseInt(user['mailbox-quota'].limit)) {
|
|
data[key] = quota === 'No quota' ? '0' : quota
|
|
}
|
|
} else if (!key.includes('mail_') && formData[key] !== user[key]) {
|
|
data[key] = formData[key]
|
|
}
|
|
}
|
|
|
|
if (Object.keys(data).length === 0) {
|
|
this.serverError = this.$i18n.t('error_modify_something')
|
|
return
|
|
}
|
|
|
|
api.put(
|
|
{ uri: 'users', param: this.name, storeKey: 'users_details' },
|
|
data,
|
|
{ key: 'users.update', name: this.name }
|
|
).then(() => {
|
|
this.$router.push({ name: 'user-info', param: { name: this.name } })
|
|
}).catch(err => {
|
|
if (err.name !== 'APIBadRequestError') throw err
|
|
this.serverError = err.message
|
|
})
|
|
},
|
|
|
|
addEmailField (type) {
|
|
this.form['mail_' + type].push(type === 'aliases'
|
|
? { localPart: '', separator: '@', domain: this.mainDomain }
|
|
: ''
|
|
)
|
|
// Focus last input after rendering update
|
|
this.$nextTick(() => {
|
|
const inputs = this.$el.querySelectorAll(`#mail-${type} input`)
|
|
inputs[inputs.length - 1].focus()
|
|
})
|
|
},
|
|
|
|
removeEmailField (type, index) {
|
|
this.form['mail_' + type].splice(index, 1)
|
|
}
|
|
},
|
|
|
|
mixins: [validationMixin],
|
|
components: { AdressInputSelect }
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
#lastname-label {
|
|
border-left: 0;
|
|
}
|
|
|
|
.mail-list {
|
|
display: flex;
|
|
justify-items: stretch;
|
|
|
|
.form-group {
|
|
margin-bottom: .5rem;
|
|
width: 100%;
|
|
}
|
|
|
|
.btn-danger {
|
|
align-self: flex-start;
|
|
margin-left: .5rem;
|
|
}
|
|
}
|
|
</style>
|