disambiguate groups and prep for group invitations

- rename Group to BookwyrmGroup
- create group memberships and invitations
- adjust all model name references accordingly
This commit is contained in:
Hugh Rundle
2021-10-02 10:10:37 +10:00
parent 66494e7788
commit 2f42161dda
10 changed files with 255 additions and 69 deletions

View File

@ -1,14 +1,14 @@
""" do book related things with other users """
from django.apps import apps
from django.db import models
from django.utils import timezone
from django.db import models, IntegrityError, models, 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):
class BookwyrmGroup(BookWyrmModel):
"""A group of users"""
name = fields.CharField(max_length=100)
@ -16,27 +16,138 @@ class Group(BookWyrmModel):
"User", on_delete=models.PROTECT)
description = fields.TextField(blank=True, null=True)
privacy = fields.PrivacyField()
members = models.ManyToManyField(
"User",
symmetrical=False,
through="GroupMember",
through_fields=("group", "user"),
related_name="bookwyrm_groups"
)
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):
class BookwyrmGroupMember(models.Model):
"""Users who are members of a group"""
group = models.ForeignKey("Group", on_delete=models.CASCADE)
user = models.ForeignKey("User", on_delete=models.CASCADE)
created_date = models.DateTimeField(auto_now_add=True)
updated_date = models.DateTimeField(auto_now=True)
group = models.ForeignKey(
"BookwyrmGroup",
on_delete=models.CASCADE,
related_name="memberships"
)
user = models.ForeignKey(
"User",
on_delete=models.CASCADE,
related_name="memberships"
)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["group", "user"], name="unique_member"
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 BookwyrmGroupInvitation 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(
"BookwyrmGroup",
on_delete=models.CASCADE,
related_name="user_invitations"
)
user = models.ForeignKey(
"User",
on_delete=models.CASCADE,
related_name="group_invitations"
)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["group", "user"], name="unique_invitation"
)
]
def save(self, *args, **kwargs): # pylint: disable=arguments-differ
"""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 BookwyrmGroupMember.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():
BookwyrmGroupMember.from_request(self)
self.delete()
# let the other members know about it
model = apps.get_model("bookwyrm.Notification", require_ready=True)
for member in self.group.members.all:
if member != self.user:
model.objects.create(
user=member,
related_user=self.user,
related_group=self.group,
notification_type="ACCEPT",
)
def reject(self):
"""generate a Reject for this membership request"""
self.delete()
# TODO: send notification