From 2e573b3465117d6542e539de7e78b3d84a3dbf76 Mon Sep 17 00:00:00 2001
From: "J.-S. Caux" <J.S.Caux@uva.nl>
Date: Thu, 30 Jan 2020 08:06:20 +0100
Subject: [PATCH] Drafting/reworking/deleting (draft) composed messages now
 working

---
 apimail/api/views.py                          | 24 ++++++++--
 apimail/models/account.py                     |  2 +-
 .../assets/vue/components/MessageComposer.vue | 15 ++++--
 .../assets/vue/components/MessageContent.vue  | 12 ++---
 .../assets/vue/components/MessagesTable.vue   | 47 +++++++++++++++----
 apimail/urls.py                               | 10 ++++
 6 files changed, 87 insertions(+), 23 deletions(-)

diff --git a/apimail/api/views.py b/apimail/api/views.py
index 8fb9f8db5..f8c31e898 100644
--- a/apimail/api/views.py
+++ b/apimail/api/views.py
@@ -9,7 +9,7 @@ from django.shortcuts import get_object_or_404
 from django.utils import timezone
 
 from rest_framework.generics import (
-    CreateAPIView, ListAPIView,
+    CreateAPIView, DestroyAPIView, ListAPIView,
     RetrieveAPIView, UpdateAPIView)
 from rest_framework.permissions import AllowAny, IsAdminUser, IsAuthenticated
 from rest_framework.response import Response
@@ -41,9 +41,9 @@ class UserEmailAccountAccessListAPIView(ListAPIView):
 
     def get_queryset(self):
         queryset = self.request.user.email_account_accesses.all()
-        if self.request.query_params.get('current', None):
+        if self.request.query_params.get('current', None) == 'true':
             queryset = queryset.current()
-        if self.request.query_params.get('cansend', None):
+        if self.request.query_params.get('cansend', None) == 'true':
             queryset = queryset.can_send()
         return queryset
 
@@ -66,13 +66,29 @@ class ComposedMessageCreateAPIView(CreateAPIView):
         return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
 
 
+class ComposedMessageUpdateAPIView(UpdateAPIView):
+    queryset = ComposedMessage.objects.all()
+    serializer_class = ComposedMessageSerializer
+    lookup_field = 'uuid'
+
+
+class ComposedMessageDestroyAPIView(DestroyAPIView):
+    permission_classes = (IsAuthenticated,)
+    serializer_class = ComposedMessageSerializer
+    lookup_field = 'uuid'
+
+    def get_queryset(self):
+        return ComposedMessage.objects.filter_for_user(self.request.user)
+
+
 class ComposedMessageListAPIView(ListAPIView):
+    permission_classes = (IsAuthenticated,)
     serializer_class = ComposedMessageSerializer
     lookup_field = 'uuid'
 
     def get_queryset(self):
         queryset = ComposedMessage.objects.filter_for_user(self.request.user)
-        if self.request.query_params.get('draft', None):
+        if self.request.query_params.get('status', None) == 'draft':
             queryset = queryset.filter(status=ComposedMessage.STATUS_DRAFT)
         return queryset
 
diff --git a/apimail/models/account.py b/apimail/models/account.py
index d5906d123..c0537002d 100644
--- a/apimail/models/account.py
+++ b/apimail/models/account.py
@@ -53,7 +53,7 @@ class EmailAccountAccess(models.Model):
     date_from = models.DateField()
     date_until = models.DateField()
 
-    objects = EmailAccountAccessQuerySet()
+    objects = EmailAccountAccessQuerySet.as_manager()
 
     class Meta:
         ordering = ['account__email', 'user__last_name', '-date_until',]
