Skip to content

Commit b882338

Browse files
authored
baseapp-chats: edit group details (#214)
* BA-2022 group title page * BA-2022 versioning * BA-2022 Fix migrations * BA-2022 Versioning * Revert "BA-2022 Versioning" This reverts commit ec610b2.
1 parent f1b5821 commit b882338

File tree

9 files changed

+485
-29
lines changed

9 files changed

+485
-29
lines changed

baseapp-chats/baseapp_chats/base.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ def save(self, *args, **kwargs):
153153
class AbstractChatRoomParticipant(TimeStampedModel, RelayModel):
154154
class ChatRoomParticipantRoles(models.IntegerChoices):
155155
MEMBER = 1, _("member")
156-
ADMIM = 2, _("admin")
156+
ADMIN = 2, _("admin")
157157

158158
@property
159159
def description(self):
@@ -178,7 +178,7 @@ def description(self):
178178

179179
class Meta:
180180
abstract = True
181-
ordering = ["created"]
181+
ordering = ["-role", "profile__name"]
182182

183183
@classmethod
184184
def get_graphql_object_type(cls):

baseapp-chats/baseapp_chats/graphql/mutations.py

+154-2
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
from django.utils import timezone
1313
from django.utils.translation import gettext_lazy as _
1414
from graphene_django.types import ErrorType
15+
from rest_framework import serializers
1516

1617
from baseapp_chats.graphql.subscriptions import ChatRoomOnMessagesCountUpdate
1718
from baseapp_chats.utils import send_message, send_new_chat_message_notification
1819

1920
ChatRoom = swapper.load_model("baseapp_chats", "ChatRoom")
2021
ChatRoomParticipant = swapper.load_model("baseapp_chats", "ChatRoomParticipant")
22+
ChatRoomParticipantRoles = ChatRoomParticipant.ChatRoomParticipantRoles
2123
Message = swapper.load_model("baseapp_chats", "Message")
2224
MessageStatus = swapper.load_model("baseapp_chats", "MessageStatus")
2325
UnreadMessageCount = swapper.load_model("baseapp_chats", "UnreadMessageCount")
@@ -32,6 +34,10 @@
3234
ChatRoomParticipantObjectType = ChatRoomParticipant.get_graphql_object_type()
3335

3436

37+
class ImageSerializer(serializers.Serializer):
38+
image = serializers.ImageField(required=False, allow_null=True)
39+
40+
3541
class ChatRoomCreate(RelayMutation):
3642
room = graphene.Field(ChatRoomObjectType._meta.connection.Edge)
3743
profile = graphene.Field(ProfileObjectType)
@@ -129,17 +135,32 @@ def mutate_and_get_payload(cls, root, info, profile_id, participants, is_group,
129135
),
130136
)
131137
image = info.context.FILES.get("image", None)
138+
serializer = ImageSerializer(data={"image": image})
139+
if not serializer.is_valid():
140+
return ChatRoomCreate(
141+
errors=[ErrorType(field="image", messages=serializer.errors["image"])]
142+
)
143+
132144
room = ChatRoom.objects.create(
133145
created_by=info.context.user,
134146
last_message_time=timezone.now(),
135147
is_group=is_group,
136148
title=title,
137-
image=image,
149+
image=serializer.validated_data["image"],
138150
)
139151

140152
created_participants = ChatRoomParticipant.objects.bulk_create(
141153
[
142-
ChatRoomParticipant(profile=participant, room=room, accepted_at=timezone.now())
154+
ChatRoomParticipant(
155+
profile=participant,
156+
room=room,
157+
role=(
158+
ChatRoomParticipantRoles.ADMIN
159+
if participant == profile
160+
else ChatRoomParticipantRoles.MEMBER
161+
),
162+
accepted_at=timezone.now(),
163+
)
143164
for participant in participants
144165
]
145166
)
@@ -155,6 +176,136 @@ def mutate_and_get_payload(cls, root, info, profile_id, participants, is_group,
155176
)
156177

157178

