Compare commits

...

2 commits

Author SHA1 Message Date
c8a75b5670 stash
All checks were successful
/ test (push) Successful in 55s
2024-11-28 19:20:46 +01:00
f9a7abdbb1 stash 2024-11-28 19:20:36 +01:00
8 changed files with 147 additions and 41 deletions

View file

@ -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'),
]

View file

@ -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
View file

View file

@ -3,7 +3,7 @@ services:
build:
context: ../../core
dockerfile: ../deploy/dev/Dockerfile.backend
command: bash -c 'python manage.py migrate && python manage.py runserver 0.0.0.0:8000'
command: bash -c 'python manage.py runserver 0.0.0.0:8000'
environment:
- HTTP_HOST=core
- DB_HOST=db
@ -13,6 +13,7 @@ services:
- DB_PASSWORD=system3
volumes:
- ../../core:/code
- ./testdata.py:/code/testdata.py
ports:
- "8000:8000"
depends_on:

87
deploy/dev/testdata.py Normal file
View file

@ -0,0 +1,87 @@
import os
def setup():
from authentication.models import ExtendedUser, EventPermission
from inventory.models import Event
from django.contrib.auth.models import Permission, Group
permissions = ['add_item', 'view_item', 'view_file', 'delete_item', 'change_item']
if not ExtendedUser.objects.filter(username='admin').exists():
admin = ExtendedUser.objects.create_superuser('admin', 'admin@example.com', 'admin')
admin.set_password('admin')
admin.user_permissions.add(*Permission.objects.all())
admin.save()
if not ExtendedUser.objects.filter(username='testuser').exists():
testuser = ExtendedUser.objects.create_user('testuser', 'testuser@example.com', 'testuser')
testuser.set_password('testuser')
testuser.user_permissions.add(*Permission.objects.all())
testuser.save()
team = Group.objects.get(name='Team')
team.permissions.add(
*Permission.objects.all()
)
if not ExtendedUser.objects.filter(username='testuser2').exists():
testuser2 = ExtendedUser.objects.create_user('testuser2', 'testuser2@example.com', 'testuser2')
testuser2.set_password('testuser2')
testuser2.groups.add(team)
testuser2.save()
# hbug = ExtendedUser.objects.get(username='hbug')
if not Event.objects.filter(slug='TEST1').exists():
event1 = Event.objects.create(name='first test event', slug='TEST1',
start='2023-12-18 00:00:00.000000', end='2023-12-27 00:00:00.000000',
pre_start='2023-12-31 00:00:00.000000', post_end='2024-01-04 00:00:00.000000')
if not Event.objects.filter(slug='TEST2').exists():
event2 = Event.objects.create(name='second test event', slug='TEST2',
start='2024-12-18 00:00:00.000000', end='2024-12-27 00:00:00.000000',
pre_start='2024-12-31 00:00:00.000000', post_end='2025-01-04 00:00:00.000000')
# for permission in permissions:
# EventPermission.objects.create(event=event_37c3, user=hbug,
# permission=Permission.objects.get(codename=permission))
# from tickets.models import IssueThread
#
# from mail.models import Email
#
# issue_thread = IssueThread.objects.create(
# name="test",
# event=Event.objects.get(slug='TEST1')
# )
# mail1 = Email.objects.create(
# subject='test subject',
# body='test',
# sender='test1@test',
# recipient='test2@test',
# issue_thread=issue_thread,
# )
# mail1_reply = Email.objects.create(
# subject='Message received',
# body='Thank you for your message.',
# sender='test2@test',
# recipient='test1@test',
# in_reply_to=mail1.reference,
# issue_thread=issue_thread,
# )
def main():
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings")
import django
django.setup()
from django.core.management import call_command
call_command('migrate')
setup()
print('testdata initialised')
if __name__ == '__main__':
main()

View 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>

View 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"/>
@ -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>

View file

@ -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,