forked from mirrors/bookwyrm
f8e0de1ea9
Godammit Hugh remember to do this before pushing new code.
157 lines
4.9 KiB
Python
157 lines
4.9 KiB
Python
""" do book related things with other users """
|
|
from django.apps import apps
|
|
from django.db import models, IntegrityError, transaction
|
|
from django.db.models import Q
|
|
from bookwyrm.settings import DOMAIN
|
|
from .base_model import BookWyrmModel
|
|
from . import fields
|
|
from .relationship import UserBlocks
|
|
|
|
# from .user import User
|
|
|
|
|
|
class Group(BookWyrmModel):
|
|
"""A group of users"""
|
|
|
|
name = fields.CharField(max_length=100)
|
|
user = fields.ForeignKey("User", on_delete=models.PROTECT)
|
|
description = fields.TextField(blank=True, null=True)
|
|
privacy = fields.PrivacyField()
|
|
|
|
def get_remote_id(self):
|
|
"""don't want the user to be in there in this case"""
|
|
return f"https://{DOMAIN}/group/{self.id}"
|
|
|
|
|
|
class GroupMember(models.Model):
|
|
"""Users who are members of a group"""
|
|
|
|
created_date = models.DateTimeField(auto_now_add=True)
|
|
updated_date = models.DateTimeField(auto_now=True)
|
|
group = models.ForeignKey(
|
|
"Group", on_delete=models.CASCADE, related_name="memberships"
|
|
)
|
|
user = models.ForeignKey(
|
|
"User", on_delete=models.CASCADE, related_name="memberships"
|
|
)
|
|
|
|
class Meta:
|
|
"""Users can only have one membership per group"""
|
|
|
|
constraints = [
|
|
models.UniqueConstraint(fields=["group", "user"], name="unique_membership")
|
|
]
|
|
|
|
def save(self, *args, **kwargs):
|
|
"""don't let a user invite someone who blocked them"""
|
|
# blocking in either direction is a no-go
|
|
if UserBlocks.objects.filter(
|
|
Q(
|
|
user_subject=self.group.user,
|
|
user_object=self.user,
|
|
)
|
|
| Q(
|
|
user_subject=self.user,
|
|
user_object=self.group.user,
|
|
)
|
|
).exists():
|
|
raise IntegrityError()
|
|
# accepts and requests are handled by the GroupInvitation model
|
|
super().save(*args, **kwargs)
|
|
|
|
@classmethod
|
|
def from_request(cls, join_request):
|
|
"""converts a join request into a member relationship"""
|
|
|
|
# remove the invite
|
|
join_request.delete()
|
|
|
|
# make a group member
|
|
return cls.objects.create(
|
|
user=join_request.user,
|
|
group=join_request.group,
|
|
)
|
|
|
|
|
|
class GroupMemberInvitation(models.Model):
|
|
"""adding a user to a group requires manual confirmation"""
|
|
|
|
created_date = models.DateTimeField(auto_now_add=True)
|
|
group = models.ForeignKey(
|
|
"Group", on_delete=models.CASCADE, related_name="user_invitations"
|
|
)
|
|
user = models.ForeignKey(
|
|
"User", on_delete=models.CASCADE, related_name="group_invitations"
|
|
)
|
|
|
|
class Meta:
|
|
"""Users can only have one outstanding invitation per group"""
|
|
|
|
constraints = [
|
|
models.UniqueConstraint(fields=["group", "user"], name="unique_invitation")
|
|
]
|
|
|
|
def save(self, *args, **kwargs):
|
|
"""make sure the membership doesn't already exist"""
|
|
# if there's an invitation for a membership that already exists, accept it
|
|
# without changing the local database state
|
|
if GroupMember.objects.filter(user=self.user, group=self.group).exists():
|
|
self.accept()
|
|
return
|
|
|
|
# blocking in either direction is a no-go
|
|
if UserBlocks.objects.filter(
|
|
Q(
|
|
user_subject=self.group.user,
|
|
user_object=self.user,
|
|
)
|
|
| Q(
|
|
user_subject=self.user,
|
|
user_object=self.group.user,
|
|
)
|
|
).exists():
|
|
raise IntegrityError()
|
|
|
|
# make an invitation
|
|
super().save(*args, **kwargs)
|
|
|
|
# now send the invite
|
|
model = apps.get_model("bookwyrm.Notification", require_ready=True)
|
|
notification_type = "INVITE"
|
|
model.objects.create(
|
|
user=self.user,
|
|
related_user=self.group.user,
|
|
related_group=self.group,
|
|
notification_type=notification_type,
|
|
)
|
|
|
|
def accept(self):
|
|
"""turn this request into the real deal"""
|
|
|
|
with transaction.atomic():
|
|
GroupMember.from_request(self)
|
|
|
|
model = apps.get_model("bookwyrm.Notification", require_ready=True)
|
|
# tell the group owner
|
|
model.objects.create(
|
|
user=self.group.user,
|
|
related_user=self.user,
|
|
related_group=self.group,
|
|
notification_type="ACCEPT",
|
|
)
|
|
|
|
# let the other members know about it
|
|
for membership in self.group.memberships.all():
|
|
member = membership.user
|
|
if member not in (self.user, self.group.user):
|
|
model.objects.create(
|
|
user=member,
|
|
related_user=self.user,
|
|
related_group=self.group,
|
|
notification_type="JOIN",
|
|
)
|
|
|
|
def reject(self):
|
|
"""generate a Reject for this membership request"""
|
|
|
|
self.delete()
|