179+
class ChatRoomUpdate(RelayMutation):
180+
room = graphene.Field(ChatRoomObjectType._meta.connection.Edge)
181+
182+
class Input:
183+
room_id = graphene.ID(required=True)
184+
profile_id = graphene.ID(required=True)
185+
title = graphene.String(required=False)
186+
delete_image = graphene.Boolean(default_value=False)
187+
add_participants = graphene.List(graphene.ID, default_value=[])
188+
remove_participants = graphene.List(graphene.ID, default_value=[])
189+
190+
@classmethod
191+
@login_required
192+
def mutate_and_get_payload(
193+
cls,
194+
root,
195+
info,
196+
room_id,
197+
profile_id,
198+
delete_image,
199+
add_participants,
200+
remove_participants,
201+
**input,
202+
):
203+
room = get_obj_from_relay_id(info, room_id)
204+
profile = get_obj_from_relay_id(info, profile_id)
205+
206+
if not room or not getattr(room, "is_group", False):
207+
return ChatRoomUpdate(
208+
errors=[
209+
ErrorType(
210+
field="room_id",
211+
messages=[_("This room cannot be updated")],
212+
)
213+
]
214+
)
215+
216+
if not info.context.user.has_perm("baseapp_profiles.use_profile", profile):
217+
return ChatRoomUpdate(
218+
errors=[
219+
ErrorType(
220+
field="profile_id",
221+
messages=[_("You don't have permission to act as this profile")],
222+
)
223+
]
224+
)
225+
226+
add_participants = [
227+
get_obj_from_relay_id(info, participant) for participant in add_participants
228+
]
229+
add_participants = [
230+
participant for participant in add_participants if participant is not None
231+
]
232+
add_participants_ids = [participant.pk for participant in add_participants]
233+
234+
remove_participants = [
235+
get_obj_from_relay_id(info, participant) for participant in remove_participants
236+
]
237+
remove_participants = [
238+
participant for participant in remove_participants if participant is not None
239+
]
240+
remove_participants_ids = [participant.pk for participant in remove_participants]
241+
242+
# Check if added participants are blocked
243+
if Block.objects.filter(
244+
Q(actor_id=profile.id, target_id__in=add_participants_ids)
245+
| Q(actor_id__in=add_participants_ids, target_id=profile.id)
246+
).exists():
247+
return ChatRoomUpdate(
248+
errors=[
249+
ErrorType(
250+
field="add_participants",
251+
messages=[_("You can't add those participants to a chatroom")],
252+
)
253+
]
254+
)
255+
256+
if not info.context.user.has_perm(
257+
"baseapp_chats.modify_chatroom",
258+
{"profile": profile, "room": room, "add_participants": add_participants},
259+
):
260+
return ChatRoomUpdate(
261+
errors=[
262+
ErrorType(
263+
field="room_id",
264+
messages=[_("You don't have permission to update this room")],
265+
)
266+
]
267+
)
268+
269+
title = input.get("title", None)
270+
if title is not None:
271+
room.title = title
272+
273+
image = info.context.FILES.get("image", None)
274+
serializer = ImageSerializer(data={"image": image})
275+
if not serializer.is_valid():
276+
return ChatRoomUpdate(
277+
errors=[ErrorType(field="image", messages=serializer.errors["image"])]
278+
)
279+
280+
if image is not None:
281+
room.image = serializer.validated_data["image"]
282+
elif delete_image:
283+
room.image = None
284+
285+
room.save()
286+
287+
for participant in remove_participants_ids:
288+
# TODO: Delete participant
289+
# ChatRoomParticipant.objects.filter(
290+
# profile_id=participant.pk, room=room
291+
# ).delete()
292+
pass
293+
294+
for participant in add_participants:
295+
# TODO: Add participant
296+
# ChatRoomParticipant.objects.create(
297+
# profile=participant, room=room, accepted_at=timezone.now()
298+
# )
299+
# Subscriptions?
300+
pass
301+
302+
return ChatRoomUpdate(
303+
room=ChatRoomObjectType._meta.connection.Edge(
304+
node=room,
305+
),
306+
)
307+
308+
158309
class ChatRoomSendMessage(RelayMutation):
159310
message = graphene.Field(MessageObjectType._meta.connection.Edge)
160311

@@ -417,6 +568,7 @@ def mutate_and_get_payload(cls, root, info, room_id, profile_id, archive, **inpu
417568

418569
class ChatsMutations(object):
419570
chat_room_create = ChatRoomCreate.Field()
571+
chat_room_update = ChatRoomUpdate.Field()
420572
chat_room_send_message = ChatRoomSendMessage.Field()
421573
chat_room_read_messages = ChatRoomReadMessages.Field()
422574
chat_room_unread = ChatRoomUnread.Field()

baseapp-chats/baseapp_chats/graphql/object_types.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,20 @@
1515
Profile = swapper.load_model("baseapp_profiles", "Profile")
1616
ChatRoom = swapper.load_model("baseapp_chats", "ChatRoom")
1717
ChatRoomParticipant = swapper.load_model("baseapp_chats", "ChatRoomParticipant")
18+
ChatRoomParticipantRoleTypesEnum = graphene.Enum.from_enum(
19+
ChatRoomParticipant.ChatRoomParticipantRoles
20+
)
1821
UnreadMessageCount = swapper.load_model("baseapp_chats", "UnreadMessageCount")
1922
Message = swapper.load_model("baseapp_chats", "Message")
2023

2124

2225
class BaseChatRoomParticipantObjectType:
26+
role = graphene.Field(ChatRoomParticipantRoleTypesEnum)
27+
2328
class Meta:
2429
interfaces = (relay.Node,)
2530
model = ChatRoomParticipant
26-
fields = ("id", "has_archived_room", "profile")
31+
fields = ("id", "has_archived_room", "profile", "role")
2732
filter_fields = ("profile__target_content_type",)
2833

2934

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Generated by Django 5.0.8 on 2025-01-10 12:02
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("baseapp_chats", "0007_messagestatus_increment_unread_count"),
10+
]
11+
12+
operations = [
13+
migrations.AlterModelOptions(
14+
name="chatroomparticipant",
15+
options={"ordering": ["-role", "profile__name"]},
16+
),
17+
]

