2020-09-17 20:09:11 +00:00
|
|
|
''' defines relationships between users '''
|
|
|
|
from django.db import models
|
2021-01-25 01:07:19 +00:00
|
|
|
from django.db.models import Q
|
|
|
|
from django.dispatch import receiver
|
2020-09-17 20:09:11 +00:00
|
|
|
|
2020-09-21 15:10:37 +00:00
|
|
|
from bookwyrm import activitypub
|
2020-11-30 22:54:45 +00:00
|
|
|
from .base_model import ActivitypubMixin, BookWyrmModel
|
|
|
|
from . import fields
|
2020-09-17 20:09:11 +00:00
|
|
|
|
|
|
|
|
2020-10-16 17:37:33 +00:00
|
|
|
class UserRelationship(ActivitypubMixin, BookWyrmModel):
|
2020-09-17 20:09:11 +00:00
|
|
|
''' many-to-many through table for followers '''
|
2020-11-30 22:54:45 +00:00
|
|
|
user_subject = fields.ForeignKey(
|
2020-09-17 20:09:11 +00:00
|
|
|
'User',
|
|
|
|
on_delete=models.PROTECT,
|
2020-11-30 22:54:45 +00:00
|
|
|
related_name='%(class)s_user_subject',
|
|
|
|
activitypub_field='actor',
|
2020-09-17 20:09:11 +00:00
|
|
|
)
|
2020-11-30 22:54:45 +00:00
|
|
|
user_object = fields.ForeignKey(
|
2020-09-17 20:09:11 +00:00
|
|
|
'User',
|
|
|
|
on_delete=models.PROTECT,
|
2020-11-30 22:54:45 +00:00
|
|
|
related_name='%(class)s_user_object',
|
|
|
|
activitypub_field='object',
|
2020-09-17 20:09:11 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
''' relationships should be unique '''
|
|
|
|
abstract = True
|
|
|
|
constraints = [
|
|
|
|
models.UniqueConstraint(
|
|
|
|
fields=['user_subject', 'user_object'],
|
|
|
|
name='%(class)s_unique'
|
|
|
|
),
|
|
|
|
models.CheckConstraint(
|
|
|
|
check=~models.Q(user_subject=models.F('user_object')),
|
|
|
|
name='%(class)s_no_self'
|
|
|
|
)
|
|
|
|
]
|
|
|
|
|
2020-10-16 17:37:33 +00:00
|
|
|
activity_serializer = activitypub.Follow
|
|
|
|
|
2020-12-13 02:06:48 +00:00
|
|
|
def get_remote_id(self, status=None):# pylint: disable=arguments-differ
|
2020-09-17 20:09:11 +00:00
|
|
|
''' use shelf identifier in remote_id '''
|
2020-10-16 17:37:33 +00:00
|
|
|
status = status or 'follows'
|
2020-09-17 20:09:11 +00:00
|
|
|
base_path = self.user_subject.remote_id
|
2020-10-16 17:37:33 +00:00
|
|
|
return '%s#%s/%d' % (base_path, status, self.id)
|
|
|
|
|
2020-09-17 20:09:11 +00:00
|
|
|
|
2020-09-29 20:19:57 +00:00
|
|
|
def to_accept_activity(self):
|
|
|
|
''' generate an Accept for this follow request '''
|
|
|
|
return activitypub.Accept(
|
2020-10-16 17:37:33 +00:00
|
|
|
id=self.get_remote_id(status='accepts'),
|
|
|
|
actor=self.user_object.remote_id,
|
|
|
|
object=self.to_activity()
|
2020-09-29 20:19:57 +00:00
|
|
|
).serialize()
|
|
|
|
|
2020-10-16 17:37:33 +00:00
|
|
|
|
2020-09-29 20:19:57 +00:00
|
|
|
def to_reject_activity(self):
|
2020-12-27 22:29:43 +00:00
|
|
|
''' generate a Reject for this follow request '''
|
2020-09-29 20:19:57 +00:00
|
|
|
return activitypub.Reject(
|
2020-10-16 17:37:33 +00:00
|
|
|
id=self.get_remote_id(status='rejects'),
|
|
|
|
actor=self.user_object.remote_id,
|
|
|
|
object=self.to_activity()
|
2020-09-29 20:19:57 +00:00
|
|
|
).serialize()
|
|
|
|
|
2020-09-17 20:09:11 +00:00
|
|
|
|
|
|
|
class UserFollows(UserRelationship):
|
|
|
|
''' Following a user '''
|
|
|
|
status = 'follows'
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_request(cls, follow_request):
|
|
|
|
''' converts a follow request into a follow relationship '''
|
|
|
|
return cls(
|
|
|
|
user_subject=follow_request.user_subject,
|
|
|
|
user_object=follow_request.user_object,
|
2020-10-16 17:37:33 +00:00
|
|
|
remote_id=follow_request.remote_id,
|
2020-09-17 20:09:11 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class UserFollowRequest(UserRelationship):
|
|
|
|
''' following a user requires manual or automatic confirmation '''
|
|
|
|
status = 'follow_request'
|
|
|
|
|
2020-11-01 20:49:07 +00:00
|
|
|
def save(self, *args, **kwargs):
|
|
|
|
''' make sure the follow relationship doesn't already exist '''
|
|
|
|
try:
|
|
|
|
UserFollows.objects.get(
|
|
|
|
user_subject=self.user_subject,
|
|
|
|
user_object=self.user_object
|
|
|
|
)
|
|
|
|
return None
|
|
|
|
except UserFollows.DoesNotExist:
|
|
|
|
return super().save(*args, **kwargs)
|
|
|
|
|
2020-09-17 20:09:11 +00:00
|
|
|
|
|
|
|
class UserBlocks(UserRelationship):
|
|
|
|
''' prevent another user from following you and seeing your posts '''
|
|
|
|
status = 'blocks'
|
2021-01-23 19:03:10 +00:00
|
|
|
activity_serializer = activitypub.Block
|
2021-01-25 01:07:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
@receiver(models.signals.post_save, sender=UserBlocks)
|
|
|
|
#pylint: disable=unused-argument
|
|
|
|
def execute_after_save(sender, instance, created, *args, **kwargs):
|
|
|
|
''' remove follow or follow request rels after a block is created '''
|
|
|
|
UserFollows.objects.filter(
|
|
|
|
Q(user_subject=instance.user_subject,
|
|
|
|
user_object=instance.user_object) | \
|
|
|
|
Q(user_subject=instance.user_object,
|
|
|
|
user_object=instance.user_subject)
|
|
|
|
).delete()
|
|
|
|
UserFollowRequest.objects.filter(
|
|
|
|
Q(user_subject=instance.user_subject,
|
|
|
|
user_object=instance.user_object) | \
|
|
|
|
Q(user_subject=instance.user_object,
|
|
|
|
user_object=instance.user_subject)
|
|
|
|
).delete()
|