From 81b22367f17882e46f7b7eb03a8fcac3aaeb7f18 Mon Sep 17 00:00:00 2001 From: Isak Berg Endresen Date: Mon, 12 May 2025 12:28:48 +0200 Subject: [PATCH 1/5] Send websocket message for comments --- lego/apps/articles/serializers.py | 2 +- lego/apps/comments/action_handlers.py | 6 +++++ lego/apps/comments/constants.py | 4 ++++ lego/apps/comments/serializers/__init__.py | 0 .../comments.py} | 0 lego/apps/comments/serializers/sockets.py | 8 +++++++ lego/apps/comments/views.py | 2 +- lego/apps/comments/websockets.py | 24 +++++++++++++++++++ lego/apps/companies/serializers.py | 2 +- lego/apps/events/serializers/events.py | 2 +- lego/apps/forums/serializers.py | 2 +- lego/apps/gallery/serializers.py | 2 +- lego/apps/meetings/serializers.py | 2 +- lego/apps/polls/serializers.py | 2 +- lego/apps/websockets/groups.py | 7 ++++++ lego/utils/serializers.py | 8 ++++--- 16 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 lego/apps/comments/constants.py create mode 100644 lego/apps/comments/serializers/__init__.py rename lego/apps/comments/{serializers.py => serializers/comments.py} (100%) create mode 100644 lego/apps/comments/serializers/sockets.py create mode 100644 lego/apps/comments/websockets.py diff --git a/lego/apps/articles/serializers.py b/lego/apps/articles/serializers.py index b93adb2b9..e40e386ba 100644 --- a/lego/apps/articles/serializers.py +++ b/lego/apps/articles/serializers.py @@ -2,7 +2,7 @@ from rest_framework.fields import CharField from lego.apps.articles.models import Article -from lego.apps.comments.serializers import CommentSerializer +from lego.apps.comments.serializers.comments import CommentSerializer from lego.apps.content.fields import ContentSerializerField from lego.apps.files.fields import ImageField from lego.apps.tags.serializers import TagSerializerMixin diff --git a/lego/apps/comments/action_handlers.py b/lego/apps/comments/action_handlers.py index 1118554e0..cae56fb36 100644 --- a/lego/apps/comments/action_handlers.py +++ b/lego/apps/comments/action_handlers.py @@ -2,6 +2,8 @@ from lego.apps.action_handlers.registry import register_handler from lego.apps.comments.models import Comment from lego.apps.comments.notifications import CommentReplyNotification +from lego.apps.comments.constants import SOCKET_ADD_SUCCESS, SOCKET_DELETE_SUCCESS +from lego.apps.comments.websockets import notify_comment from lego.apps.feeds.activity import Activity from lego.apps.feeds.feed_manager import feed_manager from lego.apps.feeds.models import NotificationFeed, UserFeed @@ -45,8 +47,11 @@ def handle_create(self, instance, **kwargs): author=author, ) reply_notification.notify() + + notify_comment(SOCKET_ADD_SUCCESS, instance) def handle_delete(self, instance, **kwargs): + notify_comment(SOCKET_DELETE_SUCCESS, instance) if not ( isinstance(instance.parent, ObjectPermissionsModel) and not instance.parent.require_auth @@ -58,6 +63,7 @@ def handle_delete(self, instance, **kwargs): self.manager.remove_activity( activity, [recipient.pk for recipient in recipients], feeds ) + def get_feeds_and_recipients(self, comment): result = [] diff --git a/lego/apps/comments/constants.py b/lego/apps/comments/constants.py new file mode 100644 index 000000000..5744e931f --- /dev/null +++ b/lego/apps/comments/constants.py @@ -0,0 +1,4 @@ +SOCKET_ADD_SUCCESS = "Comment.SOCKET_ADD.SUCCESS" +SOCKET_ADD_FAILURE = "Comment.SOCKET_ADD.FAILURE" +SOCKET_DELETE_SUCCESS = "Comment.SOCKET_DELETE.SUCCESS" +SOCKET_DELETE_FAILURE = "Comment.SOCKET_DELETE.FAILURE" diff --git a/lego/apps/comments/serializers/__init__.py b/lego/apps/comments/serializers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/lego/apps/comments/serializers.py b/lego/apps/comments/serializers/comments.py similarity index 100% rename from lego/apps/comments/serializers.py rename to lego/apps/comments/serializers/comments.py diff --git a/lego/apps/comments/serializers/sockets.py b/lego/apps/comments/serializers/sockets.py new file mode 100644 index 000000000..487cdf6b8 --- /dev/null +++ b/lego/apps/comments/serializers/sockets.py @@ -0,0 +1,8 @@ +from lego.apps.comments.serializers.comments import CommentSerializer +from lego.apps.websockets.serializers import WebsocketSerializer + +from rest_framework import serializers + + +class CommentSocketSerializer(WebsocketSerializer): + payload = CommentSerializer() diff --git a/lego/apps/comments/views.py b/lego/apps/comments/views.py index 0df1a9803..5ba47d317 100644 --- a/lego/apps/comments/views.py +++ b/lego/apps/comments/views.py @@ -1,7 +1,7 @@ from rest_framework import mixins, viewsets from lego.apps.comments.models import Comment -from lego.apps.comments.serializers import CommentSerializer, UpdateCommentSerializer +from lego.apps.comments.serializers.comments import CommentSerializer, UpdateCommentSerializer from lego.apps.permissions.api.views import AllowedPermissionsMixin diff --git a/lego/apps/comments/websockets.py b/lego/apps/comments/websockets.py new file mode 100644 index 000000000..864f50322 --- /dev/null +++ b/lego/apps/comments/websockets.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from lego.apps.comments.serializers.sockets import CommentSocketSerializer +from lego.apps.websockets.groups import group_for_comment_target, group_for_user +from lego.apps.websockets.notifiers import notify_group + +if TYPE_CHECKING: + from lego.apps.comments.models import Comment + + +def notify_comment(action_type: str, comment: Comment, **kwargs): + group = "global" + # group = group_for_comment_target(comment.content_object) + serializer = CommentSocketSerializer( + { + "type": action_type, + "payload": comment, + "meta": kwargs, + } + ) + data = serializer.data + notify_group(group, data) diff --git a/lego/apps/companies/serializers.py b/lego/apps/companies/serializers.py index ad26bd204..3766eec69 100644 --- a/lego/apps/companies/serializers.py +++ b/lego/apps/companies/serializers.py @@ -3,7 +3,7 @@ from rest_framework import serializers from rest_framework.fields import CharField -from lego.apps.comments.serializers import CommentSerializer +from lego.apps.comments.serializers.comments import CommentSerializer from lego.apps.companies.constants import INTERESTED, NOT_CONTACTED from lego.apps.companies.fields import SemesterField from lego.apps.companies.models import ( diff --git a/lego/apps/events/serializers/events.py b/lego/apps/events/serializers/events.py index ee03b6a5d..f4870c782 100644 --- a/lego/apps/events/serializers/events.py +++ b/lego/apps/events/serializers/events.py @@ -4,7 +4,7 @@ from rest_framework import serializers from rest_framework.fields import CharField -from lego.apps.comments.serializers import CommentSerializer +from lego.apps.comments.serializers.comments import CommentSerializer from lego.apps.companies.fields import CompanyField from lego.apps.companies.models import Company from lego.apps.content.fields import ContentSerializerField diff --git a/lego/apps/forums/serializers.py b/lego/apps/forums/serializers.py index d7b537ec7..15b816a9b 100644 --- a/lego/apps/forums/serializers.py +++ b/lego/apps/forums/serializers.py @@ -1,7 +1,7 @@ from rest_framework import serializers from rest_framework.fields import CharField -from lego.apps.comments.serializers import CommentSerializer +from lego.apps.comments.serializers.comments import CommentSerializer from lego.apps.content.fields import ContentSerializerField from lego.apps.forums.models import Forum, Thread from lego.apps.users.serializers.users import PublicUserSerializer diff --git a/lego/apps/gallery/serializers.py b/lego/apps/gallery/serializers.py index 6ce8b5504..eadebd589 100644 --- a/lego/apps/gallery/serializers.py +++ b/lego/apps/gallery/serializers.py @@ -1,7 +1,7 @@ from rest_framework import serializers from rest_framework.fields import CharField -from lego.apps.comments.serializers import CommentSerializer +from lego.apps.comments.serializers.comments import CommentSerializer from lego.apps.events.fields import PublicEventField from lego.apps.events.models import Event from lego.apps.files.fields import FileField, ImageField diff --git a/lego/apps/meetings/serializers.py b/lego/apps/meetings/serializers.py index e2abd9303..4964d2438 100644 --- a/lego/apps/meetings/serializers.py +++ b/lego/apps/meetings/serializers.py @@ -1,7 +1,7 @@ from rest_framework import serializers from rest_framework.fields import CharField -from lego.apps.comments.serializers import CommentSerializer +from lego.apps.comments.serializers.comments import CommentSerializer from lego.apps.content.fields import ContentSerializerField from lego.apps.meetings import constants from lego.apps.meetings.models import Meeting, MeetingInvitation, ReportChangelog diff --git a/lego/apps/polls/serializers.py b/lego/apps/polls/serializers.py index fec958d2b..9858b9c5d 100644 --- a/lego/apps/polls/serializers.py +++ b/lego/apps/polls/serializers.py @@ -4,7 +4,7 @@ from rest_framework import serializers from rest_framework.fields import CharField, IntegerField -from lego.apps.comments.serializers import CommentSerializer +from lego.apps.comments.serializers.comments import CommentSerializer from lego.apps.polls.models import Option, Poll from lego.apps.tags.serializers import TagSerializerMixin from lego.utils.serializers import BasisModelSerializer diff --git a/lego/apps/websockets/groups.py b/lego/apps/websockets/groups.py index c147737ce..1f53f1ee6 100644 --- a/lego/apps/websockets/groups.py +++ b/lego/apps/websockets/groups.py @@ -2,6 +2,8 @@ from typing import TYPE_CHECKING +from lego.utils.content_types import instance_to_string + if TYPE_CHECKING: from lego.apps.events.models import Event @@ -12,3 +14,8 @@ def group_for_user(user_id: str) -> str: def group_for_event(event: Event, has_registrations_access: bool) -> str: return f"event-{'full' if has_registrations_access else 'limited'}-{event.pk}" + + +def group_for_comment_target(content_target) -> str: + content_target_string = instance_to_string(content_target) + return f"comment-{content_target_string}" diff --git a/lego/utils/serializers.py b/lego/utils/serializers.py index d683a46ba..36d039864 100644 --- a/lego/utils/serializers.py +++ b/lego/utils/serializers.py @@ -3,7 +3,7 @@ from lego.apps.users.fields import AbakusGroupField, PublicUserField from lego.apps.users.models import AbakusGroup, User -from lego.utils.content_types import string_to_instance +from lego.utils.content_types import instance_to_string, string_to_instance class GenericRelationField(serializers.CharField): @@ -14,11 +14,13 @@ class GenericRelationField(serializers.CharField): } def __init__(self, *args, **kwargs): - kwargs["write_only"] = True super().__init__(*args, **kwargs) def to_representation(self, value): - return None + try: + return instance_to_string(value) + except Exception: + pass def to_internal_value(self, data): try: From 1db0bd1b3292a35a9562256138f6ba6e153ed2c0 Mon Sep 17 00:00:00 2001 From: Isak Berg Endresen Date: Mon, 12 May 2025 12:47:17 +0200 Subject: [PATCH 2/5] Fixme --- lego/apps/comments/action_handlers.py | 6 ++---- lego/apps/comments/serializers/sockets.py | 4 ++-- lego/apps/comments/views.py | 5 ++++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lego/apps/comments/action_handlers.py b/lego/apps/comments/action_handlers.py index cae56fb36..778bce22c 100644 --- a/lego/apps/comments/action_handlers.py +++ b/lego/apps/comments/action_handlers.py @@ -1,8 +1,8 @@ from lego.apps.action_handlers.handler import Handler from lego.apps.action_handlers.registry import register_handler +from lego.apps.comments.constants import SOCKET_ADD_SUCCESS, SOCKET_DELETE_SUCCESS from lego.apps.comments.models import Comment from lego.apps.comments.notifications import CommentReplyNotification -from lego.apps.comments.constants import SOCKET_ADD_SUCCESS, SOCKET_DELETE_SUCCESS from lego.apps.comments.websockets import notify_comment from lego.apps.feeds.activity import Activity from lego.apps.feeds.feed_manager import feed_manager @@ -27,6 +27,7 @@ def get_activity(comment, reply=False): ) def handle_create(self, instance, **kwargs): + notify_comment(SOCKET_ADD_SUCCESS, instance) activity = self.get_activity(instance) author = instance.created_by for feeds, recipients in self.get_feeds_and_recipients(instance): @@ -47,8 +48,6 @@ def handle_create(self, instance, **kwargs): author=author, ) reply_notification.notify() - - notify_comment(SOCKET_ADD_SUCCESS, instance) def handle_delete(self, instance, **kwargs): notify_comment(SOCKET_DELETE_SUCCESS, instance) @@ -63,7 +62,6 @@ def handle_delete(self, instance, **kwargs): self.manager.remove_activity( activity, [recipient.pk for recipient in recipients], feeds ) - def get_feeds_and_recipients(self, comment): result = [] diff --git a/lego/apps/comments/serializers/sockets.py b/lego/apps/comments/serializers/sockets.py index 487cdf6b8..ac87af07b 100644 --- a/lego/apps/comments/serializers/sockets.py +++ b/lego/apps/comments/serializers/sockets.py @@ -1,8 +1,8 @@ +from rest_framework import serializers + from lego.apps.comments.serializers.comments import CommentSerializer from lego.apps.websockets.serializers import WebsocketSerializer -from rest_framework import serializers - class CommentSocketSerializer(WebsocketSerializer): payload = CommentSerializer() diff --git a/lego/apps/comments/views.py b/lego/apps/comments/views.py index 5ba47d317..f9094c2ad 100644 --- a/lego/apps/comments/views.py +++ b/lego/apps/comments/views.py @@ -1,7 +1,10 @@ from rest_framework import mixins, viewsets from lego.apps.comments.models import Comment -from lego.apps.comments.serializers.comments import CommentSerializer, UpdateCommentSerializer +from lego.apps.comments.serializers.comments import ( + CommentSerializer, + UpdateCommentSerializer, +) from lego.apps.permissions.api.views import AllowedPermissionsMixin From 791e37792d300a6f673be93088e2808cfcef2228 Mon Sep 17 00:00:00 2001 From: Isak Berg Endresen Date: Thu, 15 May 2025 11:11:04 +0200 Subject: [PATCH 3/5] Fixme --- lego/apps/comments/serializers/sockets.py | 2 -- lego/apps/comments/websockets.py | 3 +-- lego/apps/websockets/groups.py | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lego/apps/comments/serializers/sockets.py b/lego/apps/comments/serializers/sockets.py index ac87af07b..1f2934070 100644 --- a/lego/apps/comments/serializers/sockets.py +++ b/lego/apps/comments/serializers/sockets.py @@ -1,5 +1,3 @@ -from rest_framework import serializers - from lego.apps.comments.serializers.comments import CommentSerializer from lego.apps.websockets.serializers import WebsocketSerializer diff --git a/lego/apps/comments/websockets.py b/lego/apps/comments/websockets.py index 864f50322..0ba2615ef 100644 --- a/lego/apps/comments/websockets.py +++ b/lego/apps/comments/websockets.py @@ -3,7 +3,6 @@ from typing import TYPE_CHECKING from lego.apps.comments.serializers.sockets import CommentSocketSerializer -from lego.apps.websockets.groups import group_for_comment_target, group_for_user from lego.apps.websockets.notifiers import notify_group if TYPE_CHECKING: @@ -12,7 +11,7 @@ def notify_comment(action_type: str, comment: Comment, **kwargs): group = "global" - # group = group_for_comment_target(comment.content_object) + # group = group_for_content_target(comment.content_object) serializer = CommentSocketSerializer( { "type": action_type, diff --git a/lego/apps/websockets/groups.py b/lego/apps/websockets/groups.py index 1f53f1ee6..9e4afbb4b 100644 --- a/lego/apps/websockets/groups.py +++ b/lego/apps/websockets/groups.py @@ -16,6 +16,6 @@ def group_for_event(event: Event, has_registrations_access: bool) -> str: return f"event-{'full' if has_registrations_access else 'limited'}-{event.pk}" -def group_for_comment_target(content_target) -> str: +def group_for_content_target(content_target) -> str: content_target_string = instance_to_string(content_target) return f"comment-{content_target_string}" From 212caca13bbb993510cea18b95aa446301859684 Mon Sep 17 00:00:00 2001 From: Isak Berg Endresen Date: Thu, 29 May 2025 17:00:06 +0200 Subject: [PATCH 4/5] Add support for dynamically joining ws groups --- lego/apps/comments/websockets.py | 5 ++- lego/apps/websockets/constants.py | 18 ++++++++ lego/apps/websockets/consumers.py | 74 ++++++++++++++++++++++++++----- lego/apps/websockets/groups.py | 42 ++++++++++++++++-- 4 files changed, 124 insertions(+), 15 deletions(-) create mode 100644 lego/apps/websockets/constants.py diff --git a/lego/apps/comments/websockets.py b/lego/apps/comments/websockets.py index 0ba2615ef..e3f2735dc 100644 --- a/lego/apps/comments/websockets.py +++ b/lego/apps/comments/websockets.py @@ -4,14 +4,15 @@ from lego.apps.comments.serializers.sockets import CommentSocketSerializer from lego.apps.websockets.notifiers import notify_group +from lego.apps.websockets.groups import group_for_content_model if TYPE_CHECKING: from lego.apps.comments.models import Comment def notify_comment(action_type: str, comment: Comment, **kwargs): - group = "global" - # group = group_for_content_target(comment.content_object) + # group = "global" + group = group_for_content_model(comment) serializer = CommentSocketSerializer( { "type": action_type, diff --git a/lego/apps/websockets/constants.py b/lego/apps/websockets/constants.py new file mode 100644 index 000000000..a9946defc --- /dev/null +++ b/lego/apps/websockets/constants.py @@ -0,0 +1,18 @@ +WS_STATUS_CONNECTED = "CONNECTED" +WS_STATUS_CLOSED = "CLOSED" +WS_STATUS_ERROR = "ERROR" + +WS_GROUP_TYPES = [ + "global" + "user", + "event", + "comment" +] + +WS_GROUP_JOIN_BEGIN = "Websockets.GROUP_JOIN.BEGIN" +WS_GROUP_JOIN_SUCCESS = "Websockets.GROUP_JOIN.SUCCESS" +WS_GROUP_JOIN_FAILURE = "Websockets.GROUP_JOIN.FAILURE" + +WS_GROUP_LEAVE_BEGIN = "Websockets.GROUP_LEAVE.BEGIN" +WS_GROUP_LEAVE_SUCCESS = "Websockets.GROUP_LEAVE.SUCCESS" +WS_GROUP_LEAVE_FAILURE = "Websockets.GROUP_LEAVE.FAILURE" diff --git a/lego/apps/websockets/consumers.py b/lego/apps/websockets/consumers.py index f841c475b..089a66167 100644 --- a/lego/apps/websockets/consumers.py +++ b/lego/apps/websockets/consumers.py @@ -1,24 +1,78 @@ from asgiref.sync import AsyncToSync -from channels.generic.websocket import WebsocketConsumer +from channels.generic.websocket import JsonWebsocketConsumer from lego.apps.events.websockets import find_event_groups +from lego.apps.websockets.groups import group_for_user, verify_group_access from lego.apps.users.models import User -from lego.apps.websockets.groups import group_for_user - +from lego.apps.websockets import constants def find_groups(user: User): return ["global", group_for_user(user.pk)] + find_event_groups(user) -class GroupConsumer(WebsocketConsumer): +class GroupConsumer(JsonWebsocketConsumer): + """ + Custom consumer for handling websocket groups. + + Create own logic for tracking user groups as the WebsocketConsumer groups functionality + does not have access to the user object. + """ + user_groups = set() + user = None + + def debug(self, message): + if self.user: + print(f"[{self.user.username.upper()}] {message}") + else: + print(f"[NO USER IN SCOPE] {message}") + def connect(self): - self.accept() - for group in find_groups(self.scope["user"]): + user = self.scope["user"] + for group in find_groups(user): AsyncToSync(self.channel_layer.group_add)(group, self.channel_name) - - def disconnect(self, message): - for group in find_groups(self.scope["user"]): + self.user = user + self.user_groups = set(find_groups(user)) + self.accept() + + def disconnect(self, code): + for group in self.user_groups: AsyncToSync(self.channel_layer.group_discard)(group, self.channel_name) - + self.user_groups.clear() + + def receive_json(self, content, **kwargs): + type = content.get("type") + payload = content.get("payload") + + if type == constants.WS_GROUP_JOIN_BEGIN: + group = payload.get("group") + if self.user and verify_group_access(self.user, group): + AsyncToSync(self.channel_layer.group_add)(group, self.channel_name) + self.user_groups.add(group) + self.send_message(constants.WS_GROUP_JOIN_SUCCESS, payload={ "group": group }) + else: + self.send_message(constants.WS_GROUP_JOIN_FAILURE, payload={ "group": group }) + + if type == constants.WS_GROUP_LEAVE_BEGIN: + group = payload.get("group") + if group in self.groups: + AsyncToSync(self.channel_layer.group_discard)(group, self.channel_name) + self.user_groups.remove(group) + self.send_message(constants.WS_GROUP_LEAVE_SUCCESS) + + + def send_message(self, type: str, payload=None, meta=None): + """ + Send message on standardised format. + """ + content = { + "type": type, + "payload": payload, + "meta": meta + } + self.send_json(content) + def notification_message(self, event): self.send(text_data=event["text"]) + + + diff --git a/lego/apps/websockets/groups.py b/lego/apps/websockets/groups.py index 9e4afbb4b..437d3a98e 100644 --- a/lego/apps/websockets/groups.py +++ b/lego/apps/websockets/groups.py @@ -2,7 +2,10 @@ from typing import TYPE_CHECKING +from lego.apps.users.models import User from lego.utils.content_types import instance_to_string +from lego.utils.content_types import string_to_instance +from lego.apps.permissions.constants import LIST if TYPE_CHECKING: from lego.apps.events.models import Event @@ -16,6 +19,39 @@ def group_for_event(event: Event, has_registrations_access: bool) -> str: return f"event-{'full' if has_registrations_access else 'limited'}-{event.pk}" -def group_for_content_target(content_target) -> str: - content_target_string = instance_to_string(content_target) - return f"comment-{content_target_string}" +def group_for_content_model(model) -> str: + modelname = model._meta.model_name + content_target_string = instance_to_string(model.content_object) + return f"{modelname}-{content_target_string}" + + +def verify_group_access(user: User, group): + if not group: + return False + + group_type, rest = group.split("-", 1) + + if group_type == "comment": + content_target = string_to_instance(rest) + if user and content_target: + return user.has_perm(LIST, content_target) + + # if group_type == WS_GROUP_TYPE_USER: + # user_id = rest + # return user_id == str(user.pk) + + # if group_type == WS_GROUP_TYPE_EVENT: + # event_access, event_id = rest.split("-", 1) + # """Not implemented""" + + + return False + + +def stringify_group(group): + pass + + # if content_target: + # return f"{group.type}-{content_target}" + + # return str(group.type) \ No newline at end of file From 0ea7bee7ab1dc2252c4158a8eae2aa997f8eca27 Mon Sep 17 00:00:00 2001 From: Isak Berg Endresen Date: Thu, 29 May 2025 17:31:38 +0200 Subject: [PATCH 5/5] Fixme --- lego/apps/comments/websockets.py | 2 +- lego/apps/websockets/constants.py | 7 +---- lego/apps/websockets/consumers.py | 44 +++++++++++++++---------------- lego/apps/websockets/groups.py | 26 +++++++++--------- 4 files changed, 35 insertions(+), 44 deletions(-) diff --git a/lego/apps/comments/websockets.py b/lego/apps/comments/websockets.py index e3f2735dc..8001a59b2 100644 --- a/lego/apps/comments/websockets.py +++ b/lego/apps/comments/websockets.py @@ -3,8 +3,8 @@ from typing import TYPE_CHECKING from lego.apps.comments.serializers.sockets import CommentSocketSerializer -from lego.apps.websockets.notifiers import notify_group from lego.apps.websockets.groups import group_for_content_model +from lego.apps.websockets.notifiers import notify_group if TYPE_CHECKING: from lego.apps.comments.models import Comment diff --git a/lego/apps/websockets/constants.py b/lego/apps/websockets/constants.py index a9946defc..5a0020b39 100644 --- a/lego/apps/websockets/constants.py +++ b/lego/apps/websockets/constants.py @@ -2,12 +2,7 @@ WS_STATUS_CLOSED = "CLOSED" WS_STATUS_ERROR = "ERROR" -WS_GROUP_TYPES = [ - "global" - "user", - "event", - "comment" -] +WS_GROUP_TYPES = ["global" "user", "event", "comment"] WS_GROUP_JOIN_BEGIN = "Websockets.GROUP_JOIN.BEGIN" WS_GROUP_JOIN_SUCCESS = "Websockets.GROUP_JOIN.SUCCESS" diff --git a/lego/apps/websockets/consumers.py b/lego/apps/websockets/consumers.py index 089a66167..d98cd6f59 100644 --- a/lego/apps/websockets/consumers.py +++ b/lego/apps/websockets/consumers.py @@ -2,9 +2,10 @@ from channels.generic.websocket import JsonWebsocketConsumer from lego.apps.events.websockets import find_event_groups -from lego.apps.websockets.groups import group_for_user, verify_group_access from lego.apps.users.models import User from lego.apps.websockets import constants +from lego.apps.websockets.groups import group_for_user, verify_group_access + def find_groups(user: User): return ["global", group_for_user(user.pk)] + find_event_groups(user) @@ -13,66 +14,63 @@ def find_groups(user: User): class GroupConsumer(JsonWebsocketConsumer): """ Custom consumer for handling websocket groups. - + Create own logic for tracking user groups as the WebsocketConsumer groups functionality does not have access to the user object. """ + user_groups = set() user = None - + def debug(self, message): if self.user: print(f"[{self.user.username.upper()}] {message}") else: print(f"[NO USER IN SCOPE] {message}") - + def connect(self): user = self.scope["user"] for group in find_groups(user): AsyncToSync(self.channel_layer.group_add)(group, self.channel_name) - self.user = user + self.user = user self.user_groups = set(find_groups(user)) self.accept() - + def disconnect(self, code): for group in self.user_groups: AsyncToSync(self.channel_layer.group_discard)(group, self.channel_name) self.user_groups.clear() - + def receive_json(self, content, **kwargs): type = content.get("type") payload = content.get("payload") - + if type == constants.WS_GROUP_JOIN_BEGIN: group = payload.get("group") if self.user and verify_group_access(self.user, group): AsyncToSync(self.channel_layer.group_add)(group, self.channel_name) self.user_groups.add(group) - self.send_message(constants.WS_GROUP_JOIN_SUCCESS, payload={ "group": group }) + self.send_message( + constants.WS_GROUP_JOIN_SUCCESS, payload={"group": group} + ) else: - self.send_message(constants.WS_GROUP_JOIN_FAILURE, payload={ "group": group }) - + self.send_message( + constants.WS_GROUP_JOIN_FAILURE, payload={"group": group} + ) + if type == constants.WS_GROUP_LEAVE_BEGIN: group = payload.get("group") if group in self.groups: AsyncToSync(self.channel_layer.group_discard)(group, self.channel_name) self.user_groups.remove(group) self.send_message(constants.WS_GROUP_LEAVE_SUCCESS) - - - def send_message(self, type: str, payload=None, meta=None): + + def send_message(self, type: str, payload=None, meta=None): """ Send message on standardised format. """ - content = { - "type": type, - "payload": payload, - "meta": meta - } + content = {"type": type, "payload": payload, "meta": meta} self.send_json(content) - + def notification_message(self, event): self.send(text_data=event["text"]) - - - diff --git a/lego/apps/websockets/groups.py b/lego/apps/websockets/groups.py index 437d3a98e..3d97123a2 100644 --- a/lego/apps/websockets/groups.py +++ b/lego/apps/websockets/groups.py @@ -2,10 +2,9 @@ from typing import TYPE_CHECKING -from lego.apps.users.models import User -from lego.utils.content_types import instance_to_string -from lego.utils.content_types import string_to_instance from lego.apps.permissions.constants import LIST +from lego.apps.users.models import User +from lego.utils.content_types import instance_to_string, string_to_instance if TYPE_CHECKING: from lego.apps.events.models import Event @@ -28,30 +27,29 @@ def group_for_content_model(model) -> str: def verify_group_access(user: User, group): if not group: return False - + group_type, rest = group.split("-", 1) - + if group_type == "comment": content_target = string_to_instance(rest) if user and content_target: - return user.has_perm(LIST, content_target) - + return user.has_perm(LIST, content_target) + # if group_type == WS_GROUP_TYPE_USER: # user_id = rest # return user_id == str(user.pk) - + # if group_type == WS_GROUP_TYPE_EVENT: # event_access, event_id = rest.split("-", 1) # """Not implemented""" - - + return False - + def stringify_group(group): pass - + # if content_target: # return f"{group.type}-{content_target}" - - # return str(group.type) \ No newline at end of file + + # return str(group.type)