diff --git a/bookwyrm/activitypub/__init__.py b/bookwyrm/activitypub/__init__.py index 9a32bfc9..bd1a0266 100644 --- a/bookwyrm/activitypub/__init__.py +++ b/bookwyrm/activitypub/__init__.py @@ -4,7 +4,8 @@ import sys from .base_activity import ActivityEncoder, Image, PublicKey, Signature from .base_activity import Link, Mention -from .base_activity import ActivitySerializerError, tag_formatter +from .base_activity import ActivitySerializerError +from .base_activity import tag_formatter, image_formatter from .note import Note, GeneratedNote, Article, Comment, Review, Quotation from .note import Tombstone from .interaction import Boost, Like diff --git a/bookwyrm/activitypub/base_activity.py b/bookwyrm/activitypub/base_activity.py index 18e5ccbe..65752105 100644 --- a/bookwyrm/activitypub/base_activity.py +++ b/bookwyrm/activitypub/base_activity.py @@ -1,11 +1,15 @@ ''' basics for an activitypub serializer ''' from dataclasses import dataclass, fields, MISSING from json import JSONEncoder +from uuid import uuid4 -from bookwyrm import books_manager, models - +from django.core.files.base import ContentFile from django.db.models.fields.related_descriptors \ import ForwardManyToOneDescriptor, ManyToManyDescriptor +from django.db.models.fields.files import ImageFileDescriptor +import requests + +from bookwyrm import books_manager, models class ActivitySerializerError(ValueError): @@ -91,6 +95,7 @@ class ActivityObject: model_fields = [m.name for m in model._meta.get_fields()] mapped_fields = {} many_to_many_fields = {} + image_fields = {} for mapping in model.activity_mappings: if mapping.model_key not in model_fields: @@ -110,12 +115,13 @@ class ActivityObject: formatted_value = mapping.model_formatter(value) if isinstance(model_field, ManyToManyDescriptor): many_to_many_fields[mapping.model_key] = formatted_value + elif isinstance(model_field, ImageFileDescriptor): + image_fields[mapping.model_key] = formatted_value else: mapped_fields[mapping.model_key] = formatted_value - - # updating an existing model isntance if instance: + # updating an existing model isntance for k, v in mapped_fields.items(): setattr(instance, k, v) instance.save() @@ -123,9 +129,14 @@ class ActivityObject: # creating a new model instance instance = model.objects.create(**mapped_fields) + # add many-to-many fields for (model_key, values) in many_to_many_fields.items(): getattr(instance, model_key).set(values) instance.save() + + # add images + for (model_key, value) in image_fields.items(): + getattr(instance, model_key).save(*value, save=True) return instance @@ -174,3 +185,21 @@ def tag_formatter(tags): continue items.append(item) return items + + +def image_formatter(image_json): + ''' helper function to load images and format them for a model ''' + url = image_json.get('url') + if not url: + return None + + try: + response = requests.get(url) + except ConnectionError: + return None + if not response.ok: + return None + + image_name = str(uuid4()) + '.' + url.split('.')[-1] + image_content = ContentFile(response.content) + return [image_name, image_content] diff --git a/bookwyrm/models/user.py b/bookwyrm/models/user.py index 2bea373b..b38a4b19 100644 --- a/bookwyrm/models/user.py +++ b/bookwyrm/models/user.py @@ -114,7 +114,8 @@ class User(OrderedCollectionPageMixin, AbstractUser): ), ActivityMapping( 'icon', 'avatar', - lambda x: image_formatter(x, '/static/images/default_avi.jpg') + lambda x: image_formatter(x, '/static/images/default_avi.jpg'), + activitypub.image_formatter ), ActivityMapping( 'manuallyApprovesFollowers', diff --git a/bookwyrm/remote_user.py b/bookwyrm/remote_user.py index 9178451b..9aa6079e 100644 --- a/bookwyrm/remote_user.py +++ b/bookwyrm/remote_user.py @@ -25,11 +25,6 @@ def get_or_create_remote_user(actor): user = create_remote_user(data) user.federated_server = get_or_create_remote_server(actor_parts.netloc) user.save() - - avatar = get_avatar(data) - if avatar: - user.avatar.save(*avatar) - if user.bookwyrm_user: get_remote_reviews.delay(user.id) return user @@ -69,21 +64,6 @@ def refresh_remote_user(user): activity.to_model(models.User, instance=user) -def get_avatar(data): - ''' find the icon attachment and load the image from the remote sever ''' - icon_blob = data.get('icon') - if not icon_blob or not icon_blob.get('url'): - return None - - response = requests.get(icon_blob['url']) - if not response.ok: - return None - - image_name = str(uuid4()) + '.' + icon_blob['url'].split('.')[-1] - image_content = ContentFile(response.content) - return [image_name, image_content] - - @app.task def get_remote_reviews(user_id): ''' ingest reviews by a new remote bookwyrm user '''