mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
Account creation form improvements.
This commit is contained in:
@@ -6,6 +6,9 @@ export default {
|
||||
getAll ({ domain, role }) {
|
||||
return repository.get(`/${resource}/`, { params: { domain, role } })
|
||||
},
|
||||
getRandomPassword () {
|
||||
return repository.get(`${resource}/random_password`)
|
||||
},
|
||||
validate (data) {
|
||||
return repository.post(`${resource}/validate/`, data)
|
||||
},
|
||||
|
||||
@@ -104,7 +104,26 @@
|
||||
<creation-summary
|
||||
:sections="summarySections"
|
||||
@modify-step="val => step = val"
|
||||
/>
|
||||
>
|
||||
<template v-slot:item.random_password="{ item }">
|
||||
<v-col cols="12" class="highligth white--text">
|
||||
<v-row>
|
||||
<v-col><span>{{ item.key }}</span></v-col>
|
||||
<v-col class="text-right" v-if="item.type === 'yesno'">
|
||||
{{ item.value|yesno }}
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col class="text-right py-1">
|
||||
{{ account.password }}
|
||||
<v-btn icon color="white" :title="'Copy to clipboard'|translate" @click="copyPassword">
|
||||
<v-icon>mdi-clipboard-multiple-outline</v-icon>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
</template>
|
||||
</creation-summary>
|
||||
<div class="d-flex justify-center mt-8">
|
||||
<v-btn
|
||||
color="primary"
|
||||
@@ -156,6 +175,7 @@ export default {
|
||||
{ key: this.$gettext('First name'), value: this.account.first_name },
|
||||
{ key: this.$gettext('Last name'), value: this.account.last_name },
|
||||
{
|
||||
name: 'random_password',
|
||||
key: this.$gettext('Random password'),
|
||||
value: this.account.random_password,
|
||||
type: 'yesno'
|
||||
@@ -204,6 +224,11 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
copyPassword () {
|
||||
navigator.clipboard.writeText(this.account.password).then(() => {
|
||||
bus.$emit('notification', { msg: this.$gettext('Password copied to clipboard') })
|
||||
})
|
||||
},
|
||||
getInitialForm () {
|
||||
return {
|
||||
aliases: [],
|
||||
@@ -211,7 +236,8 @@ export default {
|
||||
role: 'SimpleUsers',
|
||||
mailbox: {
|
||||
use_domain_quota: true
|
||||
}
|
||||
},
|
||||
random_password: true
|
||||
}
|
||||
},
|
||||
async close (withConfirm) {
|
||||
@@ -242,6 +268,11 @@ export default {
|
||||
this.$refs[`form_${current}`].$refs.observer.setErrors(error.response.data)
|
||||
return
|
||||
}
|
||||
if (next === 5 && this.account.random_password) {
|
||||
accounts.getRandomPassword().then(resp => {
|
||||
this.$set(this.account, 'password', resp.data.password)
|
||||
})
|
||||
}
|
||||
this.step = next
|
||||
},
|
||||
submit () {
|
||||
@@ -286,4 +317,7 @@ export default {
|
||||
.edit-link {
|
||||
text-decoration: none;
|
||||
}
|
||||
.highligth {
|
||||
background-color: #515D78;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,23 +5,26 @@
|
||||
rules="required"
|
||||
vid="username"
|
||||
>
|
||||
<label class="m-label">{{ $gettext('Username') }}</label>
|
||||
<email-field
|
||||
v-model="account.username"
|
||||
:label="'Username' | translate"
|
||||
:error-messages="errors"
|
||||
:placeholder="usernamePlaceholder"
|
||||
outlined
|
||||
class="mt-4"
|
||||
dense
|
||||
/>
|
||||
</validation-provider>
|
||||
<label class="m-label">{{ $gettext('First name') }}</label>
|
||||
<v-text-field
|
||||
v-model="account.first_name"
|
||||
:label="'First name' | translate"
|
||||
outlined
|
||||
dense
|
||||
/>
|
||||
<label class="m-label">{{ $gettext('Last name') }}</label>
|
||||
<v-text-field
|
||||
v-model="account.last_name"
|
||||
:label="'Last name' | translate"
|
||||
outlined
|
||||
dense
|
||||
/>
|
||||
<v-switch
|
||||
v-model="account.random_password"
|
||||
@@ -32,13 +35,15 @@
|
||||
v-if="!account.random_password"
|
||||
v-slot="{ errors }"
|
||||
rules="required"
|
||||
vid="password"
|
||||
>
|
||||
<label class="m-label">{{ $gettext('Password') }}</label>
|
||||
<v-text-field
|
||||
v-model="account.password"
|
||||
:label="'Password' | translate"
|
||||
:error-messages="errors"
|
||||
outlined
|
||||
type="password"
|
||||
dense
|
||||
/>
|
||||
</validation-provider>
|
||||
<validation-provider
|
||||
@@ -46,12 +51,13 @@
|
||||
v-slot="{ errors }"
|
||||
rules="required"
|
||||
>
|
||||
<label class="m-label">{{ $gettext('Confirmation') }}</label>
|
||||
<v-text-field
|
||||
v-model="account.password_confirmation"
|
||||
:label="'Confirmation' | translate"
|
||||
:error-messages="errors"
|
||||
outlined
|
||||
type="password"
|
||||
dense
|
||||
/>
|
||||
</validation-provider>
|
||||
<v-switch
|
||||
@@ -70,6 +76,14 @@ export default {
|
||||
EmailField
|
||||
},
|
||||
props: ['account'],
|
||||
computed: {
|
||||
usernamePlaceholder () {
|
||||
if (this.account.role === 'SimpleUsers') {
|
||||
return this.$gettext('Enter an email address')
|
||||
}
|
||||
return this.$gettext('Enter a simple username or an email address')
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
|
||||
@@ -10,29 +10,38 @@
|
||||
:error-messages="errors"
|
||||
:disabled="account.role === 'SimpleUsers'"
|
||||
outlined
|
||||
dense
|
||||
/>
|
||||
</validation-provider>
|
||||
<div class="d-flex">
|
||||
<label class="m-label">{{ $gettext('Quota') }}</label>
|
||||
<v-switch
|
||||
v-model="account.mailbox.use_domain_quota"
|
||||
:label="useDomainQuotaLabel"
|
||||
/>
|
||||
<validation-provider
|
||||
v-if="!account.mailbox.use_domain_quota"
|
||||
v-slot="{ errors }"
|
||||
rules=""
|
||||
vid="quota"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="account.mailbox.quota"
|
||||
:label="'Quota' | translate"
|
||||
:placeholder="'Ex: 10MB. Leave empty for no limit'|translate"
|
||||
:hint="'Quota for this mailbox, can be expressed in KB, MB (default) or GB. Define a custom value or use domain\'s default one. Leave empty to define an unlimited value (not allowed for domain administrators).' | translate"
|
||||
persistent-hint
|
||||
class="mr-4 quota"
|
||||
:disabled="account.mailbox.use_domain_quota"
|
||||
outlined
|
||||
:error-messages="errors"
|
||||
dense
|
||||
/>
|
||||
<v-switch
|
||||
v-model="account.mailbox.use_domain_quota"
|
||||
:label="useDomainQuotaLabel"
|
||||
/>
|
||||
</div>
|
||||
</validation-provider>
|
||||
<label class="m-label">{{ $gettext('Daily message sending limit') }}</label>
|
||||
<v-text-field
|
||||
v-model="account.mailbox.message_limit"
|
||||
:label="'Message sending limit' | translate"
|
||||
:placeholder="'Leave empty for no limit' | translate"
|
||||
:hint="'Number of messages this mailbox can send per day. Leave empty for no limit.' | translate"
|
||||
persistent-hint
|
||||
outlined
|
||||
dense
|
||||
/>
|
||||
</validation-observer>
|
||||
</template>
|
||||
|
||||
@@ -4,12 +4,23 @@
|
||||
v-slot="{ errors }"
|
||||
rules="required"
|
||||
>
|
||||
<choice-field v-model="account.role"
|
||||
:label="'Role' | translate"
|
||||
:choices="accountRoles"
|
||||
:error-messages="errors"
|
||||
:choices-per-line="2"
|
||||
/>
|
||||
<v-row>
|
||||
<v-col cols="7">
|
||||
<choice-field v-model="account.role"
|
||||
:label="'Role' | translate"
|
||||
:choices="accountRoles"
|
||||
:error-messages="errors"
|
||||
:choices-per-line="2"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="5">
|
||||
<v-alert text color="primary" class="mt-11 ml-4 rounded-lg">
|
||||
<h3 class="headline">{{ roleLabel }}</h3>
|
||||
<p class="mt-4">{{ roleHelp }}</p>
|
||||
<v-icon color="white" class="float-right" large>mdi-help-circle-outline</v-icon>
|
||||
</v-alert>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</validation-provider>
|
||||
</validation-observer>
|
||||
</template>
|
||||
@@ -22,24 +33,36 @@ export default {
|
||||
ChoiceField
|
||||
},
|
||||
props: ['account'],
|
||||
computed: {
|
||||
roleLabel () {
|
||||
return this.accountRoles.find(role => role.value === this.account.role).label
|
||||
},
|
||||
roleHelp () {
|
||||
return this.accountRoles.find(role => role.value === this.account.role).help
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
accountRoles: [
|
||||
{
|
||||
label: this.$gettext('Domain administrator'),
|
||||
value: 'DomainAdmins'
|
||||
value: 'DomainAdmins',
|
||||
help: this.$gettext('A user with privileges on one or more domain. He will be allowed to administer mailboxes and he can also have a mailbox.')
|
||||
},
|
||||
{
|
||||
label: this.$gettext('Reseller'),
|
||||
value: 'Resellers'
|
||||
value: 'Resellers',
|
||||
help: this.$gettext('help')
|
||||
},
|
||||
{
|
||||
label: this.$gettext('Simple user'),
|
||||
value: 'SimpleUsers'
|
||||
value: 'SimpleUsers',
|
||||
help: this.$gettext('A user with no privileges but with a mailbox. He will be allowed to use all end-user features: webmail, calendar, contacts, filters.')
|
||||
},
|
||||
{
|
||||
label: this.$gettext('Super administrator'),
|
||||
value: 'SuperAdmins'
|
||||
value: 'SuperAdmins',
|
||||
help: this.$gettext('A user with all privileges, can do anything. By default, such a user does not have a mailbox so he can\'t access end-user features.')
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="text-subtitle-1 grey--text text--darken-1 mb-4" :class="{ 'label--disabled': disabled }">
|
||||
<label class="v-label">{{ label }}</label>
|
||||
<label class="m-label">{{ label }}</label>
|
||||
</div>
|
||||
<div class="d-flex" v-for="(lineChoices, index1) in formatedChoices" :key="index1">
|
||||
<div v-for="(choice, index2) in lineChoices"
|
||||
|
||||
@@ -10,12 +10,14 @@
|
||||
dense
|
||||
>
|
||||
<template v-if="item.value !== undefined">
|
||||
<v-col><span class="grey--text">{{ item.key }}</span></v-col>
|
||||
<v-col class="text-right" v-if="item.type === 'yesno'">{{ item.value|yesno }}</v-col>
|
||||
<v-col class="text-right" v-else-if="item.type === 'list'">
|
||||
{{ displayList(item.value) }}
|
||||
</v-col>
|
||||
<v-col class="text-right" v-else>{{ item.value }}</v-col>
|
||||
<slot :name="`item.${item.name}`" v-bind:item="item" v-bind:displayList="displayList">
|
||||
<v-col><span class="grey--text">{{ item.key }}</span></v-col>
|
||||
<v-col class="text-right" v-if="item.type === 'yesno'">{{ item.value|yesno }}</v-col>
|
||||
<v-col class="text-right" v-else-if="item.type === 'list'">
|
||||
{{ displayList(item.value) }}
|
||||
</v-col>
|
||||
<v-col class="text-right" v-else>{{ item.value }}</v-col>
|
||||
</slot>
|
||||
</template>
|
||||
</v-row>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@ import App from './App.vue'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import vuetify from './plugins/vuetify'
|
||||
import sassStyles from './styles/global.scss' // eslint-disable-line no-unused-vars
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
|
||||
@@ -5,10 +5,14 @@ Vue.use(Vuetify)
|
||||
|
||||
export default new Vuetify({
|
||||
theme: {
|
||||
options: {
|
||||
customProperties: true
|
||||
},
|
||||
themes: {
|
||||
light: {
|
||||
primary: '#046BF8',
|
||||
secondary: '#F18429'
|
||||
secondary: '#F18429',
|
||||
label: '#616161'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
.m-label {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
color: var(--v-label-base);
|
||||
}
|
||||
@@ -305,7 +305,13 @@ class WritableAccountSerializer(AccountSerializer):
|
||||
raise serializers.ValidationError({
|
||||
"username": _("Must be equal to mailbox full_address")
|
||||
})
|
||||
if not self.partial and not data.get("random_password"):
|
||||
condition = (
|
||||
not data.get("random_password") and (
|
||||
data.get("password") or
|
||||
not self.partial
|
||||
)
|
||||
)
|
||||
if condition:
|
||||
password = data.get("password")
|
||||
if password:
|
||||
try:
|
||||
|
||||
@@ -160,9 +160,11 @@ class WritableAccountSerializer(v1_serializers.WritableAccountSerializer):
|
||||
)
|
||||
|
||||
class Meta(v1_serializers.WritableAccountSerializer.Meta):
|
||||
fields = v1_serializers.WritableAccountSerializer.Meta.fields + (
|
||||
"aliases",
|
||||
)
|
||||
fields = tuple(
|
||||
field
|
||||
for field in v1_serializers.WritableAccountSerializer.Meta.fields
|
||||
if field != "random_password"
|
||||
) + ("aliases", )
|
||||
|
||||
def validate_aliases(self, value):
|
||||
"""Check if required domains are locals and user can access them."""
|
||||
|
||||
@@ -161,6 +161,12 @@ class AccountViewSet(v1_viewsets.AccountViewSet):
|
||||
serializer.is_valid(raise_exception=True)
|
||||
return response.Response(status=204)
|
||||
|
||||
@action(methods=["get"], detail=False)
|
||||
def random_password(self, request, **kwargs):
|
||||
"""Generate a random password."""
|
||||
password = lib.make_password()
|
||||
return response.Response({"password": password})
|
||||
|
||||
|
||||
class IdentityViewSet(viewsets.ViewSet):
|
||||
"""Viewset for identities."""
|
||||
|
||||
Reference in New Issue
Block a user