diff --git a/apimail/static/apimail/assets/vue/components/MessageComposer.vue b/apimail/static/apimail/assets/vue/components/MessageComposer.vue
index 3546d6e5f..c5d203aec 100644
--- a/apimail/static/apimail/assets/vue/components/MessageComposer.vue
+++ b/apimail/static/apimail/assets/vue/components/MessageComposer.vue
@@ -185,15 +185,24 @@ export default {
     },
     methods: {
 	fetchCurrentAccounts () {
-	    fetch('/mail/api/user_account_accesses?current&cansend')
+	    fetch('/mail/api/user_account_accesses?current=true&cansend=true')
 		.then(stream => stream.json())
 		.then(data => this.from_account_accesses = data.results)
 		.catch(error => console.error(error))
 	},
 	saveMessage (status) {
-	    fetch('/mail/api/composed_message/create',
+	    var url = '/mail/api/composed_message'
+	    var method = 'POST'
+	    if (this.draftmessage) { // draft message exists, update
+		url += '/' + this.draftmessage.uuid + '/update'
+		method = 'PATCH'
+	    }
+	    else {
+		url += '/create'
+	    }
+	    fetch(url,
 	    	  {
-	    	      method: 'POST',
+	    	      method: method,
 	    	      headers: {
 	    		  "X-CSRFToken": csrftoken,
 	    		  "Content-Type": "application/json; charset=utf-8"
diff --git a/apimail/static/apimail/assets/vue/components/MessageContent.vue b/apimail/static/apimail/assets/vue/components/MessageContent.vue
index 7aaf8470f..6823393f4 100644
--- a/apimail/static/apimail/assets/vue/components/MessageContent.vue
+++ b/apimail/static/apimail/assets/vue/components/MessageContent.vue
@@ -25,9 +25,9 @@
 	    no-close-on-backdrop
 	    >
 	    <message-composer :originalmessage="message" action="reply"></message-composer>
-	    <template v-slot:modal-footer="{ cancel, }">
-	      <b-button size="sm" variant="danger" @click="cancel()">
-		Cancel/close
+	    <template v-slot:modal-footer="{ close, }">
+	      <b-button size="sm" variant="danger" @click="close()">
+		Close
 	      </b-button>
 	    </template>
 	  </b-modal>
@@ -49,9 +49,9 @@
 	    no-close-on-backdrop
 	    >
 	    <message-composer :originalmessage="message" action="forward"></message-composer>
-	    <template v-slot:modal-footer="{ cancel, }">
-	      <b-button variant="danger" @click="cancel()">
-		Cancel
+	    <template v-slot:modal-footer="{ close, }">
+	      <b-button variant="danger" @click="close()">
+		Close
 	      </b-button>
 	    </template>
 	  </b-modal>
diff --git a/apimail/static/apimail/assets/vue/components/MessagesTable.vue b/apimail/static/apimail/assets/vue/components/MessagesTable.vue
index 920886fd9..a4d33108c 100644
--- a/apimail/static/apimail/assets/vue/components/MessagesTable.vue
+++ b/apimail/static/apimail/assets/vue/components/MessagesTable.vue
@@ -10,42 +10,51 @@
     no-close-on-backdrop
     >
     <message-composer :draftmessage="draftMessageSelected"></message-composer>
-    <template v-slot:modal-footer="{ cancel, }">
-      <b-button size="sm" variant="danger" @click="cancel()">
-	Cancel/close
+    <template v-slot:modal-footer="{ close, }">
+      <b-button size="sm" variant="danger" @click="close()">
+	Close
       </b-button>
     </template>
   </b-modal>
 
 
-  <div v-if="draftmessages">
+  <div v-if="draftMessages.length > 0" class="m-2 mb-4">
     <h2>Message drafts to complete</h2>
     <table class="table">
       <tr>
 	<th>From</th>
 	<th>To</th>
 	<th>Subject</th>
+	<th>Status</th>
 	<th>Actions</th>
       </tr>
       <tr
-	v-for="draftmsg in draftmessages"
+	v-for="draftmsg in draftMessages"
 	>
 	<td>{{ draftmsg.from_account }}</td>
 	<td>{{ draftmsg.to_recipient }}</td>
 	<td>{{ draftmsg.subject }}</td>
+	<td>{{ draftmsg.status }}</td>
 	<td>
 	  <b-button
 	    @click="showReworkDraftModal(draftmsg)"
+	    size="sm"
 	    variant="warning"
 	    >
 	    Rework draft
 	  </b-button>
+	  <b-button
+	    @click="deleteDraft(draftmsg.uuid)"
+	    size="sm"
+	    variant="danger"
+	    >
+	    Delete
+	  </b-button>
 	</td>
       </tr>
     </table>
   </div>
 
-
   <h2>Click on an account to view messages</h2>
 
   <table class="table">
@@ -292,8 +301,9 @@ export default {
 	return {
 	    accesses: null,
 	    accountSelected: null,
-	    draftmessages: null,
+	    draftMessages: [],
 	    draftMessageSelected: null,
+	    queuedMessages: null,
 	    messages: [],
 	    perPage: 10,
 	    currentPage: 1,
@@ -340,15 +350,31 @@ export default {
 		.catch(error => console.error(error))
 	},
 	fetchDrafts () {
-	    fetch('/mail/api/composed_messages?draft')
+	    fetch('/mail/api/composed_messages?status=draft')
 		.then(stream => stream.json())
-		.then(data => this.draftmessages = data.results)
+		.then(data => this.draftMessages = data.results)
 		.catch(error => console.error(error))
 	},
 	showReworkDraftModal (draftmsg) {
 	    this.draftMessageSelected = draftmsg
 	    this.$bvModal.show('modal-resumedraft')
 	},
+	deleteDraft (uuid) {
+	    fetch('/mail/api/composed_message/' + uuid + '/delete',
+		  {
+		      method: 'DELETE',
+		      headers: {
+			  "X-CSRFToken": csrftoken,
+		      }
+		  }
+		 )
+		.then(response => {
+		    if (response.ok) {
+			this.fetchDrafts()
+		    }
+		})
+		.catch(error => console.error(error))
+	},
 	tagMessage (message, tag, action) {
 	    fetch('/mail/api/stored_message/' + message.uuid + '/tag',
 		  {
@@ -422,6 +448,9 @@ export default {
 	this.fetchAccounts()
 	this.fetchTags()
 	this.fetchDrafts()
+	this.$root.$on('bv::modal::hide', (bvEvent, modalId) => {
+	    this.fetchDrafts()
+	})
     },
     watch: {
 	accountSelected: function () {
diff --git a/apimail/urls.py b/apimail/urls.py
index 4ee92e16c..cfb68c000 100644
--- a/apimail/urls.py
+++ b/apimail/urls.py
@@ -32,6 +32,16 @@ urlpatterns = [
             apiviews.ComposedMessageCreateAPIView.as_view(),
             name='composed_message_create'
         ),
+        path( # /mail/api/composed_message/<uuid>/update
+            'composed_message/<uuid:uuid>/update',
+            apiviews.ComposedMessageUpdateAPIView.as_view(),
+            name='composed_message_update'
+        ),
+        path( # /mail/api/composed_message/<uuid>/delete
+            'composed_message/<uuid:uuid>/delete',
+            apiviews.ComposedMessageDestroyAPIView.as_view(),
+            name='composed_message_delete'
+        ),
         path( # /mail/api/composed_messages
             'composed_messages',
             apiviews.ComposedMessageListAPIView.as_view(),
-- 
GitLab