mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-18 05:51:26 +02:00
Various fixes
This commit is contained in:
@@ -8,36 +8,37 @@
|
||||
<label class="m-label">{{ $gettext('Name') }}</label>
|
||||
<v-text-field
|
||||
v-model="form.name"
|
||||
dense
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
:rules="[rules.required]"
|
||||
/>
|
||||
<label class="m-label">{{ $gettext('Choose a domain') }}</label>
|
||||
<v-select
|
||||
v-model="form.target"
|
||||
:items="Object.values(domainsStore.domains)"
|
||||
:items="domains"
|
||||
item-title="name"
|
||||
item-value="pk"
|
||||
:rules="[rules.required]"
|
||||
single-line
|
||||
dense
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
/>
|
||||
<v-switch v-model="form.enabled" label="Enabled" color="primary" />
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn @click="emit('close')">
|
||||
<v-btn :loading="working" @click="emit('close')">
|
||||
{{ $gettext('Close') }}
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="domainAlias && domainAlias.pk"
|
||||
color="error"
|
||||
:loading="working"
|
||||
@click="deleteAlias"
|
||||
>
|
||||
{{ $gettext('Delete') }}
|
||||
</v-btn>
|
||||
<v-btn color="primary" type="submit">
|
||||
<v-btn color="primary" type="submit" :loading="working">
|
||||
{{ submitLabel }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
@@ -47,21 +48,23 @@
|
||||
|
||||
<script setup lang="js">
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
import { useDomainsStore } from '@/stores'
|
||||
import { computed, ref, onMounted } from 'vue'
|
||||
import { useBusStore } from '@/stores'
|
||||
import rules from '@/plugins/rules.js'
|
||||
import domainsApi from '@/api/domains'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const domainsStore = useDomainsStore()
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
const { displayNotification } = useBusStore()
|
||||
|
||||
const props = defineProps({
|
||||
domainAlias: { type: Object, default: () => null },
|
||||
})
|
||||
|
||||
const domains = ref([])
|
||||
const form = ref({ enabled: true })
|
||||
|
||||
const working = ref(false)
|
||||
const vFormRef = ref()
|
||||
|
||||
const submitLabel = computed(() => {
|
||||
@@ -74,8 +77,15 @@ const title = computed(() =>
|
||||
: $gettext('Add a new domain alias')
|
||||
)
|
||||
|
||||
function deleteAlias() {
|
||||
domainsStore.deleteAlias(props.domainAlias).then(() => emit('close'))
|
||||
async function deleteAlias() {
|
||||
working.value = true
|
||||
try {
|
||||
await domainsApi.deleteDomainAlias(props.domainAlias.pk)
|
||||
displayNotification({ msg: $gettext('Domain alias deleted') })
|
||||
emit('close')
|
||||
} finally {
|
||||
working.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
@@ -83,24 +93,26 @@ async function submit() {
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
working.value = true
|
||||
try {
|
||||
if (!form.value.pk) {
|
||||
// Create
|
||||
domainsStore.createAlias(form.value).then(() => {
|
||||
emit('close')
|
||||
})
|
||||
await domainsApi.createDomainAlias(form.value)
|
||||
displayNotification({ msg: $gettext('Domain alias created') })
|
||||
} else {
|
||||
// Update
|
||||
const data = JSON.parse(JSON.stringify(form.value))
|
||||
domainsStore.updateAlias(data).then(() => emit('close'))
|
||||
await domainsApi.updateDomainAlias(props.domainAlias.pk, data)
|
||||
displayNotification({ msg: $gettext('Domain alias updated') })
|
||||
}
|
||||
} catch (error) {
|
||||
//TODO
|
||||
console.error(error)
|
||||
emit('close')
|
||||
} finally {
|
||||
working.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
domainsApi.getDomains().then((resp) => {
|
||||
domains.value = resp.data
|
||||
})
|
||||
if (props.domainAlias) {
|
||||
form.value = JSON.parse(JSON.stringify(props.domainAlias))
|
||||
}
|
||||
|
||||
@@ -69,7 +69,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="js">
|
||||
import ParametersApi from '@/api/parameters.js'
|
||||
import domainsApi from '@/api/domains'
|
||||
import ParametersApi from '@/api/parameters'
|
||||
import CreationForm from '@/components/tools/CreationForm.vue'
|
||||
import DomainGeneralForm from './form_steps/DomainGeneralForm.vue'
|
||||
import DomainDNSForm from './form_steps/DomainDNSForm.vue'
|
||||
@@ -78,15 +79,14 @@ import DomainOptionsForm from './form_steps/DomainOptionsForm.vue'
|
||||
import DomainTransportForm from './form_steps/DomainTransportForm.vue'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useBusStore, useDomainsStore } from '@/stores'
|
||||
import { useBusStore } from '@/stores'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const busStore = useBusStore()
|
||||
const domainsStore = useDomainsStore()
|
||||
const router = useRouter()
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
const emit = defineEmits(['close', 'created'])
|
||||
|
||||
const defaultDomain = {
|
||||
name: '',
|
||||
@@ -123,6 +123,7 @@ onMounted(() => {
|
||||
const createAdmin = ref(false)
|
||||
|
||||
// Reference to steps components
|
||||
const form = ref()
|
||||
const general = ref()
|
||||
const dns = ref()
|
||||
const limitations = ref()
|
||||
@@ -298,7 +299,7 @@ function updateCreateAdmin(value) {
|
||||
createAdmin.value = value
|
||||
}
|
||||
|
||||
function submit() {
|
||||
async function submit() {
|
||||
const data = JSON.parse(JSON.stringify(domain.value))
|
||||
if (!createAdmin.value) {
|
||||
delete data.domain_admin
|
||||
@@ -311,11 +312,14 @@ function submit() {
|
||||
} else {
|
||||
delete data.transport
|
||||
}
|
||||
domainsStore.createDomain(data).then((resp) => {
|
||||
try {
|
||||
const resp = await domainsApi.createDomain(data)
|
||||
router.push({ name: 'DomainDetail', params: { id: resp.data.pk } })
|
||||
busStore.displayNotification({ msg: $gettext('Domain created') })
|
||||
close()
|
||||
})
|
||||
} finally {
|
||||
form.value.working = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<LoadingData v-if="!domainsStore.domainsLoaded || working" />
|
||||
<LoadingData v-if="working" />
|
||||
<div v-else>
|
||||
<v-expansion-panels v-model="panel" :multiple="formErrors">
|
||||
<v-expansion-panel eager value="generalForm">
|
||||
@@ -181,13 +181,13 @@ import parametersApi from '@/api/parameters'
|
||||
import ResourcesForm from '@/components/tools/ResourcesForm.vue'
|
||||
import { computed, ref, onMounted } from 'vue'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
import { useAuthStore, useDomainsStore } from '@/stores'
|
||||
import { useAuthStore } from '@/stores'
|
||||
import { useRouter } from 'vue-router'
|
||||
import domainsApi from '@/api/domains'
|
||||
|
||||
const router = useRouter()
|
||||
const { $gettext } = useGettext()
|
||||
const authStore = useAuthStore()
|
||||
const domainsStore = useDomainsStore()
|
||||
|
||||
const props = defineProps({ domain: { type: Object, default: null } })
|
||||
|
||||
@@ -253,8 +253,13 @@ async function save() {
|
||||
if (data.type === 'relaydomain') {
|
||||
transportForm.value.checkSettingTypes(data)
|
||||
}
|
||||
domainsStore.updateDomain(data).then(() => {
|
||||
router.go(-1)
|
||||
if (data.message_limit === '') {
|
||||
data.message_limit = null
|
||||
}
|
||||
await domainsApi.updateDomain(editedDomain.value.pk, data)
|
||||
router.push({
|
||||
name: 'DomainDetail',
|
||||
params: { id: editedDomain.value.pk },
|
||||
})
|
||||
} finally {
|
||||
working.value = false
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
v-model="selected"
|
||||
:expanded="expanded"
|
||||
:headers="domainHeaders"
|
||||
:items="Object.values(domains)"
|
||||
:items="domains"
|
||||
item-value="name"
|
||||
:search="search"
|
||||
show-select
|
||||
expand-on-click
|
||||
:loading="!domainsLoaded"
|
||||
:loading="loading"
|
||||
>
|
||||
<template #top>
|
||||
<v-toolbar color="white" flat>
|
||||
@@ -19,7 +19,6 @@
|
||||
:placeholder="$gettext('Search')"
|
||||
variant="outlined"
|
||||
single-line
|
||||
flat
|
||||
hide-details
|
||||
density="compact"
|
||||
class="flex-grow-0 w-33 mr-4"
|
||||
@@ -149,7 +148,7 @@
|
||||
<a href="#" class="mr-2" @click="editDomainAlias(item, alias)">{{
|
||||
alias.name
|
||||
}}</a>
|
||||
<v-chip variant="elevated" size="x-small" color="success"
|
||||
<v-chip variant="flat" size="x-small" color="success"
|
||||
>DNS OK</v-chip
|
||||
>
|
||||
</span>
|
||||
@@ -181,7 +180,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="js">
|
||||
import { useAuthStore, useBusStore, useDomainsStore } from '@/stores'
|
||||
import { useBusStore } from '@/stores'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { usePermissions } from '@/composables/permissions'
|
||||
@@ -190,18 +189,15 @@ import ConfirmDialog from '@/components/tools/ConfirmDialog.vue'
|
||||
import DNSStatusChip from './DNSStatusChip.vue'
|
||||
import DomainAliasForm from './DomainAliasForm.vue'
|
||||
import MenuItems from '@/components/tools/MenuItems.vue'
|
||||
import domainsApi from '@/api/domains'
|
||||
|
||||
import { computed, ref, onMounted } from 'vue'
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const router = useRouter()
|
||||
|
||||
const { canSetRole } = usePermissions()
|
||||
const busStore = useBusStore()
|
||||
const domainStore = useDomainsStore()
|
||||
const domains = computed(() => domainStore.domains)
|
||||
const domainsLoaded = computed(() => domainStore.domainsLoaded)
|
||||
const aliases = computed(() => domainStore.domainAliases)
|
||||
|
||||
const domainHeaders = [
|
||||
{ title: $gettext('Name'), key: 'name' },
|
||||
@@ -224,8 +220,11 @@ if (canSetRole.value) {
|
||||
})
|
||||
}
|
||||
|
||||
const aliases = ref({})
|
||||
const confirm = ref()
|
||||
const domains = ref([])
|
||||
const keepDomainFolder = ref(false)
|
||||
const loading = ref(false)
|
||||
const selectedDomain = ref(null)
|
||||
const selectedDomainAlias = ref(null)
|
||||
const showAdminList = ref(false)
|
||||
@@ -234,8 +233,15 @@ const search = ref('')
|
||||
const selected = ref([])
|
||||
const expanded = ref([])
|
||||
|
||||
function reloadDomains() {
|
||||
domainStore.getDomains()
|
||||
async function reloadDomains() {
|
||||
aliases.value = {}
|
||||
loading.value = true
|
||||
try {
|
||||
const resp = await domainsApi.getDomains()
|
||||
domains.value = resp.data
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function closeDomainAliasForm() {
|
||||
@@ -260,10 +266,10 @@ async function deleteDomain(domain) {
|
||||
return
|
||||
}
|
||||
const data = { keep_folder: keepDomainFolder.value }
|
||||
domainStore.deleteDomain({ id: domain.pk, data }).then(() => {
|
||||
busStore.displayNotification({ msg: $gettext('Domain deleted') })
|
||||
keepDomainFolder.value = false
|
||||
})
|
||||
await domainsApi.deleteDomain(domain.pk, data)
|
||||
reloadDomains()
|
||||
busStore.displayNotification({ msg: $gettext('Domain deleted') })
|
||||
keepDomainFolder.value = false
|
||||
}
|
||||
|
||||
function editDomain(domain) {
|
||||
@@ -282,7 +288,8 @@ function loadAliases(item, internalItem, isItemExpanded, toggleExpand) {
|
||||
toggleExpand(internalItem)
|
||||
return
|
||||
}
|
||||
domainStore.getAliases(item.name).then(() => {
|
||||
domainsApi.getDomainAliases(item.name).then((resp) => {
|
||||
aliases.value[item.name] = resp.data
|
||||
toggleExpand(internalItem)
|
||||
})
|
||||
}
|
||||
@@ -325,6 +332,10 @@ function getDomainMenuItems(domain) {
|
||||
onMounted(() => {
|
||||
reloadDomains()
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
reloadDomains,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
'Number of messages this domain can send per day. Leave empty for no limit.'
|
||||
)
|
||||
"
|
||||
type="number"
|
||||
persistent-hint
|
||||
variant="outlined"
|
||||
:rules="[rules.numericOrNull]"
|
||||
|
||||
@@ -70,15 +70,14 @@ import AccountMailboxForm from './form_steps/AccountMailboxForm.vue'
|
||||
import AccountAliasForm from './form_steps/AccountAliasForm.vue'
|
||||
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useBusStore, useDomainsStore, useIdentitiesStore } from '@/stores'
|
||||
import { ref, computed } from 'vue'
|
||||
import { useBusStore, useDomainsStore } from '@/stores'
|
||||
import { usePermissions } from '@/composables/permissions'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const { canSetRole } = usePermissions()
|
||||
const busStore = useBusStore()
|
||||
const domainsStore = useDomainsStore()
|
||||
const identitiesStore = useIdentitiesStore()
|
||||
const emit = defineEmits(['close', 'created'])
|
||||
|
||||
const needsMailbox = computed(
|
||||
@@ -196,6 +195,7 @@ const account = ref({
|
||||
})
|
||||
|
||||
// Form refs
|
||||
const form = ref()
|
||||
const role = ref()
|
||||
const identification = ref()
|
||||
const mailbox = ref()
|
||||
@@ -255,20 +255,21 @@ function close() {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
function submit() {
|
||||
async function submit() {
|
||||
const data = preparePayload({ ...account.value })
|
||||
data.password = data.new_password
|
||||
delete data.new_password
|
||||
delete data.password_confirmation
|
||||
identitiesStore.createIdentity('account', data).then(() => {
|
||||
try {
|
||||
await accountsApi.create(data)
|
||||
emit('created')
|
||||
close()
|
||||
})
|
||||
} finally {
|
||||
form.value.working = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
domainsStore.getDomains()
|
||||
})
|
||||
domainsStore.getDomains()
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<LoadingData v-if="!accountsStore.accountsLoaded || working" />
|
||||
<LoadingData v-if="working" />
|
||||
<div v-else>
|
||||
<v-expansion-panels v-model="panel" :multiple="formErrors">
|
||||
<v-expansion-panel v-if="canSetRole" eager value="roleForm">
|
||||
@@ -12,7 +12,7 @@
|
||||
<v-fade-transition leave-absolute>
|
||||
<span v-if="expanded"></span>
|
||||
<v-row v-else no-gutters style="width: 100%">
|
||||
<v-col cols="6">
|
||||
<v-col v-if="roleForm" cols="6">
|
||||
{{ roleForm.roleLabel }}
|
||||
</v-col>
|
||||
</v-row>
|
||||
@@ -30,7 +30,7 @@
|
||||
<v-col cols="4">
|
||||
{{ $gettext('Identification') }}
|
||||
</v-col>
|
||||
<v-col cols="8" class="text-secondary">
|
||||
<v-col cols="8" class="">
|
||||
<v-fade-transition leave-absolute>
|
||||
<span v-if="expanded"></span>
|
||||
<v-row v-else no-gutters style="width: 100%">
|
||||
@@ -68,7 +68,7 @@
|
||||
<v-col cols="4">
|
||||
{{ $gettext('Mailbox') }}
|
||||
</v-col>
|
||||
<v-col cols="8" class="text-secondary">
|
||||
<v-col cols="8" class="">
|
||||
<v-fade-transition leave-absolute>
|
||||
<span v-if="expanded"></span>
|
||||
<v-row v-else no-gutters style="width: 100%">
|
||||
@@ -115,14 +115,13 @@
|
||||
<v-col cols="4">
|
||||
{{ $gettext('Alias') }}
|
||||
</v-col>
|
||||
<v-col cols="8" class="text-secondary">
|
||||
<v-col cols="8" class="">
|
||||
<v-fade-transition leave-absolute>
|
||||
<span v-if="expanded"></span>
|
||||
<v-row v-else no-gutters style="width: 100%">
|
||||
<template v-if="editedAccount.aliases">
|
||||
<v-col cols="6">
|
||||
<div class="mr-2">
|
||||
{{ $gettext('Aliases:') }}
|
||||
{{ $gettext('Number of associated alias:') }}
|
||||
{{ editedAccount.aliases.length }}
|
||||
</div>
|
||||
@@ -143,7 +142,7 @@
|
||||
<v-col cols="4">
|
||||
{{ $gettext('Resources') }}
|
||||
</v-col>
|
||||
<v-col cols="8" class="text-secondary">
|
||||
<v-col cols="8" class="">
|
||||
<v-fade-transition leave-absolute>
|
||||
<span v-if="expanded"></span>
|
||||
<v-row v-else no-gutters style="width: 100%">
|
||||
@@ -155,14 +154,12 @@
|
||||
>
|
||||
<v-col cols="6">
|
||||
<div class="mr-2">
|
||||
{{ $gettext('Mailbox:') }}
|
||||
{{ $gettext('Number of allowed mailboxes:') }}
|
||||
{{ editedAccount.resources[0].max_value }}
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<div class="mr-2">
|
||||
{{ $gettext('Mailbox aliases:') }}
|
||||
{{ $gettext('Number of allowed mailbox aliases:') }}
|
||||
{{ editedAccount.resources[1].max_value }}
|
||||
</div>
|
||||
@@ -182,12 +179,13 @@
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
<div class="mt-4 d-flex justify-end">
|
||||
<v-btn :loading="working" @click="$router.go(-1)">
|
||||
<v-btn :loading="working" variant="outlined" @click="$router.go(-1)">
|
||||
{{ $gettext('Cancel') }}
|
||||
</v-btn>
|
||||
<v-btn
|
||||
class="ml-4"
|
||||
color="primary darken-1"
|
||||
variant="outlined"
|
||||
:loading="working"
|
||||
@click="save"
|
||||
>
|
||||
@@ -198,13 +196,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="js">
|
||||
import { useAccountsStore, useAuthStore, useIdentitiesStore } from '@/stores'
|
||||
import { useAuthStore } from '@/stores'
|
||||
import AccountGeneralForm from './form_steps/AccountGeneralForm.vue'
|
||||
import AccountMailboxForm from './form_steps/AccountMailboxForm.vue'
|
||||
import AccountAliasForm from './form_steps/AccountAliasForm.vue'
|
||||
import AccountRoleForm from './form_steps/AccountRoleForm.vue'
|
||||
import ResourcesForm from '@/components/tools/ResourcesForm.vue'
|
||||
import LoadingData from '@/components/tools/LoadingData.vue'
|
||||
import accountsApi from '@/api/accounts.js'
|
||||
import parametersApi from '@/api/parameters'
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
@@ -212,9 +211,7 @@ import { useRoute, useRouter } from 'vue-router'
|
||||
import { usePermissions } from '@/composables/permissions'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const accountsStore = useAccountsStore()
|
||||
const authStore = useAuthStore()
|
||||
const identitiesStore = useIdentitiesStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { canSetRole } = usePermissions()
|
||||
@@ -309,11 +306,10 @@ async function save() {
|
||||
data.role = 'SimpleUsers'
|
||||
}
|
||||
}
|
||||
identitiesStore.updateIdentity('account', data).then(() => {
|
||||
router.push({
|
||||
name: 'AccountDetail',
|
||||
params: { id: route.params.id },
|
||||
})
|
||||
await accountsApi.patch(editedAccount.value.pk, data)
|
||||
router.push({
|
||||
name: 'AccountDetail',
|
||||
params: { id: route.params.id },
|
||||
})
|
||||
} finally {
|
||||
working.value = false
|
||||
@@ -321,9 +317,10 @@ async function save() {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
accountsStore
|
||||
.getAccount(route.params.id)
|
||||
.then((response) => (editedAccount.value = { ...response.data }))
|
||||
accountsApi.get(route.params.id).then((response) => {
|
||||
editedAccount.value = response.data
|
||||
working.value = false
|
||||
})
|
||||
|
||||
if (canSetRole.value) {
|
||||
parametersApi.getGlobalApplication('limits').then((response) => {
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="js">
|
||||
import { useBusStore, useIdentitiesStore } from '@/stores'
|
||||
import { useBusStore } from '@/stores'
|
||||
import { ref, computed } from 'vue'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
import { useRouter } from 'vue-router'
|
||||
@@ -33,12 +33,12 @@ import CreationForm from '@/components/tools/CreationForm.vue'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const busStore = useBusStore()
|
||||
const identitiesStore = useIdentitiesStore()
|
||||
const router = useRouter()
|
||||
|
||||
const emit = defineEmits(['close', 'created'])
|
||||
|
||||
//Forms refs
|
||||
const form = ref()
|
||||
const general = ref()
|
||||
const recipients = ref()
|
||||
|
||||
@@ -139,13 +139,17 @@ function validateAlias() {
|
||||
return validation
|
||||
}
|
||||
|
||||
function submit() {
|
||||
async function submit() {
|
||||
if (alias.value.domain !== undefined) {
|
||||
delete alias.value.domain
|
||||
}
|
||||
identitiesStore.createIdentity('alias', alias.value).then(() => {
|
||||
|
||||
try {
|
||||
await aliasesApi.create(alias.value)
|
||||
emit('created')
|
||||
close()
|
||||
})
|
||||
} finally {
|
||||
form.value.working = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<LoadingData v-if="!aliasesStore.aliasesLoaded || working" />
|
||||
<LoadingData v-if="working" />
|
||||
<div v-else>
|
||||
<v-expansion-panels v-model="panel" :multiple="formErrors">
|
||||
<v-expansion-panel eager value="generalForm">
|
||||
@@ -81,24 +81,22 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="js">
|
||||
import { useIdentitiesStore, useAliasesStore } from '@/stores'
|
||||
import AliasGeneralForm from './form_steps/AliasGeneralForm.vue'
|
||||
import AliasRecipientForm from './form_steps/AliasRecipientForm'
|
||||
import LoadingData from '@/components/tools/LoadingData.vue'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import aliasesApi from '@/api/aliases'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const identitiesStore = useIdentitiesStore()
|
||||
const aliasesStore = useAliasesStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const editedAlias = ref({ pk: route.params.id })
|
||||
|
||||
const panel = ref(0)
|
||||
const working = ref(false)
|
||||
const working = ref(true)
|
||||
const formErrors = ref(false)
|
||||
|
||||
// refs
|
||||
@@ -126,20 +124,21 @@ async function save() {
|
||||
|
||||
working.value = true
|
||||
|
||||
identitiesStore
|
||||
.updateIdentity('alias', editedAlias.value)
|
||||
.then(() =>
|
||||
router.push({
|
||||
name: 'AliasDetail',
|
||||
params: { id: route.params.id },
|
||||
})
|
||||
)
|
||||
.finally(() => (working.value = false))
|
||||
try {
|
||||
await aliasesApi.patch(route.params.id, editedAlias.value)
|
||||
router.push({
|
||||
name: 'AliasDetail',
|
||||
params: { id: route.params.id },
|
||||
})
|
||||
} finally {
|
||||
working.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
aliasesStore.getAlias(route.params.id).then((response) => {
|
||||
editedAlias.value = { ...response.data }
|
||||
aliasesApi.get(route.params.id).then((response) => {
|
||||
editedAlias.value = response.data
|
||||
working.value = false
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
:headers="headers"
|
||||
:items="identities"
|
||||
:search="search"
|
||||
:loading="!identitiesLoaded"
|
||||
:loading="loading"
|
||||
item-value="identity"
|
||||
class="elevation-0"
|
||||
show-select
|
||||
@@ -77,7 +77,7 @@
|
||||
item.possible_actions.length !== 0
|
||||
"
|
||||
>
|
||||
<v-icon size="2.5em" color="blue">mdi-circle-small</v-icon>
|
||||
<v-icon size="large" color="blue">mdi-circle-small</v-icon>
|
||||
</template>
|
||||
<v-menu location="bottom">
|
||||
<template #activator="{ props }">
|
||||
@@ -107,13 +107,14 @@
|
||||
<script setup lang="js">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
import { useIdentitiesStore } from '@/stores'
|
||||
import MenuItems from '@/components/tools/MenuItems.vue'
|
||||
import ConfirmDialog from '@/components/tools/ConfirmDialog.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import accountsApi from '@/api/accounts'
|
||||
import aliasesApi from '@/api/aliases'
|
||||
import identitiesApi from '@/api/identities'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const identitiesStore = useIdentitiesStore()
|
||||
const router = useRouter()
|
||||
|
||||
const headers = ref([
|
||||
@@ -128,18 +129,23 @@ const headers = ref([
|
||||
},
|
||||
])
|
||||
|
||||
const identities = computed(() => identitiesStore.identities)
|
||||
const identitiesLoaded = computed(() => identitiesStore.identitiesLoaded)
|
||||
const identities = ref([])
|
||||
const loading = ref(false)
|
||||
const search = ref('')
|
||||
const selected = ref([])
|
||||
const keepAccountFolder = ref(false)
|
||||
const confirmAlias = ref()
|
||||
const confirmAccount = ref()
|
||||
|
||||
function fetchIdentities() {
|
||||
identitiesStore.getIdentities().then(() => {
|
||||
async function fetchIdentities() {
|
||||
loading.value = true
|
||||
try {
|
||||
const resp = await identitiesApi.getAll()
|
||||
identities.value = resp.data
|
||||
selected.value = []
|
||||
})
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function getMenuItems(item) {
|
||||
@@ -214,13 +220,14 @@ async function deleteAccount(account) {
|
||||
if (!result) {
|
||||
return
|
||||
}
|
||||
identitiesStore
|
||||
.deleteIdentity(account.type, account.pk, {
|
||||
keepdir: keepAccountFolder.value,
|
||||
})
|
||||
.then(() => {
|
||||
keepAccountFolder.value = false
|
||||
})
|
||||
|
||||
loading.value = false
|
||||
try {
|
||||
await accountsApi.delete(account.pk, { keepdir: keepAccountFolder.value })
|
||||
fetchIdentities()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function editAlias() {}
|
||||
@@ -240,12 +247,20 @@ async function deleteAlias(alias) {
|
||||
if (!result) {
|
||||
return
|
||||
}
|
||||
identitiesStore.deleteIdentity(alias.type, alias.pk)
|
||||
loading.value = false
|
||||
try {
|
||||
await aliasesApi.delete(alias.pk)
|
||||
fetchIdentities()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (identities.value.length < 1) {
|
||||
fetchIdentities()
|
||||
}
|
||||
fetchIdentities()
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
fetchIdentities,
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -53,8 +53,11 @@ import rules from '@/plugins/rules'
|
||||
const { $gettext } = useGettext()
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const props = defineProps({ modelValue: { type: Object, default: null } })
|
||||
|
||||
const domainsStore = useDomainsStore()
|
||||
|
||||
const domains = computed(() => domainsStore.domains)
|
||||
|
||||
const vFormRef = ref()
|
||||
const form = ref({
|
||||
mailbox: {},
|
||||
@@ -94,7 +97,9 @@ watch(
|
||||
const domainQuota = computed(() => {
|
||||
const email = form.value.mailbox.full_address
|
||||
if (email && email.indexOf('@') !== -1) {
|
||||
const domain = domainsStore.getDomainByName(email.split('@')[1])
|
||||
const domain = domains.value.find(
|
||||
(item) => item.name === email.split('@')[1]
|
||||
)
|
||||
if (domain) {
|
||||
return parseInt(domain.default_mailbox_quota)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<label class="m-label">{{ $gettext('Domain') }}</label>
|
||||
<v-select
|
||||
v-model="alias.domain"
|
||||
:items="Object.values(domains)"
|
||||
:items="domains"
|
||||
item-title="name"
|
||||
return-object
|
||||
variant="outlined"
|
||||
@@ -69,16 +69,14 @@
|
||||
import aliasesApi from '@/api/aliases'
|
||||
import EmailField from '@/components/tools/EmailField'
|
||||
import { ref, computed } from 'vue'
|
||||
import { useDomainsStore } from '@/stores'
|
||||
import rules from '@/plugins/rules'
|
||||
import domainsApi from '@/api/domains'
|
||||
|
||||
const domainsStore = useDomainsStore()
|
||||
const props = defineProps({ modelValue: { type: Object, default: null } })
|
||||
|
||||
const alias = computed(() => props.modelValue)
|
||||
|
||||
const domains = computed(() => domainsStore.domains)
|
||||
|
||||
const domains = ref([])
|
||||
const vFormRef = ref()
|
||||
|
||||
function copyAlias() {
|
||||
@@ -94,5 +92,9 @@ async function updateAddress() {
|
||||
})
|
||||
}
|
||||
|
||||
domainsApi.getDomains().then((resp) => {
|
||||
domains.value = resp.data
|
||||
})
|
||||
|
||||
defineExpose({ vFormRef })
|
||||
</script>
|
||||
|
||||
@@ -164,6 +164,10 @@ function create() {
|
||||
working.value = true
|
||||
emit('create')
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
working,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import { useBusStore } from './bus.store'
|
||||
|
||||
import accountsApi from '@/api/accounts'
|
||||
import gettext from '@/plugins/gettext'
|
||||
|
||||
export const useAccountsStore = defineStore('accounts', () => {
|
||||
const busStore = useBusStore()
|
||||
|
||||
const { $gettext } = gettext
|
||||
|
||||
const accounts = ref({})
|
||||
const accountsLoaded = ref(false)
|
||||
|
||||
async function $reset() {
|
||||
accounts.value = {}
|
||||
accountsLoaded.value = false
|
||||
}
|
||||
|
||||
async function getAll() {
|
||||
accounts.value = {}
|
||||
return accountsApi.getAll().then((response) => {
|
||||
for (const account of response.data) {
|
||||
accounts.value[account.pk] = account
|
||||
}
|
||||
return response
|
||||
})
|
||||
}
|
||||
|
||||
async function getAccount(pk) {
|
||||
accountsLoaded.value = false
|
||||
return accountsApi
|
||||
.get(pk)
|
||||
.then((response) => {
|
||||
accounts.value[pk] = response.data
|
||||
return response
|
||||
})
|
||||
.finally(() => {
|
||||
accountsLoaded.value = true
|
||||
})
|
||||
}
|
||||
|
||||
async function updateAccount(data) {
|
||||
return accountsApi.patch(data.pk, data).then((response) => {
|
||||
accounts.value[data.pk] = response.data
|
||||
busStore.displayNotification({ msg: $gettext('Account updated') })
|
||||
return response
|
||||
})
|
||||
}
|
||||
|
||||
async function createAccount(data) {
|
||||
return accountsApi.create(data).then((response) => {
|
||||
accounts.value[response.data.pk] = response.data
|
||||
busStore.displayNotification({ msg: $gettext('Account created') })
|
||||
return response
|
||||
})
|
||||
}
|
||||
|
||||
async function deleteAccount(pk, options) {
|
||||
accountsLoaded.value = false
|
||||
return accountsApi
|
||||
.delete(pk, options)
|
||||
.then((response) => {
|
||||
busStore.displayNotification({
|
||||
msg: $gettext('Account successfully deleted'),
|
||||
})
|
||||
return response
|
||||
})
|
||||
.finally(() => (accountsLoaded.value = true))
|
||||
}
|
||||
return {
|
||||
accounts,
|
||||
accountsLoaded,
|
||||
getAll,
|
||||
getAccount,
|
||||
updateAccount,
|
||||
createAccount,
|
||||
deleteAccount,
|
||||
$reset,
|
||||
}
|
||||
})
|
||||
@@ -1,94 +0,0 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import aliasesApi from '@/api/aliases'
|
||||
import { useBusStore } from './bus.store'
|
||||
|
||||
import gettext from '@/plugins/gettext'
|
||||
|
||||
export const useAliasesStore = defineStore('aliases', () => {
|
||||
const busStore = useBusStore()
|
||||
const { $gettext } = gettext
|
||||
|
||||
const aliases = ref({})
|
||||
const aliasesLoaded = ref(false)
|
||||
|
||||
async function $reset() {
|
||||
aliases.value = {}
|
||||
aliasesLoaded.value = false
|
||||
}
|
||||
|
||||
async function getAll() {
|
||||
aliasesLoaded.value = false
|
||||
return aliasesApi
|
||||
.getAll()
|
||||
.then((response) => {
|
||||
aliases.value = {}
|
||||
for (const account of response.data) {
|
||||
aliases.value[account.pk] = account
|
||||
}
|
||||
return response
|
||||
})
|
||||
.finally(() => (aliasesLoaded.value = true))
|
||||
}
|
||||
|
||||
async function getAlias(pk) {
|
||||
aliasesLoaded.value = false
|
||||
return aliasesApi
|
||||
.get(pk)
|
||||
.then((response) => {
|
||||
aliases.value[pk] = response.data
|
||||
return response
|
||||
})
|
||||
.finally(() => (aliasesLoaded.value = true))
|
||||
}
|
||||
|
||||
async function createAlias(data) {
|
||||
aliasesLoaded.value = false
|
||||
return aliasesApi
|
||||
.create(data)
|
||||
.then((response) => {
|
||||
aliases.value[response.data.pk] = response.data
|
||||
busStore.displayNotification({
|
||||
msg: $gettext('Alias successfully created'),
|
||||
})
|
||||
return response
|
||||
})
|
||||
.finally(() => (aliasesLoaded.value = true))
|
||||
}
|
||||
|
||||
async function deleteAlias(pk) {
|
||||
aliasesLoaded.value = false
|
||||
return aliasesApi
|
||||
.delete(pk)
|
||||
.then((response) => {
|
||||
busStore.displayNotification({
|
||||
msg: $gettext('Alias successfully deleted'),
|
||||
})
|
||||
return response
|
||||
})
|
||||
.finally(() => (aliasesLoaded.value = true))
|
||||
}
|
||||
|
||||
async function updateAlias(data) {
|
||||
aliasesLoaded.value = false
|
||||
return aliasesApi
|
||||
.patch(data.pk, data)
|
||||
.then((response) => {
|
||||
aliases.value[data.pk] = response.data
|
||||
busStore.displayNotification({ msg: $gettext('Alias updated') })
|
||||
return response
|
||||
})
|
||||
.finally(() => (aliasesLoaded.value = true))
|
||||
}
|
||||
|
||||
return {
|
||||
aliases,
|
||||
aliasesLoaded,
|
||||
getAll,
|
||||
getAlias,
|
||||
createAlias,
|
||||
deleteAlias,
|
||||
updateAlias,
|
||||
$reset,
|
||||
}
|
||||
})
|
||||
@@ -1,155 +1,31 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { useBusStore } from './bus.store'
|
||||
import { ref } from 'vue'
|
||||
import gettext from '@/plugins/gettext'
|
||||
|
||||
import domainApi from '@/api/domains'
|
||||
|
||||
export const useDomainsStore = defineStore('domains', () => {
|
||||
const busStore = useBusStore()
|
||||
const { $gettext } = gettext
|
||||
const domainsLoaded = ref(false)
|
||||
const domains = ref({})
|
||||
const domainAliases = ref({})
|
||||
|
||||
async function $reset() {
|
||||
domainsLoaded.value = false
|
||||
domains.value = {}
|
||||
domainAliases.value = {}
|
||||
}
|
||||
|
||||
async function getDomains() {
|
||||
domainsLoaded.value = false
|
||||
return domainApi.getDomains().then((response) => {
|
||||
domains.value = {}
|
||||
const _domains = {}
|
||||
response.data.forEach(function (domain) {
|
||||
_domains[domain.pk] = domain
|
||||
})
|
||||
domains.value = _domains
|
||||
domainsLoaded.value = true
|
||||
return response
|
||||
})
|
||||
}
|
||||
|
||||
async function getDomain(pk) {
|
||||
domainsLoaded.value = false
|
||||
return domainApi.getDomain(pk).then((response) => {
|
||||
domains.value[pk] = response.data
|
||||
domainsLoaded.value = true
|
||||
return response
|
||||
})
|
||||
}
|
||||
|
||||
function getDomainByName(name) {
|
||||
for (const domain of Object.values(domains.value)) {
|
||||
if (domain.name === name) {
|
||||
return domain
|
||||
}
|
||||
try {
|
||||
const resp = await domainApi.getDomains()
|
||||
domains.value = resp.data
|
||||
} finally {
|
||||
domainsLoaded.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function createDomain(data) {
|
||||
return domainApi.createDomain(data).then((response) => {
|
||||
domains.value[response.data.pk] = response.data
|
||||
return response
|
||||
})
|
||||
}
|
||||
|
||||
async function updateDomain(data) {
|
||||
return domainApi.updateDomain(data.pk, data).then((response) => {
|
||||
domains.value[data.pk] = response.data
|
||||
busStore.displayNotification({ msg: $gettext('Domain updated') })
|
||||
return response
|
||||
})
|
||||
}
|
||||
|
||||
async function deleteDomain({ id, data }) {
|
||||
return domainApi.deleteDomain(id, data).then((response) => {
|
||||
delete domains.value[id]
|
||||
return response
|
||||
})
|
||||
}
|
||||
|
||||
async function getAliases(name) {
|
||||
return domainApi.getDomainAliases(name).then((response) => {
|
||||
domainAliases.value[name] = response.data
|
||||
return response
|
||||
})
|
||||
}
|
||||
|
||||
async function _filterAlias(domainName, pk) {
|
||||
if (domainAliases.value[domainName] !== undefined) {
|
||||
return domainAliases.value[domainName].filter((al) => al.pk != pk)
|
||||
}
|
||||
}
|
||||
|
||||
async function createAlias(data) {
|
||||
return domainApi.createDomainAlias(data).then((response) => {
|
||||
busStore.displayNotification({
|
||||
msg: $gettext('Domain alias created'),
|
||||
})
|
||||
if (domains.value[data.target] != null) {
|
||||
const domain = domains.value[data.target]
|
||||
if (domainAliases.value[domain.name] !== undefined) {
|
||||
domainAliases.value[domain.name].push(response.data)
|
||||
} else {
|
||||
domainAliases.value[domain.name] = response.data
|
||||
}
|
||||
domain.domainalias_count++
|
||||
}
|
||||
return response
|
||||
})
|
||||
}
|
||||
|
||||
async function deleteAlias(alias) {
|
||||
const domain = domains.value[alias.target]
|
||||
const apiRequest = domainApi.deleteDomainAlias(alias.pk)
|
||||
const filter = _filterAlias(domain.name, alias.pk)
|
||||
Promise.all([filter, apiRequest]).then((results) => {
|
||||
const newList = results[0]
|
||||
busStore.displayNotification({
|
||||
msg: $gettext('Domain alias deleted'),
|
||||
})
|
||||
if (newList != null) {
|
||||
domainAliases.value[domain.name] = newList
|
||||
}
|
||||
domain.domainalias_count--
|
||||
})
|
||||
}
|
||||
|
||||
async function updateAlias(data) {
|
||||
const domain = domains.value[data.target]
|
||||
const filter = _filterAlias(domain.name, data.pk)
|
||||
const apiRequest = domainApi.updateDomainAlias(data.pk, data)
|
||||
Promise.all([filter, apiRequest]).then((results) => {
|
||||
busStore.displayNotification({
|
||||
msg: $gettext('Domain alias updated'),
|
||||
})
|
||||
const newList = results[0]
|
||||
const response = results[1]
|
||||
if (newList != null) {
|
||||
domainAliases.value[domain.name] = newList
|
||||
domainAliases.value[domain.name].push(response.data)
|
||||
} else {
|
||||
domainAliases.value[domain.name] = response.data
|
||||
}
|
||||
})
|
||||
}
|
||||
return {
|
||||
domainsLoaded,
|
||||
domains,
|
||||
domainAliases,
|
||||
getDomain,
|
||||
getDomainByName,
|
||||
getDomains,
|
||||
createDomain,
|
||||
updateDomain,
|
||||
deleteDomain,
|
||||
createAlias,
|
||||
getAliases,
|
||||
deleteAlias,
|
||||
updateAlias,
|
||||
$reset,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import { useAccountsStore } from './accounts.store'
|
||||
import { useAliasesStore } from './aliases.store'
|
||||
|
||||
import identitiesApi from '@/api/identities'
|
||||
|
||||
import gettext from '@/plugins/gettext'
|
||||
|
||||
export const useIdentitiesStore = defineStore('identities', () => {
|
||||
const aliasesStore = useAliasesStore()
|
||||
const accountsStore = useAccountsStore()
|
||||
|
||||
const { $gettext } = gettext
|
||||
|
||||
const identitiesLoaded = ref(false)
|
||||
const identities = ref([])
|
||||
|
||||
async function $reset() {
|
||||
identitiesLoaded.value = false
|
||||
identities.value = []
|
||||
}
|
||||
|
||||
async function _getIndexByPk(type, pk) {
|
||||
for (let i = 0; i < identities.value.length; i++) {
|
||||
if (identities.value[i].pk === pk && identities.value[i].type === type) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function _deleteIdByPkAndType(type, pk) {
|
||||
_getIndexByPk(type, pk).then((index) => {
|
||||
if (index !== null) {
|
||||
identities.value.splice(index, 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function getIdentities() {
|
||||
identitiesLoaded.value = false
|
||||
return identitiesApi.getAll().then((response) => {
|
||||
identities.value = response.data
|
||||
identitiesLoaded.value = true
|
||||
return response
|
||||
})
|
||||
}
|
||||
|
||||
async function _accountToId(data, update = false) {
|
||||
const account = data
|
||||
let indexSearcher = null
|
||||
if (update) {
|
||||
indexSearcher = _getIndexByPk('account', data.pk)
|
||||
}
|
||||
let name_or_rcpt = '---'
|
||||
if (account.first_name !== '') {
|
||||
name_or_rcpt = account.first_name
|
||||
}
|
||||
if (account.last_name !== '') {
|
||||
name_or_rcpt =
|
||||
name_or_rcpt === ''
|
||||
? account.last_name
|
||||
: `${name_or_rcpt} ${account.last_name}`
|
||||
}
|
||||
const newIdentity = {
|
||||
pk: account.pk,
|
||||
type: account.type,
|
||||
identity: account.username,
|
||||
name_or_rcpt: name_or_rcpt,
|
||||
tags: [
|
||||
{ name: 'account', label: $gettext('account'), type: 'idt' },
|
||||
{
|
||||
name: account.role,
|
||||
label: $gettext(account.role),
|
||||
type: 'grp',
|
||||
},
|
||||
],
|
||||
possible_actions: [],
|
||||
}
|
||||
|
||||
if (update) {
|
||||
indexSearcher.then((index) => {
|
||||
identities.value[index] = newIdentity
|
||||
return
|
||||
})
|
||||
}
|
||||
identities.value.push(newIdentity)
|
||||
}
|
||||
|
||||
async function _aliasToId(data, update = false) {
|
||||
let indexSearcher = null
|
||||
const alias = data
|
||||
if (update) {
|
||||
indexSearcher = _getIndexByPk('alias', data.pk)
|
||||
}
|
||||
const name_or_rcpt =
|
||||
alias.recipients.length > 1
|
||||
? `${alias.recipients[0]}, ...`
|
||||
: alias.recipients[0]
|
||||
const newIdentity = {
|
||||
pk: alias.pk,
|
||||
type: alias.type,
|
||||
identity: alias.address,
|
||||
name_or_rcpt: name_or_rcpt,
|
||||
tags: [{ name: 'alias', label: $gettext('alias'), type: 'idt' }],
|
||||
possible_actions: [],
|
||||
}
|
||||
if (update) {
|
||||
indexSearcher.then((index) => {
|
||||
identities.value[index] = newIdentity
|
||||
return
|
||||
})
|
||||
}
|
||||
identities.value.push(newIdentity)
|
||||
}
|
||||
|
||||
async function createIdentity(type, data) {
|
||||
identitiesLoaded.value = false
|
||||
if (type === 'alias') {
|
||||
return aliasesStore.createAlias(data).then((response) => {
|
||||
_aliasToId(response.data)
|
||||
identitiesLoaded.value = true
|
||||
return response
|
||||
})
|
||||
} else if (type === 'account') {
|
||||
return accountsStore.createAccount(data).then((response) => {
|
||||
_accountToId(response.data)
|
||||
identitiesLoaded.value = true
|
||||
return response
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function updateIdentity(type, data) {
|
||||
identitiesLoaded.value = false
|
||||
if (type === 'alias') {
|
||||
return aliasesStore
|
||||
.updateAlias(data)
|
||||
.then((response) => {
|
||||
_aliasToId(response.data, true)
|
||||
return response
|
||||
})
|
||||
.finally(() => (identitiesLoaded.value = true))
|
||||
} else if (type === 'account') {
|
||||
return accountsStore
|
||||
.updateAccount(data)
|
||||
.then((response) => {
|
||||
_accountToId(response.data, true)
|
||||
return response
|
||||
})
|
||||
.finally(() => (identitiesLoaded.value = true))
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteIdentity(type, pk, options) {
|
||||
identitiesLoaded.value = false
|
||||
if (type === 'alias') {
|
||||
return aliasesStore.deleteAlias(pk).then((response) => {
|
||||
_deleteIdByPkAndType(type, pk)
|
||||
identitiesLoaded.value = true
|
||||
return response
|
||||
})
|
||||
} else if (type === 'account') {
|
||||
return accountsStore.deleteAccount(pk, options).then((response) => {
|
||||
_deleteIdByPkAndType(type, pk)
|
||||
identitiesLoaded.value = true
|
||||
return response
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
identities,
|
||||
getIdentities,
|
||||
identitiesLoaded,
|
||||
deleteIdentity,
|
||||
createIdentity,
|
||||
updateIdentity,
|
||||
$reset,
|
||||
}
|
||||
})
|
||||
@@ -5,9 +5,6 @@ export * from './auth.store'
|
||||
export * from './bus.store'
|
||||
export * from './layout.store'
|
||||
export * from './domains.store'
|
||||
export * from './accounts.store'
|
||||
export * from './aliases.store'
|
||||
export * from './identities.store'
|
||||
export * from './parameters.store'
|
||||
export * from './providers.store'
|
||||
export * from './contacts.store'
|
||||
|
||||
@@ -7,20 +7,19 @@
|
||||
|
||||
<script setup lang="js">
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
import { useDomainsStore } from '@/stores'
|
||||
import DomainEditForm from '@/components/admin/domains/DomainEditForm'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ref } from 'vue'
|
||||
import domainsApi from '@/api/domains'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const domainsStore = useDomainsStore()
|
||||
const route = useRoute()
|
||||
|
||||
const domain = ref({ pk: route.params.id })
|
||||
const domain = ref({})
|
||||
|
||||
domainsStore
|
||||
.getDomain(route.params.id)
|
||||
.then((response) => (domain.value = response.data))
|
||||
domainsApi.getDomain(route.params.id).then((resp) => {
|
||||
domain.value = resp.data
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<LoadingData v-if="!domain" />
|
||||
<div v-else>
|
||||
<v-toolbar flat>
|
||||
<v-toolbar-title>
|
||||
{{ $gettext('Domain') }} {{ domain.name }}
|
||||
@@ -83,31 +84,28 @@ import DomainAdminList from '@/components/admin/domains/DomainAdminList.vue'
|
||||
import DmarcAligmentChart from '@/components/admin/dmarc/DmarcAligmentChart.vue'
|
||||
import DNSDetail from '@/components/admin/domains/DNSDetail.vue'
|
||||
import DomainSummary from '@/components/admin/domains/DomainSummary.vue'
|
||||
import { useDomainsStore } from '@/stores'
|
||||
import LoadingData from '@/components/tools/LoadingData.vue'
|
||||
import ResourcesForm from '@/components/tools/ResourcesForm.vue'
|
||||
import TimeSerieChart from '@/components/tools/TimeSerieChart.vue'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
import { ref, computed } from 'vue'
|
||||
import { ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import domainsApi from '@/api/domains'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const route = useRoute()
|
||||
const domainsStore = useDomainsStore()
|
||||
|
||||
const domain = computed(() => {
|
||||
if (domainsStore.domains[route.params.id] !== undefined) {
|
||||
return domainsStore.domains[route.params.id]
|
||||
}
|
||||
refreshDomain()
|
||||
return { pk: route.params.id }
|
||||
})
|
||||
|
||||
const domain = ref(null)
|
||||
const limitsConfig = ref({})
|
||||
const tab = ref(null)
|
||||
|
||||
function refreshDomain() {
|
||||
domainsStore.getDomain(route.params.id)
|
||||
domainsApi.getDomain(route.params.id).then((resp) => {
|
||||
domain.value = resp.data
|
||||
})
|
||||
}
|
||||
|
||||
refreshDomain()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<v-toolbar-title>{{ $gettext('Domains') }}</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
|
||||
<DomainList>
|
||||
<DomainList ref="domainList">
|
||||
<template #extraActions>
|
||||
<v-menu v-if="canAddDomain" offset-y>
|
||||
<template #activator="{ props }">
|
||||
@@ -49,7 +49,7 @@
|
||||
<DomainCreationForm @close="showDomainWizard = false" />
|
||||
</v-dialog>
|
||||
<v-dialog v-model="showAliasForm" persistent max-width="800px">
|
||||
<DomainAliasForm @close="showAliasForm = false" />
|
||||
<DomainAliasForm @close="closeAliasForm()" />
|
||||
</v-dialog>
|
||||
<v-dialog v-model="showImportForm" persistent max-width="800px">
|
||||
<ImportForm
|
||||
@@ -90,6 +90,7 @@ import DomainList from '@/components/admin/domains/DomainList'
|
||||
import ImportForm from '@/components/tools/ImportForm'
|
||||
import { importExportMixin } from '@/mixins/importExport'
|
||||
|
||||
const domainList = ref()
|
||||
const showAliasForm = ref(false)
|
||||
const showDomainWizard = ref(false)
|
||||
const showImportForm = ref(false)
|
||||
@@ -110,6 +111,11 @@ function exportDomains() {
|
||||
function importDomains(data) {
|
||||
importContent(domainApi, data, importForm, $gettext)
|
||||
}
|
||||
|
||||
const closeAliasForm = () => {
|
||||
showAliasForm.value = false
|
||||
domainList.value.reloadDomains()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
<script setup lang="js">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useAccountsStore } from '@/stores'
|
||||
import { usePermissions } from '@/composables/permissions'
|
||||
import accountsApi from '@/api/accounts'
|
||||
import parametersApi from '@/api/parameters'
|
||||
import AccountAliases from '@/components/admin/identities/AccountAliases.vue'
|
||||
import AccountSenderAddresses from '@/components/admin/identities/AccountSenderAddresses.vue'
|
||||
@@ -52,27 +52,24 @@ import DomainAdminDomains from '@/components/admin/identities/DomainAdminDomains
|
||||
import ResourcesView from '@/components/tools/ResourcesView.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const accountsStore = useAccountsStore()
|
||||
const { canSetRole } = usePermissions()
|
||||
|
||||
const account = computed(() => {
|
||||
if (accountsStore.accounts[route.params.id] !== undefined) {
|
||||
return accountsStore.accounts[route.params.id]
|
||||
}
|
||||
refreshAccount()
|
||||
return { pk: route.params.id }
|
||||
})
|
||||
const account = ref({ pk: route.params.id })
|
||||
const limitsConfig = ref({})
|
||||
|
||||
function refreshAccount() {
|
||||
accountsApi.get(route.params.id).then((resp) => {
|
||||
account.value = resp.data
|
||||
})
|
||||
}
|
||||
|
||||
if (canSetRole.value) {
|
||||
parametersApi.getGlobalApplication('limits').then((resp) => {
|
||||
limitsConfig.value = resp.data
|
||||
})
|
||||
}
|
||||
|
||||
function refreshAccount() {
|
||||
accountsStore.getAccount(route.params.id)
|
||||
}
|
||||
refreshAccount()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -24,25 +24,22 @@
|
||||
import AliasRecipientsSummary from '@/components/admin/identities/AliasRecipientsSummary.vue'
|
||||
import AliasSummary from '@/components/admin/identities/AliasSummary.vue'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
import { computed } from 'vue'
|
||||
import { useAliasesStore } from '@/stores'
|
||||
import { ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import aliasesApi from '@/api/aliases'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const aliasesStore = useAliasesStore()
|
||||
const route = useRoute()
|
||||
|
||||
const alias = computed(() => {
|
||||
if (aliasesStore.aliases[route.params.id] !== undefined) {
|
||||
return aliasesStore.aliases[route.params.id]
|
||||
}
|
||||
refreshAlias()
|
||||
return { pk: route.params.id }
|
||||
})
|
||||
const alias = ref({ pk: route.params.id })
|
||||
|
||||
function refreshAlias() {
|
||||
aliasesStore.getAlias(route.params.id)
|
||||
aliasesApi.get(route.params.id).then((resp) => {
|
||||
alias.value = resp.data
|
||||
})
|
||||
}
|
||||
|
||||
refreshAlias()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<v-toolbar-title>{{ $gettext('Identities') }}</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
|
||||
<IdentityList>
|
||||
<IdentityList ref="idList">
|
||||
<template #extraActions>
|
||||
<v-menu offset-y>
|
||||
<template #activator="{ props }">
|
||||
@@ -50,11 +50,17 @@
|
||||
scrollable
|
||||
z-index="10"
|
||||
>
|
||||
<AliasCreationForm @close="showAliasCreationWizard = false" />
|
||||
<AliasCreationForm
|
||||
@created="idList.fetchIdentities()"
|
||||
@close="showAliasCreationWizard = false"
|
||||
/>
|
||||
</v-dialog>
|
||||
|
||||
<v-dialog v-model="showCreationWizard" fullscreen scrollable z-index="10">
|
||||
<AccountCreationForm @close="showCreationWizard = false" />
|
||||
<AccountCreationForm
|
||||
@created="idList.fetchIdentities()"
|
||||
@close="showCreationWizard = false"
|
||||
/>
|
||||
</v-dialog>
|
||||
|
||||
<v-dialog v-model="showImportForm" persistent max-width="800px">
|
||||
@@ -115,6 +121,7 @@ const showCreationWizard = ref(false)
|
||||
const showAliasCreationWizard = ref(false)
|
||||
const showImportForm = ref(false)
|
||||
const importForm = ref()
|
||||
const idList = ref()
|
||||
|
||||
function exportIdentities() {
|
||||
identitiesApi.exportAll().then((resp) => {
|
||||
|
||||
@@ -149,31 +149,19 @@ class PermissionsTestCase(ModoAPITestCase):
|
||||
{"name": "mailboxes", "max_value": 0},
|
||||
{"name": "mailbox_aliases", "max_value": 0},
|
||||
]
|
||||
self.values["domains"] = ["test.com", "test2.com"]
|
||||
response = self.client.put(
|
||||
reverse("v2:account-detail", args=[self.user.id]),
|
||||
self.values,
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn(
|
||||
self.user, models.Domain.objects.get(name="test.com").admins.all()
|
||||
)
|
||||
self.assertIn(
|
||||
self.user, models.Domain.objects.get(name="test2.com").admins.all()
|
||||
)
|
||||
for name in ["test.com", "test2.com"]:
|
||||
domain = models.Domain.objects.get(name=name)
|
||||
domain.add_admin(self.user)
|
||||
|
||||
self.values["domains"].remove("test2.com")
|
||||
response = self.client.put(
|
||||
reverse("v2:account-detail", args=[self.user.id]),
|
||||
self.values,
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn(
|
||||
self.user, models.Domain.objects.get(name="test.com").admins.all()
|
||||
)
|
||||
self.assertNotIn(
|
||||
self.assertIn(
|
||||
self.user, models.Domain.objects.get(name="test2.com").admins.all()
|
||||
)
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class AddressBookViewSet(viewsets.GenericViewSet):
|
||||
"""Return default user address book."""
|
||||
abook = request.user.addressbook_set.first()
|
||||
if not abook:
|
||||
return response.Response(status_code=404)
|
||||
return response.Response(status=404)
|
||||
serializer = serializers.AddressBookSerializer(abook)
|
||||
return response.Response(serializer.data)
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
tonio@homer.148277:1749538104
|
||||
@@ -165,29 +165,30 @@ class AccountViewSet(core_v1_viewsets.AccountViewSet):
|
||||
"url": "/admin",
|
||||
}
|
||||
)
|
||||
apps += [
|
||||
{
|
||||
"name": "calendar",
|
||||
"label": _("Calendars"),
|
||||
"icon": "mdi-calendar",
|
||||
"description": _("Calendar"),
|
||||
"url": "/user/calendars",
|
||||
},
|
||||
{
|
||||
"name": "contacts",
|
||||
"label": _("Contacts"),
|
||||
"icon": "mdi-contacts-outline",
|
||||
"description": _("Address book"),
|
||||
"url": "/user/contacts",
|
||||
},
|
||||
{
|
||||
"name": "webmail",
|
||||
"label": _("Webmail"),
|
||||
"icon": "mdi-at",
|
||||
"description": _("Webmail"),
|
||||
"url": "/user/webmail",
|
||||
},
|
||||
]
|
||||
if hasattr(request.user, "mailbox"):
|
||||
apps += [
|
||||
{
|
||||
"name": "calendar",
|
||||
"label": _("Calendars"),
|
||||
"icon": "mdi-calendar",
|
||||
"description": _("Calendar"),
|
||||
"url": "/user/calendars",
|
||||
},
|
||||
{
|
||||
"name": "contacts",
|
||||
"label": _("Contacts"),
|
||||
"icon": "mdi-contacts-outline",
|
||||
"description": _("Address book"),
|
||||
"url": "/user/contacts",
|
||||
},
|
||||
{
|
||||
"name": "webmail",
|
||||
"label": _("Webmail"),
|
||||
"icon": "mdi-at",
|
||||
"description": _("Webmail"),
|
||||
"url": "/user/webmail",
|
||||
},
|
||||
]
|
||||
apps += exts_pool.get_available_apps()
|
||||
serializer = serializers.ModoboaApplicationSerializer(apps, many=True)
|
||||
return response.Response(serializer.data)
|
||||
|
||||
Reference in New Issue
Block a user