Revert to old navbar layout, Improved provider creation

This commit is contained in:
Spitap
2023-04-18 15:44:08 +02:00
committed by Antoine Nguyen
parent 76c99094fd
commit 88c2ae7099
10 changed files with 222 additions and 443 deletions
@@ -23,5 +23,8 @@ export default {
},
checkProvider (params) {
return repository.post(`/${resource}/check_connection/`, params)
},
checkAssociatedDomain (params) {
return repository.post(`/${resource}/check_associated_domain/`, params)
}
}
@@ -19,7 +19,7 @@
<validation-provider
v-slot="{ errors }"
rules="required"
vid="newd_omain"
vid="new_domain"
>
<v-text-field
ref="newdomainField"
@@ -58,6 +58,8 @@
<script>
import providerApi from '@/api/imap_migration/providers'
export default {
props: ['value'],
data () {
@@ -70,6 +72,13 @@ export default {
}
},
methods: {
reset () {
this.domains = []
this.domain = {
initialdomain: '',
new_domain: ''
}
},
async validateForm () {
return await this.$refs.observer.validate()
},
@@ -78,13 +87,19 @@ export default {
},
async addDomain () {
if (await this.validateForm()) {
this.domains.push({
name: this.domain.initialdomain,
new_domain: this.domain.new_domain
providerApi.checkAssociatedDomain(this.domain).then(response => {
if (response.status === 200) {
this.domains.push({
name: this.domain.initialdomain,
new_domain: this.domain.new_domain
})
this.$emit('input', this.domains)
this.$refs.initialdomainField.reset()
this.$refs.newdomainField.reset()
}
}).catch(error => {
this.$refs.observer.setErrors(error.response.data)
})
this.$emit('input', this.domains)
this.$refs.initialdomainField.reset()
this.$refs.newdomainField.reset()
}
},
removeDomain (index) {
@@ -1,8 +1,10 @@
<template>
<creation-form
ref="form"
:title="'New provider'|translate"
:steps="steps"
:form-observer-getter="getObserver"
:form-getter="getForm"
:validate-object="validateProvider"
:summary-sections="summarySections"
@close="close"
@@ -88,16 +90,17 @@ export default {
},
close () {
this.initProvider()
this.$refs.form.resetForm()
this.$emit('close')
},
getForm (step) {
return this.$refs[`form_${step}`]
},
getObserver (step) {
return this.$refs[`form_${step}`].$refs.observer
},
validateProvider () {
},
performConnectionCheck () {
},
submit () {
const data = JSON.parse(JSON.stringify(this.provider))
@@ -93,6 +93,9 @@ export default {
}
},
methods: {
reset () {
this.form = {}
},
async checkConnection () {
if (await this.validateForm()) {
this.loading = true
@@ -123,6 +126,7 @@ export default {
},
mounted () {
this.form = { ...this.value }
console.error(this.value)
}
}
</script>
@@ -1,149 +0,0 @@
<template>
<v-list>
<template v-for="item in mainMenuItems">
<template v-if="displayMenuItem(item)">
<v-list-item
v-if="!item.children"
class="menu-item"
:to="item.to"
link
:key="item.title"
:exact="item.exact"
>
<v-list-item-icon v-if="item.icon">
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ item.text }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-group v-else
color="white"
:key="item.text"
:prepend-icon="item.icon"
no-action>
<template v-slot:activator>
<v-list-item-title>{{ item.text }}</v-list-item-title>
</template>
<template v-for="subitem in item.children">
<v-list-item v-if="!subitem.children"
:key="subitem.text"
:to="subitem.to"
link>
<v-list-item-content>
<v-list-item-title>{{ subitem.text }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-group v-else
:key="subitem.text"
:value="true"
no-action
sub-group>
<template v-slot:activator>
<v-list-item-title>{{ subitem.text }}</v-list-item-title>
</template>
<v-list-item v-for="subsubitem in subitem.children"
:key="subsubitem.text"
:to="subsubitem.to"
link>
<v-list-item-title>{{ subsubitem.text }}</v-list-item-title>
</v-list-item>
</v-list-group>
</template>
</v-list-group>
</template>
</template>
<settings-drawer
:parametersList="parametersList"
:extension="false"
/>
</v-list>
</template>
<script>
import { mapGetters } from 'vuex'
import SettingsDrawer from './SettingsDrawer.vue'
export default {
props: ['parametersList'],
components: {
SettingsDrawer
},
computed: {
...mapGetters({
authUser: 'auth/authUser'
})
},
data () {
return {
mainMenuItems: [
{
text: this.$gettext('Dashboard'),
to: { name: 'Dashboard' },
icon: 'mdi-view-dashboard-outline',
exact: true
},
{
text: this.$gettext('Domains'),
to: { name: 'DomainList' },
icon: 'mdi-domain',
roles: ['DomainAdmins', 'Resellers', 'SuperAdmins']
},
{
text: this.$gettext('Identities'),
to: { name: 'Identities' },
icon: 'mdi-account',
roles: ['DomainAdmins', 'Resellers', 'SuperAdmins']
},
{
text: this.$gettext('Alarms'),
to: { name: 'Alarms' },
icon: 'mdi-bell'
},
{
icon: 'mdi-history',
text: this.$gettext('Logs'),
roles: ['SuperAdmins', 'Resellers', 'DomainAdmins'],
children: [
{
text: this.$gettext('Audit trail'),
to: { name: 'AuditTrail' },
roles: ['SuperAdmins']
},
{
text: this.$gettext('Messages'),
to: { name: 'MessageLog' },
roles: ['DomainAdmins', 'Resellers', 'SuperAdmins']
}
]
},
{
icon: 'mdi-information',
text: this.$gettext('Information'),
roles: ['SuperAdmins'],
to: { name: 'Information' }
}
]
}
},
methods: {
displayMenuItem (item) {
return (item.roles === undefined || item.roles.indexOf(this.authUser.role) !== -1) && (item.condition === undefined || item.condition())
}
}
}
</script>
<style lang="scss" scoped>
.v-list-item {
&--active {
&::before {
opacity: 0;
}
background-color: #034bad;
color: white;
opacity: 1;
}
}
</style>
@@ -1,154 +0,0 @@
<template>
<v-list>
<template v-for="item in extensionItems">
<template v-if="!(item.activated)">
<v-list-item
class="menu-item unselectable"
:key="item.title"
:exact="item.exact"
color="blue-grey lighten-3"
@click="unselectable()"
>
<v-list-item-icon v-if="item.icon">
<v-icon color="#D3D3D3">{{ item.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ item.text }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</template>
<template v-else-if="displayMenuItem(item)">
<v-list-item
v-if="!item.children"
class="menu-item"
:to="item.to"
link
:key="item.title"
:exact="item.exact"
>
<v-list-item-icon v-if="item.icon">
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ item.text }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-group v-else
color="white"
:key="item.text"
:prepend-icon="item.icon"
no-action>
<template v-slot:activator>
<v-list-item-title>{{ item.text }}</v-list-item-title>
</template>
<template v-for="subitem in item.children">
<v-list-item v-if="!subitem.children"
:key="subitem.text"
:to="subitem.to"
link>
<v-list-item-content>
<v-list-item-title>{{ subitem.text }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-group v-else
:key="subitem.text"
:value="true"
no-action
sub-group>
<template v-slot:activator>
<v-list-item-title>{{ subitem.text }}</v-list-item-title>
</template>
<v-list-item v-for="subsubitem in subitem.children"
:key="subsubitem.text"
:to="subsubitem.to"
link>
<v-list-item-title>{{ subsubitem.text }}</v-list-item-title>
</v-list-item>
</v-list-group>
</template>
</v-list-group>
</template>
</template>
<settings-drawer
:parametersList="parametersList"
:extension="true"
/>
</v-list>
</template>
<script>
import { mapGetters } from 'vuex'
import { bus } from '@/main'
import SettingsDrawer from './SettingsDrawer.vue'
export default {
props: ['imap', 'parametersList'],
components: {
SettingsDrawer
},
computed: {
...mapGetters({
authUser: 'auth/authUser'
}),
extensionItems () {
return [
{
activated: this.imapMigration,
icon: 'mdi-email-sync-outline',
text: this.$gettext('IMAP Migration'),
roles: ['SuperAdmins', 'Resellers'],
children: [
{
text: this.$gettext('Email providers'),
to: { name: 'ProvdiersList' },
roles: ['SuperAdmins', 'Resellers']
},
{
text: this.$gettext('Migrations'),
to: { name: 'MigrationsList' },
roles: ['Resellers', 'SuperAdmins']
}
]
}
]
}
},
created () {
this.imapMigration = this.imap
bus.$on('imapSettingsChanged', this.imapMigrationStatus)
},
data () {
return {
imapMigration: false
}
},
methods: {
displayMenuItem (item) {
return (item.roles === undefined || item.roles.indexOf(this.authUser.role) !== -1) && (item.condition === undefined || item.condition())
},
unselectable () {
bus.$emit('notification', { msg: this.$gettext('Activate the extension in the settings'), type: 'warning' })
},
imapMigrationStatus (status) {
this.imapMigration = status
}
}
}
</script>
<style lang="scss" scoped>
.v-list-item {
&--active {
&::before {
opacity: 0;
}
background-color: #034bad;
color: white;
opacity: 1;
}
}
.unselectable {
color: lightgrey !important;
}
</style>
+158 -43
View File
@@ -21,33 +21,64 @@
<v-icon>mdi-chevron-left</v-icon>
</v-btn>
</div>
<v-layout
justify-center>
<v-chip v-if="page === 1" color="blue lighten-3" large>
<translate>Core</translate>
</v-chip>
<v-chip v-else-if="page === 2" color="blue lighten-3" large>
<translate>Extensions</translate>
</v-chip>
</v-layout>
<base-nav-bar
v-if="page === 1"
:parametersList="parametersList"
/>
<extension-nav-bar
v-else-if="page === 2"
:imap="imapMigration"
:parametersList="parametersList"
/>
<v-list>
<template v-for="item in menuItems">
<template v-if="displayMenuItem(item)">
<v-list-item
v-if="!item.children"
class="menu-item"
:to="item.to"
link
:key="item.title"
:exact="item.exact"
>
<v-list-item-icon v-if="item.icon">
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ item.text }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-group v-else
color="white"
:key="item.text"
:prepend-icon="item.icon"
no-action>
<template v-slot:activator>
<v-list-item-title>{{ item.text }}</v-list-item-title>
</template>
<template v-for="subitem in item.children">
<v-list-item v-if="!subitem.children"
:key="subitem.text"
:to="subitem.to"
link>
<v-list-item-content>
<v-list-item-title>{{ subitem.text }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-group v-else
:key="subitem.text"
:value="true"
no-action
sub-group>
<template v-slot:activator>
<v-list-item-title>{{ subitem.text }}</v-list-item-title>
</template>
<v-list-item v-for="subsubitem in subitem.children"
:key="subsubitem.text"
:to="subsubitem.to"
link>
<v-list-item-title>{{ subsubitem.text }}</v-list-item-title>
</v-list-item>
</v-list-group>
</template>
</v-list-group>
</template>
</template>
</v-list>
<template v-slot:append>
<v-pagination
v-if="!mini"
v-model="page"
:length="2"
circle
color="blue lighten-3"
>
</v-pagination>
<v-menu rounded="lg" offset-y top>
<template v-slot:activator="{ attrs, on }">
<div class="d-flex user-box justify-center align-center white--text py-2" v-bind="attrs" v-on="on">
@@ -82,16 +113,11 @@
</template>
<script>
import { mapGetters } from 'vuex'
import BaseNavBar from './BaseNavBar.vue'
import ExtensionNavBar from './ExtensionNavBar.vue'
import parameters from '@/api/parameters'
import { bus } from '@/main'
import { mapGetters } from 'vuex'
export default {
components: {
BaseNavBar,
ExtensionNavBar
},
computed: {
...mapGetters({
authUser: 'auth/authUser'
@@ -114,6 +140,12 @@ export default {
? `${this.authUser.first_name} ${this.authUser.last_name}`
: this.authUser.username
},
menuItems () {
if (this.$route.meta.layout === 'user') {
return this.userSettingsMenuItems
}
return this.mainMenuItems
},
mainColor () {
if (this.$route.meta.layout === 'user') {
return 'grey'
@@ -121,20 +153,81 @@ export default {
return 'primary'
}
},
created () {
parameters.getApplications().then(response => {
this.parametersList = response.data
})
parameters.getApplication('imap_migration').then(response => {
this.imapMigration = response.data.params.enabled_imapmigration
})
},
data () {
return {
parametersList: [],
imapMigration: false,
page: 1,
drawer: true,
mainMenuItems: [
{
text: this.$gettext('Dashboard'),
to: { name: 'Dashboard' },
icon: 'mdi-view-dashboard-outline',
exact: true
},
{
text: this.$gettext('Domains'),
to: { name: 'DomainList' },
icon: 'mdi-domain',
roles: ['DomainAdmins', 'Resellers', 'SuperAdmins']
},
{
text: this.$gettext('Identities'),
to: { name: 'Identities' },
icon: 'mdi-account',
roles: ['DomainAdmins', 'Resellers', 'SuperAdmins']
},
{
text: this.$gettext('Alarms'),
to: { name: 'Alarms' },
icon: 'mdi-bell'
},
{
icon: 'mdi-history',
text: this.$gettext('Logs'),
roles: ['SuperAdmins', 'Resellers', 'DomainAdmins'],
children: [
{
text: this.$gettext('Audit trail'),
to: { name: 'AuditTrail' },
roles: ['SuperAdmins']
},
{
text: this.$gettext('Messages'),
to: { name: 'MessageLog' },
roles: ['DomainAdmins', 'Resellers', 'SuperAdmins']
}
]
},
{
icon: 'mdi-email-sync-outline',
text: this.$gettext('IMAP Migration'),
roles: ['SuperAdmins', 'Resellers'],
children: [
{
text: this.$gettext('Email providers'),
to: { name: 'ProvdiersList' },
roles: ['SuperAdmins', 'Resellers']
},
{
text: this.$gettext('Migrations'),
to: { name: 'MigrationsList' },
roles: ['Resellers', 'SuperAdmins']
}
]
},
{
icon: 'mdi-cog',
text: this.$gettext('Settings'),
children: [],
roles: ['SuperAdmins']
},
{
icon: 'mdi-information',
text: this.$gettext('Information'),
roles: ['SuperAdmins'],
to: { name: 'Information' }
}
],
userSettingsMenuItems: [
{
text: this.$gettext('API'),
@@ -184,7 +277,29 @@ export default {
]
}
},
created () {
parameters.getApplications().then(response => {
response.data.forEach(item => {
this.mainMenuItems[6].children.push({
text: item.label,
to: { name: 'ParametersEdit', params: { app: item.name } }
})
})
})
parameters.getApplication('imap_migration').then(response => {
this.imapMigration = response.data.params.enabled_imapmigration
})
bus.$on('imapSettingsChanged', (status) => { this.imapMigration = status })
},
methods: {
displayMenuItem (item) {
const condition = (item.roles === undefined || item.roles.indexOf(this.authUser.role) !== -1) && (item.condition === undefined || item.condition()) && (item.activated !== false)
if (item.icon === 'mdi-email-sync-outline') {
// For imapMigration
return condition && this.imapMigration
}
return condition
},
logout () {
this.$store.dispatch('auth/logout').then(() => {
this.$router.push({ name: 'Login' })
@@ -1,86 +0,0 @@
<template>
<v-list-group
color="white"
:key="settings.text"
:prepend-icon="settings.icon"
no-action>
<template v-slot:activator>
<v-list-item-title>{{ settings.title }}</v-list-item-title>
</template>
<template v-for="subitem in settings.children">
<v-list-item v-if="!subitem.children"
:key="subitem.text"
:to="subitem.to"
link>
<v-list-item-content>
<v-list-item-title>{{ subitem.text }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-group v-else
:key="subitem.text"
:value="true"
no-action
sub-group>
<template v-slot:activator>
<v-list-item-title>{{ subitem.text }}</v-list-item-title>
</template>
<v-list-item v-for="subsubitem in subitem.children"
:key="subsubitem.text"
:to="subsubitem.to"
link>
<v-list-item-title>{{ subsubitem.text }}</v-list-item-title>
</v-list-item>
</v-list-group>
</template>
</v-list-group>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
props: ['parametersList', 'extension'],
computed: {
...mapGetters({
authUser: 'auth/authUser'
}),
settings () {
const menu = this.settingMenu
this.parametersList.forEach(item => {
if (item.is_extension === this.extension) {
menu.children.push({
text: item.label,
to: { name: 'ParametersEdit', params: { app: item.name } }
})
}
})
return menu
}
},
data () {
return {
settingMenu:
{
title: this.$gettext('Settings'),
icon: 'mdi-cog',
children: [],
roles: ['SuperAdmins']
}
}
}
}
</script>
<style lang="scss" scoped>
.v-list-item {
&--active {
&::before {
opacity: 0;
}
background-color: #034bad;
color: white;
opacity: 1;
}
}
</style>
@@ -1,6 +1,9 @@
"""APIv2 serializers."""
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from modoboa.admin.models.domain import Domain
class IMAPMigrationSettingsSerializer(serializers.Serializer):
@@ -28,3 +31,19 @@ class CheckProviderSerializer(serializers.Serializer):
address = serializers.CharField()
port = serializers.IntegerField(min_value=0, max_value=65535)
secured = serializers.BooleanField()
class CheckAssociatedDomainSerializer(serializers.Serializer):
"""A serializer for checking associated domains."""
initialdomain = serializers.CharField()
new_domain = serializers.CharField()
def validate(self, data):
if data["initialdomain"] != data["new_domain"]:
if not Domain.objects.filter(name=data["initialdomain"]):
raise ValidationError({"new_domain": [
_("You must create the local domain first.")]
})
return data
+10 -1
View File
@@ -8,7 +8,7 @@ from rest_framework import serializers, response
from rest_framework.decorators import action
from modoboa.imap_migration.api.v1 import viewsets as v1_viewsets
from .serializers import CheckProviderSerializer
from .serializers import CheckProviderSerializer, CheckAssociatedDomainSerializer
class EmailProviderViewSet(v1_viewsets.EmailProviderViewSet):
@@ -33,3 +33,12 @@ class EmailProviderViewSet(v1_viewsets.EmailProviderViewSet):
print(error)
return response.Response({'status': 'failed'}, 400)
return response.Response(status=200)
@action(methods=["post"], detail=False)
def check_associated_domain(self, request, **kwargs):
"""check that the associated domain is either the same as the provider,
or if a local domain already exists.
This is to prevent errros on setup."""
serializer = CheckAssociatedDomainSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
return response.Response(status=200)