baseapp-chats/baseapp_chats/permissions.py

+58-21
Original file line numberDiff line numberDiff line change
@@ -3,45 +3,82 @@
33
from django.db import models
44

55
ChatRoom = swapper.load_model("baseapp_chats", "ChatRoom")
6+
ChatRoomParticipant = swapper.load_model("baseapp_chats", "ChatRoomParticipant")
67
Block = swapper.load_model("baseapp_blocks", "Block")
78
Profile = swapper.load_model("baseapp_profiles", "Profile")
89

910

1011
class ChatsPermissionsBackend(BaseBackend):
11-
def has_perm(self, user_obj, perm, obj=None):
12-
if perm == "baseapp_chats.add_chatroom" and user_obj.is_authenticated:
13-
if isinstance(obj, dict):
14-
participants = obj["participants"]
15-
current_profile = obj["profile"]
12+
def can_add_chatroom(self, user_obj, obj):
13+
if isinstance(obj, dict):
14+
participants = obj["participants"]
15+
current_profile = obj["profile"]
16+
17+
if len(participants) < 2:
18+
return False
1619

17-
if len(participants) < 2:
18-
return False
20+
if current_profile:
21+
my_profile_ids = [current_profile.id]
22+
else:
23+
my_profile_ids = Profile.objects.filter_user_profiles(user_obj).values_list(
24+
"id", flat=True
25+
)
1926

20-
if current_profile:
21-
my_profile_ids = [current_profile.id]
22-
else:
23-
my_profile_ids = Profile.objects.filter_user_profiles(user_obj).values_list(
24-
"id", flat=True
25-
)
27+
participant_profile_ids = [participant.pk for participant in participants]
2628

27-
participant_profile_ids = [participant.pk for participant in participants]
29+
blocks_qs = Block.objects.filter(
30+
models.Q(actor_id__in=my_profile_ids, target_id__in=participant_profile_ids)
31+
| models.Q(actor_id__in=participant_profile_ids, target_id__in=my_profile_ids)
32+
)
2833

29-
blocks_qs = Block.objects.filter(
30-
models.Q(actor_id__in=my_profile_ids, target_id__in=participant_profile_ids)
31-
| models.Q(actor_id__in=participant_profile_ids, target_id__in=my_profile_ids)
32-
)
34+
if blocks_qs.exists():
35+
return False
3336

34-
if blocks_qs.exists():
35-
return False
37+
return True
3638

37-
return True
39+
def can_modify_chatroom(self, user_obj, obj):
40+
if isinstance(obj, dict):
41+
room = obj["room"]
42+
add_participants = obj["add_participants"]
43+
current_profile = obj["profile"]
44+
45+
if not getattr(room, "is_group", False):
46+
return False
47+
48+
chat_room_participant = ChatRoomParticipant.objects.filter(
49+
room=room, profile_id=current_profile.pk
50+
).first()
51+
if (
52+
not chat_room_participant
53+
or not chat_room_participant.role
54+
== ChatRoomParticipant.ChatRoomParticipantRoles.ADMIN
55+
):
56+
return False
57+
58+
participant_profile_ids = [participant.pk for participant in add_participants]
59+
60+
blocks_qs = Block.objects.filter(
61+
models.Q(actor_id=current_profile.id, target_id__in=participant_profile_ids)
62+
| models.Q(actor_id__in=participant_profile_ids, target_id=current_profile.id)
63+
)
64+
65+
if blocks_qs.exists():
66+
return False
67+
68+
return True
69+
70+
def has_perm(self, user_obj, perm, obj=None):
71+
if perm == "baseapp_chats.add_chatroom" and user_obj.is_authenticated:
72+
return self.can_add_chatroom(user_obj, obj)
3873
if perm == "baseapp_chats.add_chatroom_with_profile":
3974
return user_obj.has_perm("baseapp_profiles.use_profile", obj)
4075
if perm == "baseapp_chats.delete_chat":
4176
if isinstance(obj, ChatRoom):
4277
return obj.user_id == user_obj.id or user_obj.has_perm(
4378
"baseapp_profiles.use_profile", obj.actor
4479
)
80+
if perm == "baseapp_chats.modify_chatroom":
81+
return self.can_modify_chatroom(user_obj, obj)
4582
if perm == "baseapp_chats.view_chatroom":
4683
my_profile_ids = Profile.objects.filter_user_profiles(user_obj).values_list(
4784
"id", flat=True

0 commit comments

Comments
 (0)