diff --git a/core/inventory/api_v2.py b/core/inventory/api_v2.py index 60f0292..39052f8 100644 --- a/core/inventory/api_v2.py +++ b/core/inventory/api_v2.py @@ -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[\w-]+)/items/$', item, name='item'), re_path(r'^(?P[\w-]+)/items/(?P[-A-Za-z0-9+/]*={0,3})/$', search_items, name='search_items'), re_path(r'^(?P[\w-]+)/item/$', item, name='item'), + re_path(r'^(?P[\w-]+)/item/(?P\d+)/comment/$', add_comment, name='add_comment'), re_path(r'^(?P[\w-]+)/item/(?P\d+)/$', item_by_id, name='item_by_id'), ] diff --git a/core/inventory/serializers.py b/core/inventory/serializers.py index f13235c..c349e19 100644 --- a/core/inventory/serializers.py +++ b/core/inventory/serializers.py @@ -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) diff --git a/core/testdata.py b/core/testdata.py new file mode 100644 index 0000000..e69de29 diff --git a/deploy/dev/docker-compose.yml b/deploy/dev/docker-compose.yml index dff5ab3..64a15af 100644 --- a/deploy/dev/docker-compose.yml +++ b/deploy/dev/docker-compose.yml @@ -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: diff --git a/deploy/dev/testdata.py b/deploy/dev/testdata.py new file mode 100644 index 0000000..767f644 --- /dev/null +++ b/deploy/dev/testdata.py @@ -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() diff --git a/web/src/components/TimelineMail.vue b/web/src/components/TimelineMail.vue index 065d56a..6c43267 100644 --- a/web/src/components/TimelineMail.vue +++ b/web/src/components/TimelineMail.vue @@ -1,6 +1,5 @@