Federating lists and shelves

This commit is contained in:
Mouse Reeve 2021-02-02 09:37:46 -08:00
parent c7914d1394
commit e53b4e57fa
8 changed files with 74 additions and 29 deletions

View file

@ -1,5 +1,5 @@
''' defines activitypub collections (lists) ''' ''' defines activitypub collections (lists) '''
from dataclasses import dataclass from dataclasses import dataclass, field
from typing import List from typing import List
from .base_activity import ActivityObject from .base_activity import ActivityObject
@ -10,9 +10,12 @@ class OrderedCollection(ActivityObject):
''' structure of an ordered collection activity ''' ''' structure of an ordered collection activity '''
totalItems: int totalItems: int
first: str first: str
last: str = '' last: str = None
name: str = '' name: str = None
owner: str = '' summary: str = None
owner: str = None
to: List[str] = field(default_factory=lambda: [])
cc: List[str] = field(default_factory=lambda: [])
type: str = 'OrderedCollection' type: str = 'OrderedCollection'

View file

@ -18,7 +18,7 @@ class Create(Verb):
''' Create activity ''' ''' Create activity '''
to: List to: List
cc: List cc: List
signature: Signature signature: Signature = None
type: str = 'Create' type: str = 'Create'

View file

@ -161,10 +161,12 @@ class ActivitypubMixin:
''' returns the object wrapped in a Create activity ''' ''' returns the object wrapped in a Create activity '''
activity_object = self.to_activity(**kwargs) activity_object = self.to_activity(**kwargs)
signature = None
create_id = self.remote_id + '/activity'
if 'content' in activity_object:
signer = pkcs1_15.new(RSA.import_key(user.key_pair.private_key)) signer = pkcs1_15.new(RSA.import_key(user.key_pair.private_key))
content = activity_object['content'] content = activity_object['content']
signed_message = signer.sign(SHA256.new(content.encode('utf8'))) signed_message = signer.sign(SHA256.new(content.encode('utf8')))
create_id = self.remote_id + '/activity'
signature = activitypub.Signature( signature = activitypub.Signature(
creator='%s#main-key' % user.remote_id, creator='%s#main-key' % user.remote_id,

View file

@ -19,12 +19,9 @@ class List(OrderedCollectionMixin, BookWyrmModel):
name = fields.CharField(max_length=100) name = fields.CharField(max_length=100)
user = fields.ForeignKey( user = fields.ForeignKey(
'User', on_delete=models.PROTECT, activitypub_field='owner') 'User', on_delete=models.PROTECT, activitypub_field='owner')
description = fields.TextField(blank=True, null=True) description = fields.TextField(
privacy = fields.CharField( blank=True, null=True, activitypub_field='summary')
max_length=255, privacy = fields.PrivacyField()
default='public',
choices=fields.PrivacyLevels.choices
)
curation = fields.CharField( curation = fields.CharField(
max_length=255, max_length=255,
default='closed', default='closed',

View file

@ -15,11 +15,7 @@ class Shelf(OrderedCollectionMixin, BookWyrmModel):
user = fields.ForeignKey( user = fields.ForeignKey(
'User', on_delete=models.PROTECT, activitypub_field='owner') 'User', on_delete=models.PROTECT, activitypub_field='owner')
editable = models.BooleanField(default=True) editable = models.BooleanField(default=True)
privacy = fields.CharField( privacy = fields.PrivacyField()
max_length=255,
default='public',
choices=fields.PrivacyLevels.choices
)
books = models.ManyToManyField( books = models.ManyToManyField(
'Edition', 'Edition',
symmetrical=False, symmetrical=False,

View file

@ -71,10 +71,15 @@ class Goal(View):
broadcast( broadcast(
request.user, request.user,
status.to_create_activity(request.user), status.to_create_activity(request.user),
privacy=status.privacy,
software='bookwyrm') software='bookwyrm')
# re-format the activity for non-bookwyrm servers # re-format the activity for non-bookwyrm servers
remote_activity = status.to_create_activity(request.user, pure=True) remote_activity = status.to_create_activity(request.user, pure=True)
broadcast(request.user, remote_activity, software='other') broadcast(
request.user,
remote_activity,
privacy=status.privacy,
software='other')
return redirect(request.headers.get('Referer', '/')) return redirect(request.headers.get('Referer', '/'))

View file

@ -11,6 +11,7 @@ from django.views.decorators.http import require_POST
from bookwyrm import forms, models from bookwyrm import forms, models
from bookwyrm.activitypub import ActivitypubResponse from bookwyrm.activitypub import ActivitypubResponse
from bookwyrm.broadcast import broadcast
from bookwyrm.connectors import connector_manager from bookwyrm.connectors import connector_manager
from .helpers import is_api_request, object_visible_to_user, privacy_filter from .helpers import is_api_request, object_visible_to_user, privacy_filter
from .helpers import get_user_from_username from .helpers import get_user_from_username
@ -48,6 +49,14 @@ class Lists(View):
if not form.is_valid(): if not form.is_valid():
return redirect('lists') return redirect('lists')
book_list = form.save() book_list = form.save()
# let the world know
broadcast(
request.user,
book_list.to_create_activity(request.user),
privacy=book_list.privacy,
software='bookwyrm'
)
return redirect(book_list.local_path) return redirect(book_list.local_path)
class UserLists(View): class UserLists(View):
@ -128,6 +137,13 @@ class List(View):
if not form.is_valid(): if not form.is_valid():
return redirect('list', book_list.id) return redirect('list', book_list.id)
book_list = form.save() book_list = form.save()
# let the world know
broadcast(
request.user,
book_list.to_update_activity(request.user),
privacy=book_list.privacy,
software='bookwyrm'
)
return redirect(book_list.local_path) return redirect(book_list.local_path)
@ -161,6 +177,13 @@ class Curate(View):
if approved: if approved:
suggestion.approved = True suggestion.approved = True
suggestion.save() suggestion.save()
# let the world know
broadcast(
request.user,
suggestion.to_add_activity(request.user),
privacy=book_list.privacy,
software='bookwyrm'
)
else: else:
suggestion.delete() suggestion.delete()
return redirect('list-curate', book_list.id) return redirect('list-curate', book_list.id)
@ -177,11 +200,18 @@ def add_book(request, list_id):
# do you have permission to add to the list? # do you have permission to add to the list?
if request.user == book_list.user or book_list.curation == 'open': if request.user == book_list.user or book_list.curation == 'open':
# go ahead and add it # go ahead and add it
models.ListItem.objects.create( item = models.ListItem.objects.create(
book=book, book=book,
book_list=book_list, book_list=book_list,
added_by=request.user, added_by=request.user,
) )
# let the world know
broadcast(
request.user,
item.to_add_activity(request.user),
privacy=book_list.privacy,
software='bookwyrm'
)
elif book_list.curation == 'curated': elif book_list.curation == 'curated':
# make a pending entry # make a pending entry
models.ListItem.objects.create( models.ListItem.objects.create(
@ -206,5 +236,13 @@ def remove_book(request, list_id):
if not book_list.user == request.user and not item.added_by == request.user: if not book_list.user == request.user and not item.added_by == request.user:
return HttpResponseNotFound() return HttpResponseNotFound()
activity = item.to_remove_activity(request.user)
item.delete() item.delete()
# let the world know
broadcast(
request.user,
activity,
privacy=book_list.privacy,
software='bookwyrm'
)
return redirect('list', list_id) return redirect('list', list_id)

View file

@ -138,7 +138,12 @@ def shelve(request):
pass pass
shelfbook = models.ShelfBook.objects.create( shelfbook = models.ShelfBook.objects.create(
book=book, shelf=desired_shelf, added_by=request.user) book=book, shelf=desired_shelf, added_by=request.user)
broadcast(request.user, shelfbook.to_add_activity(request.user)) broadcast(
request.user,
shelfbook.to_add_activity(request.user),
privacy=shelfbook.shelf.privacy,
software='bookwyrm'
)
# post about "want to read" shelves # post about "want to read" shelves
if desired_shelf.identifier == 'to-read': if desired_shelf.identifier == 'to-read':
@ -146,7 +151,6 @@ def shelve(request):
request.user, request.user,
desired_shelf, desired_shelf,
book, book,
privacy=desired_shelf.privacy
) )
return redirect('/') return redirect('/')
@ -169,4 +173,4 @@ def handle_unshelve(user, book, shelf):
activity = row.to_remove_activity(user) activity = row.to_remove_activity(user)
row.delete() row.delete()
broadcast(user, activity) broadcast(user, activity, privacy=shelf.privacy, software='bookwyrm')