From b6907f39e97529211f53ee175ed657f999261ecf Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 13 Dec 2020 13:03:17 -0800 Subject: [PATCH] Creates Privacy field that handles setting to/cc --- bookwyrm/models/base_model.py | 25 +++------------ bookwyrm/models/fields.py | 60 +++++++++++++++++++++++++++++++++++ bookwyrm/models/import_job.py | 2 +- bookwyrm/models/shelf.py | 4 +-- bookwyrm/models/status.py | 28 ++-------------- 5 files changed, 70 insertions(+), 49 deletions(-) diff --git a/bookwyrm/models/base_model.py b/bookwyrm/models/base_model.py index e899b3c5..08cc6052 100644 --- a/bookwyrm/models/base_model.py +++ b/bookwyrm/models/base_model.py @@ -17,13 +17,6 @@ from bookwyrm.settings import DOMAIN, PAGE_LENGTH from .fields import ImageField, ManyToManyField, RemoteIdField -PrivacyLevels = models.TextChoices('Privacy', [ - 'public', - 'unlisted', - 'followers', - 'direct' -]) - class BookWyrmModel(models.Model): ''' shared fields ''' created_date = models.DateTimeField(auto_now_add=True) @@ -84,6 +77,9 @@ class ActivitypubMixin: else: self.simple_fields.append(field) + self.activity_fields = self.image_fields + \ + self.many_to_many_fields + self.simple_fields + self.deserialize_reverse_fields = self.deserialize_reverse_fields \ if hasattr(self, 'deserialize_reverse_fields') else [] self.serialize_reverse_fields = self.serialize_reverse_fields \ @@ -139,19 +135,8 @@ class ActivitypubMixin: def to_activity(self): ''' convert from a model to an activity ''' activity = {} - for field in self._meta.get_fields(): - if not hasattr(field, 'field_to_activity'): - continue - value = field.field_to_activity(getattr(self, field.name)) - if value is None: - continue - - key = field.get_activitypub_field() - if key in activity and isinstance(activity[key], list): - # handles tags on status, which accumulate across fields - activity[key] += value - else: - activity[key] = value + for field in self.activity_fields: + field.set_activity_from_field(activity, self) if hasattr(self, 'serialize_reverse_fields'): # for example, editions of a work diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index 21bdfadb..05453397 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -49,6 +49,20 @@ class ActivitypubFieldMixin: setattr(instance, self.name, formatted) + def set_activity_from_field(self, activity, instance): + ''' update the json object ''' + value = getattr(instance, self.name) + formatted = self.field_to_activity(value) + if formatted is None: + return + + key = self.get_activitypub_field() + if isinstance(activity.get(key), list): + activity[key] += formatted + else: + activity[key] = value + + def field_to_activity(self, value): ''' formatter to convert a model value into activitypub ''' if hasattr(self, 'activitypub_wrapper'): @@ -134,6 +148,52 @@ class UsernameField(ActivitypubFieldMixin, models.CharField): return value.split('@')[0] +PrivacyLevels = models.TextChoices('Privacy', [ + 'public', + 'unlisted', + 'followers', + 'direct' +]) + +class PrivacyField(ActivitypubFieldMixin, models.CharField): + ''' this maps to two differente activitypub fields ''' + public = 'https://www.w3.org/ns/activitystreams#Public' + def __init__(self, *args, **kwargs): + super().__init__( + *args, max_length=255, + choices=PrivacyLevels.choices, default='public') + + def set_field_from_activity(self, instance, data): + to = data.to + cc = data.cc + if to == [self.public]: + setattr(instance, self.name, 'public') + elif cc == []: + setattr(instance, self.name, 'direct') + elif self.public in cc: + setattr(instance, self.name, 'unlisted') + else: + setattr(instance, self.name, 'followers') + + def set_activity_from_field(self, activity, instance): + mentions = [u.remote_id for u in instance.mention_users.all()] + # this is a link to the followers list + followers = instance.user.__class__._meta.get_field('followers')\ + .field_to_activity(instance.user.followers) + if self.privacy == 'public': + activity['to'] = [self.public] + activity['cc'] = [followers] + mentions + elif self.privacy == 'unlisted': + activity['to'] = [followers] + activity['cc'] = [self.public] + mentions + elif self.privacy == 'followers': + activity['to'] = [followers] + activity['cc'] = mentions + if self.privacy == 'direct': + activity['to'] = mentions + activity['cc'] = [] + + class ForeignKey(ActivitypubRelatedFieldMixin, models.ForeignKey): ''' activitypub-aware foreign key field ''' def field_to_activity(self, value): diff --git a/bookwyrm/models/import_job.py b/bookwyrm/models/import_job.py index bf5d5caf..835094cd 100644 --- a/bookwyrm/models/import_job.py +++ b/bookwyrm/models/import_job.py @@ -8,7 +8,7 @@ from django.utils import timezone from bookwyrm import books_manager from bookwyrm.models import ReadThrough, User, Book -from .base_model import PrivacyLevels +from .fields import PrivacyLevels # Mapping goodreads -> bookwyrm shelf titles. diff --git a/bookwyrm/models/shelf.py b/bookwyrm/models/shelf.py index fc63d198..68f3614f 100644 --- a/bookwyrm/models/shelf.py +++ b/bookwyrm/models/shelf.py @@ -4,7 +4,7 @@ from django.db import models from bookwyrm import activitypub from .base_model import BookWyrmModel -from .base_model import OrderedCollectionMixin, PrivacyLevels +from .base_model import OrderedCollectionMixin from . import fields @@ -18,7 +18,7 @@ class Shelf(OrderedCollectionMixin, BookWyrmModel): privacy = fields.CharField( max_length=255, default='public', - choices=PrivacyLevels.choices + choices=fields.PrivacyLevels.choices ) books = models.ManyToManyField( 'Edition', diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py index 43fc4511..0bf897dc 100644 --- a/bookwyrm/models/status.py +++ b/bookwyrm/models/status.py @@ -6,7 +6,7 @@ from model_utils.managers import InheritanceManager from bookwyrm import activitypub from .base_model import ActivitypubMixin, OrderedCollectionPageMixin -from .base_model import BookWyrmModel, PrivacyLevels +from .base_model import BookWyrmModel from . import fields from .fields import image_serializer @@ -18,11 +18,7 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel): mention_users = fields.TagField('User', related_name='mention_user') mention_books = fields.TagField('Edition', related_name='mention_book') local = models.BooleanField(default=True) - privacy = models.CharField( - max_length=255, - default='public', - choices=PrivacyLevels.choices - ) + privacy = fields.PrivacyField(max_length=255) sensitive = fields.BooleanField(default=False) # created date is different than publish date because of federated posts published_date = fields.DateTimeField( @@ -48,7 +44,6 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel): serialize_reverse_fields = [('attachments', 'attachment')] deserialize_reverse_fields = [('attachments', 'attachment')] - #----- replies collection activitypub ----# @classmethod def replies(cls, status): ''' load all replies to a status. idk if there's a better way @@ -82,25 +77,6 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel): activity = ActivitypubMixin.to_activity(self) activity['replies'] = self.to_replies() - # privacy controls - public = 'https://www.w3.org/ns/activitystreams#Public' - mentions = [u.remote_id for u in self.mention_users.all()] - # this is a link to the followers list: - followers = self.user.__class__._meta.get_field('followers')\ - .field_to_activity(self.user.followers) - if self.privacy == 'public': - activity['to'] = [public] - activity['cc'] = [followers] + mentions - elif self.privacy == 'unlisted': - activity['to'] = [followers] - activity['cc'] = [public] + mentions - elif self.privacy == 'followers': - activity['to'] = [followers] - activity['cc'] = mentions - if self.privacy == 'direct': - activity['to'] = mentions - activity['cc'] = [] - # "pure" serialization for non-bookwyrm instances if pure: activity['content'] = self.pure_content