stash
This commit is contained in:
parent
1a3aab4d41
commit
f9a7abdbb1
6 changed files with 58 additions and 40 deletions
|
@ -1,15 +1,20 @@
|
|||
from django.urls import re_path
|
||||
from django.contrib.auth.decorators import permission_required
|
||||
from rest_framework import routers, viewsets
|
||||
from rest_framework import routers, viewsets, status
|
||||
from rest_framework.decorators import api_view, permission_classes
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from asgiref.sync import async_to_sync
|
||||
from channels.layers import get_channel_layer
|
||||
|
||||
from inventory.models import Event, Container, Item
|
||||
from inventory.serializers import EventSerializer, ContainerSerializer, ItemSerializer, SearchResultSerializer
|
||||
from inventory.models import Event, Container, Item, Comment
|
||||
from inventory.serializers import EventSerializer, ContainerSerializer, ItemSerializer, SearchResultSerializer, \
|
||||
CommentSerializer
|
||||
|
||||
from base64 import b64decode
|
||||
|
||||
from notify_sessions.models import SystemEvent
|
||||
|
||||
|
||||
class EventViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = EventSerializer
|
||||
|
@ -71,6 +76,31 @@ def item(request, event_slug):
|
|||
return Response(status=400)
|
||||
|
||||
|
||||
@api_view(['POST'])
|
||||
@permission_classes([IsAuthenticated])
|
||||
@permission_required('tickets.add_comment', raise_exception=True)
|
||||
def add_comment(request, event_slug, id):
|
||||
event = None
|
||||
if event_slug != 'none':
|
||||
event = Event.objects.get(slug=event_slug)
|
||||
item = Item.objects.get(event=event, id=id)
|
||||
if not request.user.has_event_perm(event, 'view_item'):
|
||||
return Response(status=403)
|
||||
if 'comment' not in request.data or request.data['comment'] == '':
|
||||
return Response({'status': 'error', 'message': 'missing comment'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
comment = Comment.objects.create(
|
||||
item=item,
|
||||
comment=request.data['comment'],
|
||||
)
|
||||
systemevent = SystemEvent.objects.create(type='comment added', reference=comment.id)
|
||||
channel_layer = get_channel_layer()
|
||||
async_to_sync(channel_layer.group_send)(
|
||||
'general', {"type": "generic.event", "name": "send_message_to_frontend", "event_id": systemevent.id,
|
||||
"message": "comment added"}
|
||||
)
|
||||
return Response(CommentSerializer(comment).data, status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
@api_view(['GET', 'PUT', 'DELETE', 'PATCH'])
|
||||
@permission_classes([IsAuthenticated])
|
||||
def item_by_id(request, event_slug, id):
|
||||
|
@ -117,5 +147,6 @@ urlpatterns = router.urls + [
|
|||
re_path(r'^(?P<event_slug>[\w-]+)/items/$', item, name='item'),
|
||||
re_path(r'^(?P<event_slug>[\w-]+)/items/(?P<query>[-A-Za-z0-9+/]*={0,3})/$', search_items, name='search_items'),
|
||||
re_path(r'^(?P<event_slug>[\w-]+)/item/$', item, name='item'),
|
||||
re_path(r'^(?P<event_slug>[\w-]+)/item/(?P<id>\d+)/comment/$', add_comment, name='add_comment'),
|
||||
re_path(r'^(?P<event_slug>[\w-]+)/item/(?P<id>\d+)/$', item_by_id, name='item_by_id'),
|
||||
]
|
||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework import serializers
|
|||
from rest_framework.relations import SlugRelatedField
|
||||
|
||||
from files.models import File
|
||||
from inventory.models import Event, Container, Item
|
||||
from inventory.models import Event, Container, Item, Comment
|
||||
from inventory.shared_serializers import BasicItemSerializer
|
||||
from mail.models import EventAddress
|
||||
from tickets.shared_serializers import BasicIssueSerializer
|
||||
|
@ -25,6 +25,7 @@ class EventSerializer(serializers.ModelSerializer):
|
|||
dict['addresses'] = [EventAddress.objects.get_or_create(address=x)[0] for x in addresses]
|
||||
return dict
|
||||
|
||||
|
||||
class ContainerSerializer(serializers.ModelSerializer):
|
||||
itemCount = serializers.SerializerMethodField()
|
||||
|
||||
|
@ -37,6 +38,18 @@ class ContainerSerializer(serializers.ModelSerializer):
|
|||
return len(instance.items)
|
||||
|
||||
|
||||
class CommentSerializer(serializers.ModelSerializer):
|
||||
|
||||
def validate(self, attrs):
|
||||
if 'comment' not in attrs or attrs['comment'] == '':
|
||||
raise serializers.ValidationError('comment cannot be empty')
|
||||
return attrs
|
||||
|
||||
class Meta:
|
||||
model = Comment
|
||||
fields = ('id', 'comment', 'timestamp', 'item')
|
||||
|
||||
|
||||
class ItemSerializer(BasicItemSerializer):
|
||||
timeline = serializers.SerializerMethodField()
|
||||
dataImage = serializers.CharField(write_only=True, required=False)
|
||||
|
|
0
core/testdata.py
Normal file
0
core/testdata.py
Normal file
|
@ -1,6 +1,5 @@
|
|||
<template>
|
||||
<div class="timeline-item-wrapper">
|
||||
<Lightbox v-if="lightboxHash" :hash="lightboxHash" @close="closeLightboxModal()"/>
|
||||
<div class="timeline-item-description">
|
||||
<i class="avatar | small">
|
||||
<font-awesome-icon icon="user"/>
|
||||
|
@ -23,7 +22,7 @@
|
|||
</div>
|
||||
<div class="card-footer" v-if="item.attachments.length">
|
||||
<ul>
|
||||
<li v-for="attachment in item.attachments" @click="openLightboxModalWith(attachment)">
|
||||
<li v-for="attachment in item.attachments" @click="openLightboxModalWith(attachment.hash)">
|
||||
<AuthenticatedImage :src="`/media/2/256/${attachment.hash}/`" :alt="attachment.name"
|
||||
v-if="attachment.mime_type.startsWith('image/')" cached/>
|
||||
<AuthenticatedDataLink :href="`/media/2/${attachment.hash}/`" :download="attachment.name"
|
||||
|
@ -59,16 +58,11 @@
|
|||
|
||||
import AuthenticatedImage from "@/components/AuthenticatedImage.vue";
|
||||
import AuthenticatedDataLink from "@/components/AuthenticatedDataLink.vue";
|
||||
import Lightbox from "@/components/Lightbox.vue";
|
||||
import {mapMutations} from "vuex";
|
||||
|
||||
export default {
|
||||
name: 'TimelineMail',
|
||||
components: {Lightbox, AuthenticatedImage, AuthenticatedDataLink},
|
||||
data() {
|
||||
return {
|
||||
lightboxHash: null,
|
||||
}
|
||||
},
|
||||
components: {AuthenticatedImage, AuthenticatedDataLink},
|
||||
props: {
|
||||
'item': {
|
||||
type: Object,
|
||||
|
@ -85,12 +79,7 @@ export default {
|
|||
|
||||
},
|
||||
methods: {
|
||||
openLightboxModalWith(attachment) {
|
||||
this.lightboxHash = attachment.hash;
|
||||
},
|
||||
closeLightboxModal() { // Closes the editing modal and discards the edited copy of the item.
|
||||
this.lightboxHash = null;
|
||||
},
|
||||
...mapMutations(['openLightboxModalWith'])
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<template>
|
||||
<div class="timeline-item-wrapper">
|
||||
<Lightbox v-if="lightboxHash" :hash="lightboxHash" @close="closeLightboxModal()"/>
|
||||
<div class="timeline-item-description">
|
||||
<i class="avatar | small">
|
||||
<font-awesome-icon icon="user"/>
|
||||
|
@ -19,7 +18,7 @@
|
|||
<AuthenticatedImage v-if="item.item.file" cached
|
||||
:src="`/media/2/256/${item.item.file}/`"
|
||||
class="d-block card-img-left"
|
||||
@click="openLightboxModalWith(item.item)"
|
||||
@click="openLightboxModalWith(item.item.file)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
|
@ -78,16 +77,11 @@
|
|||
|
||||
import AuthenticatedImage from "@/components/AuthenticatedImage.vue";
|
||||
import AuthenticatedDataLink from "@/components/AuthenticatedDataLink.vue";
|
||||
import Lightbox from "@/components/Lightbox.vue";
|
||||
import {mapMutations} from "vuex";
|
||||
|
||||
export default {
|
||||
name: 'TimelineRelatedItem',
|
||||
components: {Lightbox, AuthenticatedImage, AuthenticatedDataLink},
|
||||
data() {
|
||||
return {
|
||||
lightboxHash: null,
|
||||
}
|
||||
},
|
||||
components: {AuthenticatedImage, AuthenticatedDataLink},
|
||||
props: {
|
||||
'item': {
|
||||
type: Object,
|
||||
|
@ -104,13 +98,8 @@ export default {
|
|||
|
||||
},
|
||||
methods: {
|
||||
openLightboxModalWith(attachment) {
|
||||
this.lightboxHash = attachment.hash;
|
||||
},
|
||||
closeLightboxModal() { // Closes the editing modal and discards the edited copy of the item.
|
||||
this.lightboxHash = null;
|
||||
},
|
||||
},
|
||||
...mapMutations(['openLightboxModalWith'])
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -19,13 +19,9 @@
|
|||
|
||||
<script>
|
||||
|
||||
import AuthenticatedImage from "@/components/AuthenticatedImage.vue";
|
||||
import AuthenticatedDataLink from "@/components/AuthenticatedDataLink.vue";
|
||||
import Lightbox from "@/components/Lightbox.vue";
|
||||
|
||||
export default {
|
||||
name: 'TimelineRelatedTicket',
|
||||
components: {Lightbox},
|
||||
components: {},
|
||||
props: {
|
||||
'item': {
|
||||
type: Object,
|
||||
|
|
Loading…
Add table
Reference in a new issue