show mail attachments in frontend
This commit is contained in:
parent
d1626d1777
commit
04f774404a
11 changed files with 252 additions and 23 deletions
|
@ -11,6 +11,7 @@ from rest_framework.response import Response
|
|||
|
||||
from core.settings import MEDIA_ROOT
|
||||
from files.models import File
|
||||
from mail.models import EmailAttachment
|
||||
|
||||
|
||||
@swagger_auto_schema(method='GET', auto_schema=None)
|
||||
|
@ -21,7 +22,11 @@ def media_urls(request, hash):
|
|||
if request.META.get('HTTP_IF_NONE_MATCH') and request.META.get('HTTP_IF_NONE_MATCH') == hash:
|
||||
return HttpResponse(status=status.HTTP_304_NOT_MODIFIED)
|
||||
|
||||
file = File.objects.get(hash=hash)
|
||||
file = File.objects.filter(hash=hash).first()
|
||||
attachment = EmailAttachment.objects.filter(hash=hash).first()
|
||||
file = file if file else attachment
|
||||
if not file:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||
hash_path = file.file
|
||||
return HttpResponse(status=status.HTTP_200_OK,
|
||||
content_type=file.mime_type,
|
||||
|
@ -33,9 +38,10 @@ def media_urls(request, hash):
|
|||
'Age': 0,
|
||||
'ETag': file.hash,
|
||||
})
|
||||
|
||||
except File.DoesNotExist:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||
except EmailAttachment.DoesNotExist:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
|
||||
@swagger_auto_schema(method='GET', auto_schema=None)
|
||||
|
@ -47,7 +53,11 @@ def thumbnail_urls(request, size, hash):
|
|||
if request.META.get('HTTP_IF_NONE_MATCH') and request.META.get('HTTP_IF_NONE_MATCH') == hash + "_" + str(size):
|
||||
return HttpResponse(status=status.HTTP_304_NOT_MODIFIED)
|
||||
try:
|
||||
file = File.objects.get(hash=hash)
|
||||
file = File.objects.filter(hash=hash).first()
|
||||
attachment = EmailAttachment.objects.filter(hash=hash).first()
|
||||
file = file if file else attachment
|
||||
if not file:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||
hash_path = file.file
|
||||
if not os.path.exists(MEDIA_ROOT + f'/thumbnails/{size}/{hash_path}'):
|
||||
from PIL import Image
|
||||
|
@ -72,6 +82,8 @@ def thumbnail_urls(request, size, hash):
|
|||
|
||||
except File.DoesNotExist:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||
except EmailAttachment.DoesNotExist:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
|
|
|
@ -77,6 +77,13 @@ class AbstractFile(models.Model):
|
|||
|
||||
objects = FileManager()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
from django.utils import timezone
|
||||
if not self.created_at:
|
||||
self.created_at = timezone.now()
|
||||
self.updated_at = timezone.now()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
from rest_framework import routers, viewsets, serializers
|
||||
|
||||
from mail.models import Email
|
||||
from mail.models import Email, EmailAttachment
|
||||
|
||||
|
||||
class AttachmentSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = EmailAttachment
|
||||
fields = ['hash', 'mime_type', 'name']
|
||||
|
||||
|
||||
class EmailSerializer(serializers.ModelSerializer):
|
||||
|
|
|
@ -26,7 +26,7 @@ class Migration(migrations.Migration):
|
|||
def generate_email_attachments(apps, schema_editor):
|
||||
for email in Email.objects.all():
|
||||
raw = email.raw
|
||||
if raw is None:
|
||||
if raw is None or raw == '':
|
||||
continue
|
||||
parsed, body, attachments = parse_email_body(raw.encode('utf-8'), NullLogger())
|
||||
email.attachments.clear()
|
||||
|
|
|
@ -92,8 +92,8 @@ class LMTPHandlerTestCase(TestCase): # TODO replace with less hacky test
|
|||
session = mock.Mock()
|
||||
envelope = Envelope()
|
||||
envelope.mail_from = 'test1@test'
|
||||
envelope.rcpt_tos = ['test2@test']
|
||||
envelope.content = b'Subject: test\nFrom: test3@test\nTo: test4@test\nMessage-ID: <1@test>\n\ntest'
|
||||
envelope.rcpt_tos = ['test2@localhost']
|
||||
envelope.content = b'Subject: test\nFrom: test3@test\nTo: test4@localhost\nMessage-ID: <1@test>\n\ntest'
|
||||
result = async_to_sync(handler.handle_DATA)(server, session, envelope)
|
||||
self.assertEqual(result, '250 Message accepted for delivery')
|
||||
self.assertEqual(len(Email.objects.all()), 2)
|
||||
|
@ -101,14 +101,14 @@ class LMTPHandlerTestCase(TestCase): # TODO replace with less hacky test
|
|||
aiosmtplib.send.assert_called_once()
|
||||
self.assertEqual('test', Email.objects.all()[0].subject)
|
||||
self.assertEqual('test1@test', Email.objects.all()[0].sender)
|
||||
self.assertEqual('test2@test', Email.objects.all()[0].recipient)
|
||||
self.assertEqual('test2@localhost', Email.objects.all()[0].recipient)
|
||||
self.assertEqual('test', Email.objects.all()[0].body)
|
||||
self.assertEqual(IssueThread.objects.all()[0], Email.objects.all()[0].issue_thread)
|
||||
self.assertEqual('<1@test>', Email.objects.all()[0].reference)
|
||||
self.assertEqual(None, Email.objects.all()[0].in_reply_to)
|
||||
self.assertEqual(expected_auto_reply_subject.format('test', IssueThread.objects.all()[0].short_uuid()),
|
||||
Email.objects.all()[1].subject)
|
||||
self.assertEqual('test2@test', Email.objects.all()[1].sender)
|
||||
self.assertEqual('test2@localhost', Email.objects.all()[1].sender)
|
||||
self.assertEqual('test1@test', Email.objects.all()[1].recipient)
|
||||
self.assertEqual(expected_auto_reply.format(IssueThread.objects.all()[0].short_uuid()),
|
||||
Email.objects.all()[1].body)
|
||||
|
@ -284,19 +284,19 @@ class LMTPHandlerTestCase(TestCase): # TODO replace with less hacky test
|
|||
|
||||
def test_mail_reply(self):
|
||||
issue_thread = IssueThread.objects.create(
|
||||
name="test",
|
||||
name="test subject",
|
||||
)
|
||||
mail1 = Email.objects.create(
|
||||
subject='test subject',
|
||||
body='test',
|
||||
sender='test1@test',
|
||||
recipient='test2@' + MAIL_DOMAIN,
|
||||
recipient='test2@localhost',
|
||||
issue_thread=issue_thread,
|
||||
)
|
||||
mail1_reply = Email.objects.create(
|
||||
subject='Re: test subject',
|
||||
subject='Message received',
|
||||
body='Thank you for your message.',
|
||||
sender='test2@' + MAIL_DOMAIN,
|
||||
sender='test2@localhost',
|
||||
recipient='test1@test',
|
||||
in_reply_to=mail1.reference,
|
||||
issue_thread=issue_thread,
|
||||
|
@ -310,8 +310,22 @@ class LMTPHandlerTestCase(TestCase): # TODO replace with less hacky test
|
|||
self.assertEqual(len(Email.objects.all()), 3)
|
||||
self.assertEqual(len(IssueThread.objects.all()), 1)
|
||||
aiosmtplib.send.assert_called_once()
|
||||
self.assertEqual(Email.objects.all()[2].subject, 'Re: test subject')
|
||||
self.assertEqual(Email.objects.all()[2].sender, 'test2@' + MAIL_DOMAIN)
|
||||
self.assertEqual(Email.objects.all()[0].subject, 'test subject')
|
||||
self.assertEqual(Email.objects.all()[0].sender, 'test1@test')
|
||||
self.assertEqual(Email.objects.all()[0].recipient, 'test2@localhost')
|
||||
self.assertEqual(Email.objects.all()[0].body, 'test')
|
||||
self.assertEqual(Email.objects.all()[0].issue_thread, issue_thread)
|
||||
self.assertEqual(Email.objects.all()[0].reference, mail1.reference)
|
||||
self.assertEqual(Email.objects.all()[1].subject, 'Message received')
|
||||
self.assertEqual(Email.objects.all()[1].sender, 'test2@localhost')
|
||||
self.assertEqual(Email.objects.all()[1].recipient, 'test1@test')
|
||||
self.assertEqual(Email.objects.all()[1].body, 'Thank you for your message.')
|
||||
self.assertEqual(Email.objects.all()[1].issue_thread, issue_thread)
|
||||
self.assertTrue(Email.objects.all()[1].reference.startswith("<"))
|
||||
self.assertTrue(Email.objects.all()[1].reference.endswith("@localhost>"))
|
||||
self.assertEqual(Email.objects.all()[1].in_reply_to, mail1.reference)
|
||||
self.assertEqual(Email.objects.all()[2].subject, 'Re: test subject [#{0}]'.format(issue_thread.short_uuid()))
|
||||
self.assertEqual(Email.objects.all()[2].sender, 'test2@localhost')
|
||||
self.assertEqual(Email.objects.all()[2].recipient, 'test1@test')
|
||||
self.assertEqual(Email.objects.all()[2].body, 'test')
|
||||
self.assertEqual(Email.objects.all()[2].issue_thread, issue_thread)
|
||||
|
@ -633,25 +647,26 @@ dGVzdGltYWdl
|
|||
|
||||
def test_mail_plus_issue_thread(self):
|
||||
issue_thread = IssueThread.objects.create(
|
||||
name="test",
|
||||
name="test subject",
|
||||
)
|
||||
mail1 = Email.objects.create(
|
||||
subject='test subject',
|
||||
body='test',
|
||||
sender='test1@test',
|
||||
recipient='test2@test',
|
||||
recipient='test2@localhost',
|
||||
issue_thread=issue_thread,
|
||||
)
|
||||
mail1_reply = Email.objects.create(
|
||||
subject='Message received',
|
||||
body='Thank you for your message.',
|
||||
sender='test2@test',
|
||||
sender='test2@localhost',
|
||||
recipient='test1@test',
|
||||
in_reply_to=mail1.reference,
|
||||
issue_thread=issue_thread,
|
||||
)
|
||||
from aiosmtpd.smtp import Envelope
|
||||
from asgiref.sync import async_to_sync
|
||||
from email.message import EmailMessage
|
||||
import aiosmtplib
|
||||
import logging
|
||||
logging.disable(logging.CRITICAL)
|
||||
|
@ -677,4 +692,23 @@ dGVzdGltYWdl
|
|||
self.assertEqual(Email.objects.all()[2].body, 'bar')
|
||||
self.assertEqual(Email.objects.all()[2].issue_thread, issue_thread)
|
||||
self.assertEqual(Email.objects.all()[2].reference, '<3@test>')
|
||||
self.assertEqual('test', IssueThread.objects.all()[0].name)
|
||||
self.assertEqual('test subject', IssueThread.objects.all()[0].name)
|
||||
response = self.client.post(f'/api/2/tickets/{issue_thread.id}/reply/', {
|
||||
'message': 'test'
|
||||
})
|
||||
aiosmtplib.send.assert_called_once();
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(4, len(Email.objects.all()))
|
||||
self.assertEqual(4, len(Email.objects.filter(issue_thread=issue_thread)))
|
||||
self.assertEqual(1, len(IssueThread.objects.all()))
|
||||
self.assertEqual(Email.objects.all()[3].subject, 'Re: test subject [#{0}]'.format(issue_thread.short_uuid()))
|
||||
self.assertEqual(Email.objects.all()[3].sender, 'test2@localhost')
|
||||
self.assertEqual(Email.objects.all()[3].recipient, 'test1@test')
|
||||
self.assertEqual(Email.objects.all()[3].body, 'test')
|
||||
self.assertEqual(Email.objects.all()[3].issue_thread, issue_thread)
|
||||
self.assertTrue(Email.objects.all()[3].reference.startswith("<"))
|
||||
self.assertTrue(Email.objects.all()[3].reference.endswith("@localhost>"))
|
||||
self.assertEqual(Email.objects.all()[3].in_reply_to, mail1.reference)
|
||||
self.assertEqual('test subject', IssueThread.objects.all()[0].name)
|
||||
self.assertEqual('pending_new', IssueThread.objects.all()[0].state)
|
||||
self.assertEqual(None, IssueThread.objects.all()[0].assigned_to)
|
||||
|
|
|
@ -10,6 +10,7 @@ from asgiref.sync import async_to_sync
|
|||
from channels.layers import get_channel_layer
|
||||
|
||||
from core.settings import MAIL_DOMAIN
|
||||
from mail.api_v2 import AttachmentSerializer
|
||||
from mail.models import Email
|
||||
from mail.protocol import send_smtp, make_reply, collect_references
|
||||
from notify_sessions.models import SystemEvent
|
||||
|
@ -75,6 +76,7 @@ class IssueSerializer(serializers.ModelSerializer):
|
|||
'recipient': email.recipient,
|
||||
'subject': email.subject,
|
||||
'body': email.body,
|
||||
'attachments': AttachmentSerializer(email.attachments.all(), many=True).data,
|
||||
})
|
||||
return sorted(timeline, key=lambda x: x['timestamp'])
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from datetime import datetime, timedelta
|
|||
from django.test import TestCase, Client
|
||||
|
||||
from authentication.models import ExtendedUser
|
||||
from mail.models import Email
|
||||
from mail.models import Email, EmailAttachment
|
||||
from tickets.models import IssueThread, StateChange, Comment
|
||||
from django.contrib.auth.models import Permission
|
||||
from knox.models import AuthToken
|
||||
|
@ -147,6 +147,88 @@ class IssueApiTest(TestCase):
|
|||
self.assertEqual(comment.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
|
||||
response.json()[2]['timeline'][1]['timestamp'])
|
||||
|
||||
def test_issues_with_files(self):
|
||||
from django.core.files.base import ContentFile
|
||||
from hashlib import sha256
|
||||
now = datetime.now()
|
||||
issue = IssueThread.objects.create(
|
||||
name="test issue",
|
||||
)
|
||||
mail1 = Email.objects.create(
|
||||
subject='test',
|
||||
body='test',
|
||||
sender='test',
|
||||
recipient='test',
|
||||
issue_thread=issue,
|
||||
timestamp=now,
|
||||
)
|
||||
mail2 = Email.objects.create(
|
||||
subject='test',
|
||||
body='test',
|
||||
sender='test',
|
||||
recipient='test',
|
||||
issue_thread=issue,
|
||||
in_reply_to=mail1.reference,
|
||||
timestamp=now + timedelta(seconds=2),
|
||||
)
|
||||
comment = Comment.objects.create(
|
||||
issue_thread=issue,
|
||||
comment="test",
|
||||
timestamp=now + timedelta(seconds=3),
|
||||
)
|
||||
file1 = EmailAttachment.objects.create(
|
||||
name='file1', mime_type='text/plain', file=ContentFile(b"foo1", "f1"),
|
||||
hash=sha256(b"foo1").hexdigest(), email=mail1
|
||||
)
|
||||
file2 = EmailAttachment.objects.create(
|
||||
name='file2', mime_type='text/plain', file=ContentFile(b"foo2", "f2"),
|
||||
hash=sha256(b"foo2").hexdigest(), email=mail1
|
||||
)
|
||||
|
||||
response = self.client.get('/api/2/tickets/')
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertEqual(1, len(response.json()))
|
||||
self.assertEqual(issue.id, response.json()[0]['id'])
|
||||
self.assertEqual('pending_new', response.json()[0]['state'])
|
||||
self.assertEqual('test issue', response.json()[0]['name'])
|
||||
self.assertEqual(None, response.json()[0]['assigned_to'])
|
||||
self.assertEqual(36, len(response.json()[0]['uuid']))
|
||||
self.assertEqual(comment.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
|
||||
response.json()[0]['last_activity'])
|
||||
self.assertEqual(4, len(response.json()[0]['timeline']))
|
||||
self.assertEqual('state', response.json()[0]['timeline'][0]['type'])
|
||||
self.assertEqual('mail', response.json()[0]['timeline'][1]['type'])
|
||||
self.assertEqual('mail', response.json()[0]['timeline'][2]['type'])
|
||||
self.assertEqual('comment', response.json()[0]['timeline'][3]['type'])
|
||||
self.assertEqual(mail1.id, response.json()[0]['timeline'][1]['id'])
|
||||
self.assertEqual(mail2.id, response.json()[0]['timeline'][2]['id'])
|
||||
self.assertEqual(comment.id, response.json()[0]['timeline'][3]['id'])
|
||||
self.assertEqual('pending_new', response.json()[0]['timeline'][0]['state'])
|
||||
self.assertEqual('test', response.json()[0]['timeline'][1]['sender'])
|
||||
self.assertEqual('test', response.json()[0]['timeline'][1]['recipient'])
|
||||
self.assertEqual('test', response.json()[0]['timeline'][1]['subject'])
|
||||
self.assertEqual('test', response.json()[0]['timeline'][1]['body'])
|
||||
self.assertEqual(mail1.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
|
||||
response.json()[0]['timeline'][1]['timestamp'])
|
||||
self.assertEqual('test', response.json()[0]['timeline'][2]['sender'])
|
||||
self.assertEqual('test', response.json()[0]['timeline'][2]['recipient'])
|
||||
|
||||
self.assertEqual('test', response.json()[0]['timeline'][2]['subject'])
|
||||
self.assertEqual('test', response.json()[0]['timeline'][2]['body'])
|
||||
self.assertEqual(mail2.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
|
||||
response.json()[0]['timeline'][2]['timestamp'])
|
||||
self.assertEqual('test', response.json()[0]['timeline'][3]['comment'])
|
||||
self.assertEqual(comment.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
|
||||
response.json()[0]['timeline'][3]['timestamp'])
|
||||
self.assertEqual(2, len(response.json()[0]['timeline'][1]['attachments']))
|
||||
self.assertEqual(0, len(response.json()[0]['timeline'][2]['attachments']))
|
||||
self.assertEqual('file1', response.json()[0]['timeline'][1]['attachments'][0]['name'])
|
||||
self.assertEqual('file2', response.json()[0]['timeline'][1]['attachments'][1]['name'])
|
||||
self.assertEqual('text/plain', response.json()[0]['timeline'][1]['attachments'][0]['mime_type'])
|
||||
self.assertEqual('text/plain', response.json()[0]['timeline'][1]['attachments'][1]['mime_type'])
|
||||
self.assertEqual(file1.hash, response.json()[0]['timeline'][1]['attachments'][0]['hash'])
|
||||
self.assertEqual(file2.hash, response.json()[0]['timeline'][1]['attachments'][1]['hash'])
|
||||
|
||||
def test_manual_creation(self):
|
||||
response = self.client.post('/api/2/tickets/manual/',
|
||||
{'name': 'test issue', 'sender': 'test', 'recipient': 'test', 'body': 'test'},
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
"vue-router": "^3.1.3",
|
||||
"vuex": "^3.1.2",
|
||||
"vuex-router-sync": "^5.0.0",
|
||||
"vuex-shared-mutations": "^1.0.2",
|
||||
"yarn": "^1.22.21"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
48
web/src/components/AuthenticatedDataLink.vue
Normal file
48
web/src/components/AuthenticatedDataLink.vue
Normal file
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<a :href="image_data">{{ download }}</a>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
import {mapActions} from "vuex";
|
||||
|
||||
export default {
|
||||
name: "AuthenticatedDataLink",
|
||||
props: {
|
||||
href: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
download: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "unnamed"
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
image_data: "",
|
||||
servers: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['fetchImage']),
|
||||
loadImage() {
|
||||
this.fetchImage(this.href).then((response) => {
|
||||
const mime_type = response.headers.get("content-type");
|
||||
response.arrayBuffer().then((buf) => {
|
||||
const base64 = btoa(new Uint8Array(buf)
|
||||
.reduce((data, byte) => data + String.fromCharCode(byte), ""));
|
||||
this.image_data = "data:" + mime_type + ";base64," + base64;
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadImage();
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -18,6 +18,14 @@
|
|||
<font-awesome-icon icon="user"/>
|
||||
</button-->
|
||||
</div>
|
||||
<div class="card-footer" v-if="item.attachments.length">
|
||||
<ul>
|
||||
<li v-for="attachment in item.attachments">
|
||||
<AuthenticatedImage :src="`/media/2/256/${attachment.hash}/`" :alt="attachment.name" v-if="attachment.mime_type.startsWith('image/')"/>
|
||||
<AuthenticatedDataLink :href="`/media/2/256/${attachment.hash}/`" :download="attachment.name" v-else/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<!--button class="show-replies">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-arrow-forward"
|
||||
|
@ -44,8 +52,12 @@
|
|||
|
||||
<script>
|
||||
|
||||
import AuthenticatedImage from "@/components/AuthenticatedImage.vue";
|
||||
import AuthenticatedDataLink from "@/components/AuthenticatedDataLink.vue";
|
||||
|
||||
export default {
|
||||
name: 'TimelineMail',
|
||||
components: {AuthenticatedImage, AuthenticatedDataLink},
|
||||
props: {
|
||||
'item': {
|
||||
type: Object,
|
||||
|
|
|
@ -7,6 +7,7 @@ import router from '../router';
|
|||
import * as base64 from 'base-64';
|
||||
import * as utf8 from 'utf8';
|
||||
import {ticketStateColorLookup, ticketStateIconLookup} from "@/utils";
|
||||
import createMutationsSharer from "vuex-shared-mutations";
|
||||
|
||||
Vue.use(Vuex);
|
||||
const axios = AxiosBootstrap.create({
|
||||
|
@ -103,7 +104,6 @@ const store = new Vuex.Store({
|
|||
slug: slug,
|
||||
text: 'Unknown'
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
isLoggedIn(state) {
|
||||
|
@ -273,10 +273,11 @@ const store = new Vuex.Store({
|
|||
//async verifyToken({commit, state}) {
|
||||
async afterLogin({dispatch}) {
|
||||
const boxes = dispatch('loadBoxes');
|
||||
const states = dispatch('fetchTicketStates');
|
||||
const items = dispatch('loadEventItems');
|
||||
const tickets = dispatch('loadTickets');
|
||||
const user = dispatch('loadUserInfo');
|
||||
await Promise.all([boxes, items, tickets, user]);
|
||||
await Promise.all([boxes, items, tickets, user, states]);
|
||||
},
|
||||
async fetchImage({state}, url) {
|
||||
return await fetch(url, {headers: {'Authorization': `Token ${state.token}`}});
|
||||
|
@ -394,7 +395,31 @@ const store = new Vuex.Store({
|
|||
const {data} = await axios.patch(`/2/tickets/${id}/`, ticket);
|
||||
commit('updateTicket', data);
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [createMutationsSharer({
|
||||
predicate: [
|
||||
'replaceLoadedItems',
|
||||
'setItemCache',
|
||||
'setLayout',
|
||||
'replaceBoxes',
|
||||
'updateItem',
|
||||
'removeItem',
|
||||
'appendItem',
|
||||
'replaceTickets',
|
||||
'replaceUsers',
|
||||
'replaceGroups',
|
||||
'updateTicket',
|
||||
'openAddBoxModal',
|
||||
'closeAddBoxModal',
|
||||
'createToast',
|
||||
'removeToast',
|
||||
'setRemember',
|
||||
'setUser',
|
||||
'setPermissions',
|
||||
'setToken',
|
||||
'logout',
|
||||
]
|
||||
})],
|
||||
});
|
||||
|
||||
export default store;
|
||||
|
|
Loading…
Reference in a new issue