diff --git a/.github/workflows/modoboa.yml b/.github/workflows/modoboa.yml index f1a5c351e..b7c23bffc 100644 --- a/.github/workflows/modoboa.yml +++ b/.github/workflows/modoboa.yml @@ -67,7 +67,7 @@ jobs: --health-timeout 5s --health-retries 5 openldap: - image: docker.pkg.github.com/modoboa/docker-openldap/docker-openldap + image: ghcr.io/modoboa/docker-openldap/docker-openldap:latest ports: - 389/tcp env: diff --git a/docker-compose.yml b/docker-compose.yml index 721a41ebe..fbd6168a7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -87,8 +87,6 @@ services: - redis volumes: - .:/code - networks: - - default api-unsecured: container_name: modoboa_api_unsecured diff --git a/frontend/src/api/webmail.js b/frontend/src/api/webmail.js index 8f1191fa4..d4f3ab1fe 100644 --- a/frontend/src/api/webmail.js +++ b/frontend/src/api/webmail.js @@ -108,8 +108,12 @@ export default { getComposeSession(uid) { return repository.get(`/webmail/compose-sessions/${uid}/`) }, - createComposeSession() { - return repository.post('/webmail/compose-sessions/') + createComposeSession(fromDraftMessageUid) { + const data = {} + if (fromDraftMessageUid) { + data.from_draft_message = fromDraftMessageUid + } + return repository.post('/webmail/compose-sessions/', data) }, getAllowedSenders() { return repository.get('/webmail/compose-sessions/allowed_senders/') @@ -123,6 +127,12 @@ export default { } ) }, + saveComposeSession(sessionUid, data) { + return repository.post( + `/webmail/compose-sessions/${sessionUid}/save/`, + data + ) + }, removeAttachment(sessionUid, name) { return repository.delete( `/webmail/compose-sessions/${sessionUid}/attachments/${name}/` diff --git a/frontend/src/components/webmail/ComposeEmailForm.vue b/frontend/src/components/webmail/ComposeEmailForm.vue index 19b4e6a35..cc483b04f 100644 --- a/frontend/src/components/webmail/ComposeEmailForm.vue +++ b/frontend/src/components/webmail/ComposeEmailForm.vue @@ -40,6 +40,14 @@ :text="$gettext('Attachments') + ` (${attachmentCount})`" @click="openAttachmentsDialog" /> +
@@ -266,33 +274,43 @@ const initForm = () => { } } +const prepareMessage = () => { + const result = { ...form.value } + + if (result.to?.length) { + const to = [] + for (const rcpt of result.to) { + to.push(typeof rcpt === 'string' ? rcpt : rcpt.emails[0].address) + } + result.to = to + } + if (result.cc?.length) { + const cc = [] + for (const rcpt of result.cc) { + cc.push(typeof rcpt === 'string' ? rcpt : rcpt.emails[0].address) + } + result.cc = cc + } + if (result.bcc?.length) { + const bcc = [] + for (const rcpt of result.bcc) { + bcc.push(typeof rcpt === 'string' ? rcpt : rcpt.emails[0].address) + } + result.bcc = bcc + } + if (route.query.mailid) { + result.mailid = route.query.mailid + } + return result +} + const submit = async (reload) => { const { valid } = await formRef.value.validate() if (!valid) { return } working.value = true - const body = { ...form.value } - - const to = [] - for (const rcpt of body.to) { - to.push(typeof rcpt === 'string' ? rcpt : rcpt.emails[0].address) - } - body.to = to - if (body.cc?.length) { - const cc = [] - for (const rcpt of body.cc) { - cc.push(typeof rcpt === 'string' ? rcpt : rcpt.emails[0].address) - } - body.cc = cc - } - if (body.bcc?.length) { - const bcc = [] - for (const rcpt of body.bcc) { - bcc.push(typeof rcpt === 'string' ? rcpt : rcpt.emails[0].address) - } - body.bcc = bcc - } + const body = prepareMessage() try { await api.sendEmailFromComposeSession(route.query.uid, body) router.push({ name: 'MailboxView' }) @@ -337,8 +355,29 @@ const lookForContacts = debounce(async (search) => { } }, 500) -const initialize = (body) => { - if (body.signature) { +const initialize = async (body) => { + if (route.query.mailid) { + // Load draft + const draft = await api.getEmailContent('Drafts', route.query.mailid) + form.value.sender = draft.data.from_address.address + if (draft.data.to?.length) { + form.value.to = draft.data.to.map((rcpt) => rcpt.address) + } + if (draft.data.cc?.length) { + form.value.cc = draft.data.cc.map((rcpt) => rcpt.address) + showCcField.value = true + } + if (draft.data.bcc?.length) { + form.value.bcc = draft.data.bcc.map((rcpt) => rcpt.address) + showBccField.value = true + } + if (draft.data.subject) { + form.value.subject = draft.data.subject + } + if (draft.data.body) { + form.value.body = draft.data.body + } + } else if (body.signature) { if (form.value.body) { form.value.body += body.signature } else { @@ -363,6 +402,18 @@ const closeSchedulingForm = () => { showSchedulingForm.value = false } +const saveDraft = async () => { + working.value = true + const body = prepareMessage() + try { + await api.saveComposeSession(route.query.uid, body) + } catch (error) { + console.log(error) + } finally { + working.value = false + } +} + const getItemTitle = (item) => { if (typeof item === 'string') { return item @@ -379,8 +430,9 @@ watch( ) if (!route.query.uid) { - api.createComposeSession().then((resp) => { + api.createComposeSession(route.query.mailid).then((resp) => { const query = { ...route.query, uid: resp.data.uid } + attachmentCount.value = resp.data.attachments?.length || 0 router.push({ name: route.name, query }) initialize(resp.data) }) diff --git a/frontend/src/views/webmail/EmailView.vue b/frontend/src/views/webmail/EmailView.vue index e516ab6c2..0281bcc3a 100644 --- a/frontend/src/views/webmail/EmailView.vue +++ b/frontend/src/views/webmail/EmailView.vue @@ -53,6 +53,15 @@ @click="markEmailAsNotJunk" > + + @@ -318,6 +327,13 @@ const replyToEmail = (all) => { const forwardEmail = () => { router.push({ name: 'ForwardEmailView', query: route.query }) } + +const editDraft = () => { + router.push({ + name: 'ComposeEmailView', + query: { mailid: route.query.mailid }, + }) +}