circular import issues and added_by migration

This commit is contained in:
Mouse Reeve 2021-02-04 14:27:26 -08:00
parent 5a3a6151a6
commit 7381536ad6
19 changed files with 273 additions and 265 deletions

View file

@ -81,7 +81,7 @@ def handle_imported_book(user, item, include_reviews, privacy):
return return
existing_shelf = models.ShelfBook.objects.filter( existing_shelf = models.ShelfBook.objects.filter(
book=item.book, added_by=user).exists() book=item.book, user=user).exists()
# shelve the book if it hasn't been shelved already # shelve the book if it hasn't been shelved already
if item.shelf and not existing_shelf: if item.shelf and not existing_shelf:
@ -90,7 +90,7 @@ def handle_imported_book(user, item, include_reviews, privacy):
user=user user=user
) )
models.ShelfBook.objects.create( models.ShelfBook.objects.create(
book=item.book, shelf=desired_shelf, added_by=user) book=item.book, shelf=desired_shelf, user=user)
for read in item.reads: for read in item.reads:
# check for an existing readthrough with the same dates # check for an existing readthrough with the same dates

View file

@ -0,0 +1,23 @@
# Generated by Django 3.0.7 on 2021-02-04 22:23
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('bookwyrm', '0042_auto_20210201_2108'),
]
operations = [
migrations.RenameField(
model_name='listitem',
old_name='added_by',
new_name='user',
),
migrations.RenameField(
model_name='shelfbook',
old_name='added_by',
new_name='user',
),
]

View file

@ -2,20 +2,25 @@
from functools import reduce from functools import reduce
import json import json
import operator import operator
from base64 import b64encode
from uuid import uuid4
import requests import requests
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
from django.apps import apps from django.apps import apps
from django.core.paginator import Paginator
from django.db import models from django.db import models
from django.db.models import Q from django.db.models import Q
from django.dispatch import receiver from django.dispatch import receiver
from django.utils.http import http_date from django.utils.http import http_date
from bookwyrm import activitypub from bookwyrm import activitypub
from bookwyrm.settings import USER_AGENT from bookwyrm.settings import USER_AGENT, PAGE_LENGTH
from bookwyrm.signatures import make_signature, make_digest from bookwyrm.signatures import make_signature, make_digest
from bookwyrm.tasks import app from bookwyrm.tasks import app
from .fields import ImageField, ManyToManyField from bookwyrm.models.fields import ImageField, ManyToManyField
class ActivitypubMixin: class ActivitypubMixin:
@ -247,3 +252,218 @@ def execute_after_save(sender, instance, created, *args, **kwargs):
if activity and user and user.local: if activity and user and user.local:
instance.broadcast(activity, user) instance.broadcast(activity, user)
class ObjectMixin(ActivitypubMixin):
''' add this mixin for object models that are AP serializable '''
def save(self, *args, **kwargs):
''' broadcast updated '''
# first off, we want to save normally no matter what
super().save(*args, **kwargs)
# we only want to handle updates, not newly created objects
if not self.id:
return
# this will work for lists, shelves
user = self.user if hasattr(self, 'user') else None
if not user:
# users don't have associated users, they ARE users
user_model = apps.get_model('bookwyrm.User', require_ready=True)
if isinstance(self, user_model):
user = self
# book data tracks last editor
elif hasattr(self, 'last_edited_by'):
user = self.last_edited_by
# again, if we don't know the user or they're remote, don't bother
if not user or not user.local:
return
# is this a deletion?
if self.deleted:
activity = self.to_delete_activity(user)
else:
activity = self.to_update_activity(user)
self.broadcast(activity, user)
def to_create_activity(self, user, **kwargs):
''' returns the object wrapped in a Create activity '''
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))
content = activity_object['content']
signed_message = signer.sign(SHA256.new(content.encode('utf8')))
signature = activitypub.Signature(
creator='%s#main-key' % user.remote_id,
created=activity_object['published'],
signatureValue=b64encode(signed_message).decode('utf8')
)
return activitypub.Create(
id=create_id,
actor=user.remote_id,
to=activity_object['to'],
cc=activity_object['cc'],
object=activity_object,
signature=signature,
).serialize()
def to_delete_activity(self, user):
''' notice of deletion '''
return activitypub.Delete(
id=self.remote_id + '/activity',
actor=user.remote_id,
to=['%s/followers' % user.remote_id],
cc=['https://www.w3.org/ns/activitystreams#Public'],
object=self.to_activity(),
).serialize()
def to_update_activity(self, user):
''' wrapper for Updates to an activity '''
activity_id = '%s#update/%s' % (self.remote_id, uuid4())
return activitypub.Update(
id=activity_id,
actor=user.remote_id,
to=['https://www.w3.org/ns/activitystreams#Public'],
object=self.to_activity()
).serialize()
class OrderedCollectionPageMixin(ObjectMixin):
''' just the paginator utilities, so you don't HAVE to
override ActivitypubMixin's to_activity (ie, for outbox) '''
@property
def collection_remote_id(self):
''' this can be overriden if there's a special remote id, ie outbox '''
return self.remote_id
def to_ordered_collection(self, queryset, \
remote_id=None, page=False, collection_only=False, **kwargs):
''' an ordered collection of whatevers '''
if not queryset.ordered:
raise RuntimeError('queryset must be ordered')
remote_id = remote_id or self.remote_id
if page:
return to_ordered_collection_page(
queryset, remote_id, **kwargs)
if collection_only or not hasattr(self, 'activity_serializer'):
serializer = activitypub.OrderedCollection
activity = {}
else:
serializer = self.activity_serializer
# a dict from the model fields
activity = generate_activity(self)
if remote_id:
activity['id'] = remote_id
paginated = Paginator(queryset, PAGE_LENGTH)
# add computed fields specific to orderd collections
activity['totalItems'] = paginated.count
activity['first'] = '%s?page=1' % remote_id
activity['last'] = '%s?page=%d' % (remote_id, paginated.num_pages)
return serializer(**activity).serialize()
# pylint: disable=unused-argument
def to_ordered_collection_page(
queryset, remote_id, id_only=False, page=1, **kwargs):
''' serialize and pagiante a queryset '''
paginated = Paginator(queryset, PAGE_LENGTH)
activity_page = paginated.page(page)
if id_only:
items = [s.remote_id for s in activity_page.object_list]
else:
items = [s.to_activity() for s in activity_page.object_list]
prev_page = next_page = None
if activity_page.has_next():
next_page = '%s?page=%d' % (remote_id, activity_page.next_page_number())
if activity_page.has_previous():
prev_page = '%s?page=%d' % \
(remote_id, activity_page.previous_page_number())
return activitypub.OrderedCollectionPage(
id='%s?page=%s' % (remote_id, page),
partOf=remote_id,
orderedItems=items,
next=next_page,
prev=prev_page
).serialize()
class OrderedCollectionMixin(OrderedCollectionPageMixin):
''' extends activitypub models to work as ordered collections '''
@property
def collection_queryset(self):
''' usually an ordered collection model aggregates a different model '''
raise NotImplementedError('Model must define collection_queryset')
activity_serializer = activitypub.OrderedCollection
def to_activity(self, **kwargs):
''' an ordered collection of the specified model queryset '''
return self.to_ordered_collection(self.collection_queryset, **kwargs)
class CollectionItemMixin(ActivitypubMixin):
''' for items that are part of an (Ordered)Collection '''
activity_serializer = activitypub.Add
object_field = collection_field = None
def to_add_activity(self):
''' AP for shelving a book'''
object_field = getattr(self, self.object_field)
collection_field = getattr(self, self.collection_field)
return activitypub.Add(
id='%s#add' % self.remote_id,
actor=self.user.remote_id,
object=object_field.to_activity(),
target=collection_field.remote_id
).serialize()
def to_remove_activity(self):
''' AP for un-shelving a book'''
object_field = getattr(self, self.object_field)
collection_field = getattr(self, self.collection_field)
return activitypub.Remove(
id='%s#remove' % self.remote_id,
actor=self.user.remote_id,
object=object_field.to_activity(),
target=collection_field.remote_id
).serialize()
class ActivitybMixin(ActivitypubMixin):
''' add this mixin for models that are AP serializable '''
def save(self, *args, **kwargs):
''' broadcast activity '''
super().save(*args, **kwargs)
self.broadcast(self.to_activity(), self.user)
def delete(self, *args, **kwargs):
''' nevermind, undo that activity '''
self.broadcast(self.to_undo_activity(), self.user)
super().delete(*args, **kwargs)
def to_undo_activity(self):
''' undo an action '''
return activitypub.Undo(
id='%s#undo' % self.remote_id,
actor=self.user.remote_id,
object=self.to_activity()
).serialize()

View file

@ -1 +0,0 @@
from . import *

View file

@ -1,25 +0,0 @@
''' activitypub model functionality '''
from bookwyrm import activitypub
from . import ActivitypubMixin
class ActivitybMixin(ActivitypubMixin):
''' add this mixin for models that are AP serializable '''
def save(self, *args, **kwargs):
''' broadcast activity '''
super().save(*args, **kwargs)
self.broadcast(self.to_activity(), self.user)
def delete(self, *args, **kwargs):
''' nevermind, undo that activity '''
self.broadcast(self.to_undo_activity(), self.user)
super().delete(*args, **kwargs)
def to_undo_activity(self):
''' undo an action '''
return activitypub.Undo(
id='%s#undo' % self.remote_id,
actor=self.user.remote_id,
object=self.to_activity()
).serialize()

View file

@ -1,94 +0,0 @@
''' activitypub objects like Person and Book'''
from base64 import b64encode
from uuid import uuid4
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
from django.apps import apps
from bookwyrm import activitypub
from . import ActivitypubMixin
class ObjectMixin(ActivitypubMixin):
''' add this mixin for object models that are AP serializable '''
def save(self, *args, **kwargs):
''' broadcast updated '''
# first off, we want to save normally no matter what
super().save(*args, **kwargs)
# we only want to handle updates, not newly created objects
if not self.id:
return
# this will work for lists, shelves
user = self.user if hasattr(self, 'user') else None
if not user:
# users don't have associated users, they ARE users
user_model = apps.get_model('bookwyrm.User', require_ready=True)
if isinstance(self, user_model):
user = self
# book data tracks last editor
elif hasattr(self, 'last_edited_by'):
user = self.last_edited_by
# again, if we don't know the user or they're remote, don't bother
if not user or not user.local:
return
# is this a deletion?
if self.deleted:
activity = self.to_delete_activity(user)
else:
activity = self.to_update_activity(user)
self.broadcast(activity, user)
def to_create_activity(self, user, **kwargs):
''' returns the object wrapped in a Create activity '''
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))
content = activity_object['content']
signed_message = signer.sign(SHA256.new(content.encode('utf8')))
signature = activitypub.Signature(
creator='%s#main-key' % user.remote_id,
created=activity_object['published'],
signatureValue=b64encode(signed_message).decode('utf8')
)
return activitypub.Create(
id=create_id,
actor=user.remote_id,
to=activity_object['to'],
cc=activity_object['cc'],
object=activity_object,
signature=signature,
).serialize()
def to_delete_activity(self, user):
''' notice of deletion '''
return activitypub.Delete(
id=self.remote_id + '/activity',
actor=user.remote_id,
to=['%s/followers' % user.remote_id],
cc=['https://www.w3.org/ns/activitystreams#Public'],
object=self.to_activity(),
).serialize()
def to_update_activity(self, user):
''' wrapper for Updates to an activity '''
activity_id = '%s#update/%s' % (self.remote_id, uuid4())
return activitypub.Update(
id=activity_id,
actor=user.remote_id,
to=['https://www.w3.org/ns/activitystreams#Public'],
object=self.to_activity()
).serialize()

View file

@ -1,115 +0,0 @@
''' lists of objects '''
from django.core.paginator import Paginator
from bookwyrm import activitypub
from bookwyrm.settings import PAGE_LENGTH
from . import ActivitypubMixin, ObjectMixin, generate_activity
class OrderedCollectionPageMixin(ObjectMixin):
''' just the paginator utilities, so you don't HAVE to
override ActivitypubMixin's to_activity (ie, for outbox) '''
@property
def collection_remote_id(self):
''' this can be overriden if there's a special remote id, ie outbox '''
return self.remote_id
def to_ordered_collection(self, queryset, \
remote_id=None, page=False, collection_only=False, **kwargs):
''' an ordered collection of whatevers '''
if not queryset.ordered:
raise RuntimeError('queryset must be ordered')
remote_id = remote_id or self.remote_id
if page:
return to_ordered_collection_page(
queryset, remote_id, **kwargs)
if collection_only or not hasattr(self, 'activity_serializer'):
serializer = activitypub.OrderedCollection
activity = {}
else:
serializer = self.activity_serializer
# a dict from the model fields
activity = generate_activity(self)
if remote_id:
activity['id'] = remote_id
paginated = Paginator(queryset, PAGE_LENGTH)
# add computed fields specific to orderd collections
activity['totalItems'] = paginated.count
activity['first'] = '%s?page=1' % remote_id
activity['last'] = '%s?page=%d' % (remote_id, paginated.num_pages)
return serializer(**activity).serialize()
# pylint: disable=unused-argument
def to_ordered_collection_page(
queryset, remote_id, id_only=False, page=1, **kwargs):
''' serialize and pagiante a queryset '''
paginated = Paginator(queryset, PAGE_LENGTH)
activity_page = paginated.page(page)
if id_only:
items = [s.remote_id for s in activity_page.object_list]
else:
items = [s.to_activity() for s in activity_page.object_list]
prev_page = next_page = None
if activity_page.has_next():
next_page = '%s?page=%d' % (remote_id, activity_page.next_page_number())
if activity_page.has_previous():
prev_page = '%s?page=%d' % \
(remote_id, activity_page.previous_page_number())
return activitypub.OrderedCollectionPage(
id='%s?page=%s' % (remote_id, page),
partOf=remote_id,
orderedItems=items,
next=next_page,
prev=prev_page
).serialize()
class OrderedCollectionMixin(OrderedCollectionPageMixin):
''' extends activitypub models to work as ordered collections '''
@property
def collection_queryset(self):
''' usually an ordered collection model aggregates a different model '''
raise NotImplementedError('Model must define collection_queryset')
activity_serializer = activitypub.OrderedCollection
def to_activity(self, **kwargs):
''' an ordered collection of the specified model queryset '''
return self.to_ordered_collection(self.collection_queryset, **kwargs)
class CollectionItemMixin(ActivitypubMixin):
''' for items that are part of an (Ordered)Collection '''
activity_serializer = activitypub.Add
object_field = collection_field = None
def to_add_activity(self):
''' AP for shelving a book'''
object_field = getattr(self, self.object_field)
collection_field = getattr(self, self.collection_field)
return activitypub.Add(
id='%s#add' % self.remote_id,
actor=self.user.remote_id,
object=object_field.to_activity(),
target=collection_field.remote_id
).serialize()
def to_remove_activity(self):
''' AP for un-shelving a book'''
object_field = getattr(self, self.object_field)
collection_field = getattr(self, self.collection_field)
return activitypub.Remove(
id='%s#remove' % self.remote_id,
actor=self.user.remote_id,
object=object_field.to_activity(),
target=collection_field.remote_id
).serialize()

View file

@ -7,7 +7,7 @@ from model_utils.managers import InheritanceManager
from bookwyrm import activitypub from bookwyrm import activitypub
from bookwyrm.settings import DOMAIN from bookwyrm.settings import DOMAIN
from .activitypub_mixin import ObjectMixin, OrderedCollectionPageMixin from .activitypub_mixin import OrderedCollectionPageMixin, ObjectMixin
from .base_model import BookWyrmModel from .base_model import BookWyrmModel
from . import fields from . import fields

View file

@ -23,7 +23,7 @@
{% include 'snippets/book_titleby.html' with book=item.book %} {% include 'snippets/book_titleby.html' with book=item.book %}
</td> </td>
<td> <td>
{% include 'snippets/username.html' with user=item.added_by %} {% include 'snippets/username.html' with user=item.user %}
</td> </td>
<td> <td>
<div class="field has-addons"> <div class="field has-addons">

View file

@ -31,9 +31,9 @@
</div> </div>
<div class="card-footer has-background-white-bis"> <div class="card-footer has-background-white-bis">
<div class="card-footer-item"> <div class="card-footer-item">
<p>Added by {% include 'snippets/username.html' with user=item.added_by %}</p> <p>Added by {% include 'snippets/username.html' with user=item.user %}</p>
</div> </div>
{% if list.user == request.user or list.curation == 'open' and item.added_by == request.user %} {% if list.user == request.user or list.curation == 'open' and item.user == request.user %}
<form name="add-book" method="post" action="{% url 'list-remove-book' list.id %}" class="card-footer-item"> <form name="add-book" method="post" action="{% url 'list-remove-book' list.id %}" class="card-footer-item">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="item" value="{{ item.id }}"> <input type="hidden" name="item" value="{{ item.id }}">

View file

@ -38,7 +38,7 @@ class List(TestCase):
item = models.ListItem.objects.create( item = models.ListItem.objects.create(
book_list=self.list, book_list=self.list,
book=book, book=book,
added_by=self.user, user=self.user,
) )
self.assertTrue(item.approved) self.assertTrue(item.approved)

View file

@ -146,7 +146,7 @@ class GoodreadsImport(TestCase):
''' goodreads import added a book, this adds related connections ''' ''' goodreads import added a book, this adds related connections '''
shelf = self.user.shelf_set.filter(identifier='to-read').first() shelf = self.user.shelf_set.filter(identifier='to-read').first()
models.ShelfBook.objects.create( models.ShelfBook.objects.create(
shelf=shelf, added_by=self.user, book=self.book) shelf=shelf, user=self.user, book=self.book)
import_job = models.ImportJob.objects.create(user=self.user) import_job = models.ImportJob.objects.create(user=self.user)
datafile = pathlib.Path(__file__).parent.joinpath('data/goodreads.csv') datafile = pathlib.Path(__file__).parent.joinpath('data/goodreads.csv')

View file

@ -92,7 +92,7 @@ class FeedMessageViews(TestCase):
''' gets books the ~*~ algorithm ~*~ thinks you want to post about ''' ''' gets books the ~*~ algorithm ~*~ thinks you want to post about '''
models.ShelfBook.objects.create( models.ShelfBook.objects.create(
book=self.book, book=self.book,
added_by=self.local_user, user=self.local_user,
shelf=self.local_user.shelf_set.get(identifier='reading') shelf=self.local_user.shelf_set.get(identifier='reading')
) )
suggestions = views.feed.get_suggested_books(self.local_user) suggestions = views.feed.get_suggested_books(self.local_user)

View file

@ -162,7 +162,7 @@ class ListViews(TestCase):
view = views.Curate.as_view() view = views.Curate.as_view()
pending = models.ListItem.objects.create( pending = models.ListItem.objects.create(
book_list=self.list, book_list=self.list,
added_by=self.local_user, user=self.local_user,
book=self.book, book=self.book,
approved=False approved=False
) )
@ -185,7 +185,7 @@ class ListViews(TestCase):
view = views.Curate.as_view() view = views.Curate.as_view()
pending = models.ListItem.objects.create( pending = models.ListItem.objects.create(
book_list=self.list, book_list=self.list,
added_by=self.local_user, user=self.local_user,
book=self.book, book=self.book,
approved=False approved=False
) )
@ -211,7 +211,7 @@ class ListViews(TestCase):
views.list.add_book(request, self.list.id) views.list.add_book(request, self.list.id)
item = self.list.listitem_set.get() item = self.list.listitem_set.get()
self.assertEqual(item.book, self.book) self.assertEqual(item.book, self.book)
self.assertEqual(item.added_by, self.local_user) self.assertEqual(item.user, self.local_user)
self.assertTrue(item.approved) self.assertTrue(item.approved)
@ -227,7 +227,7 @@ class ListViews(TestCase):
views.list.add_book(request, self.list.id) views.list.add_book(request, self.list.id)
item = self.list.listitem_set.get() item = self.list.listitem_set.get()
self.assertEqual(item.book, self.book) self.assertEqual(item.book, self.book)
self.assertEqual(item.added_by, self.rat) self.assertEqual(item.user, self.rat)
self.assertTrue(item.approved) self.assertTrue(item.approved)
@ -243,7 +243,7 @@ class ListViews(TestCase):
views.list.add_book(request, self.list.id) views.list.add_book(request, self.list.id)
item = self.list.listitem_set.get() item = self.list.listitem_set.get()
self.assertEqual(item.book, self.book) self.assertEqual(item.book, self.book)
self.assertEqual(item.added_by, self.rat) self.assertEqual(item.user, self.rat)
self.assertFalse(item.approved) self.assertFalse(item.approved)
@ -259,7 +259,7 @@ class ListViews(TestCase):
views.list.add_book(request, self.list.id) views.list.add_book(request, self.list.id)
item = self.list.listitem_set.get() item = self.list.listitem_set.get()
self.assertEqual(item.book, self.book) self.assertEqual(item.book, self.book)
self.assertEqual(item.added_by, self.local_user) self.assertEqual(item.user, self.local_user)
self.assertTrue(item.approved) self.assertTrue(item.approved)
@ -267,7 +267,7 @@ class ListViews(TestCase):
''' take an item off a list ''' ''' take an item off a list '''
item = models.ListItem.objects.create( item = models.ListItem.objects.create(
book_list=self.list, book_list=self.list,
added_by=self.local_user, user=self.local_user,
book=self.book, book=self.book,
) )
self.assertTrue(self.list.listitem_set.exists()) self.assertTrue(self.list.listitem_set.exists())
@ -285,7 +285,7 @@ class ListViews(TestCase):
''' take an item off a list ''' ''' take an item off a list '''
item = models.ListItem.objects.create( item = models.ListItem.objects.create(
book_list=self.list, book_list=self.list,
added_by=self.local_user, user=self.local_user,
book=self.book, book=self.book,
) )
self.assertTrue(self.list.listitem_set.exists()) self.assertTrue(self.list.listitem_set.exists())

View file

@ -66,7 +66,7 @@ class ReadingViews(TestCase):
''' begin a book ''' ''' begin a book '''
to_read_shelf = self.local_user.shelf_set.get(identifier='to-read') to_read_shelf = self.local_user.shelf_set.get(identifier='to-read')
models.ShelfBook.objects.create( models.ShelfBook.objects.create(
shelf=to_read_shelf, book=self.book, added_by=self.local_user) shelf=to_read_shelf, book=self.book, user=self.local_user)
shelf = self.local_user.shelf_set.get(identifier='reading') shelf = self.local_user.shelf_set.get(identifier='reading')
self.assertEqual(to_read_shelf.books.get(), self.book) self.assertEqual(to_read_shelf.books.get(), self.book)
self.assertFalse(shelf.books.exists()) self.assertFalse(shelf.books.exists())

View file

@ -77,12 +77,12 @@ class Book(View):
.order_by('-updated_date') .order_by('-updated_date')
user_shelves = models.ShelfBook.objects.filter( user_shelves = models.ShelfBook.objects.filter(
added_by=request.user, book=book user=request.user, book=book
) )
other_edition_shelves = models.ShelfBook.objects.filter( other_edition_shelves = models.ShelfBook.objects.filter(
~Q(book=book), ~Q(book=book),
added_by=request.user, user=request.user,
book__parent_work=book.parent_work, book__parent_work=book.parent_work,
) )

View file

@ -182,7 +182,7 @@ def add_book(request, list_id):
models.ListItem.objects.create( models.ListItem.objects.create(
book=book, book=book,
book_list=book_list, book_list=book_list,
added_by=request.user, user=request.user,
) )
elif book_list.curation == 'curated': elif book_list.curation == 'curated':
# make a pending entry # make a pending entry
@ -190,7 +190,7 @@ def add_book(request, list_id):
approved=False, approved=False,
book=book, book=book,
book_list=book_list, book_list=book_list,
added_by=request.user, user=request.user,
) )
else: else:
# you can't add to this list, what were you THINKING # you can't add to this list, what were you THINKING
@ -205,7 +205,7 @@ def remove_book(request, list_id):
book_list = get_object_or_404(models.List, id=list_id) book_list = get_object_or_404(models.List, id=list_id)
item = get_object_or_404(models.ListItem, id=request.POST.get('item')) item = get_object_or_404(models.ListItem, id=request.POST.get('item'))
if not book_list.user == request.user and not item.added_by == request.user: if not book_list.user == request.user and not item.user == request.user:
return HttpResponseNotFound() return HttpResponseNotFound()
item.delete() item.delete()

View file

@ -44,7 +44,7 @@ def start_reading(request, book_id):
# this just means it isn't currently on the user's shelves # this just means it isn't currently on the user's shelves
pass pass
models.ShelfBook.objects.create( models.ShelfBook.objects.create(
book=book, shelf=shelf, added_by=request.user) book=book, shelf=shelf, user=request.user)
# post about it (if you want) # post about it (if you want)
if request.POST.get('post-status'): if request.POST.get('post-status'):
@ -81,7 +81,7 @@ def finish_reading(request, book_id):
# this just means it isn't currently on the user's shelves # this just means it isn't currently on the user's shelves
pass pass
models.ShelfBook.objects.create( models.ShelfBook.objects.create(
book=book, shelf=shelf, added_by=request.user) book=book, shelf=shelf, user=request.user)
# post about it (if you want) # post about it (if you want)
if request.POST.get('post-status'): if request.POST.get('post-status'):

View file

@ -49,7 +49,7 @@ class Shelf(View):
return ActivitypubResponse(shelf.to_activity(**request.GET)) return ActivitypubResponse(shelf.to_activity(**request.GET))
books = models.ShelfBook.objects.filter( books = models.ShelfBook.objects.filter(
added_by=user, shelf=shelf user=user, shelf=shelf
).order_by('-updated_date').all() ).order_by('-updated_date').all()
data = { data = {
@ -136,7 +136,7 @@ def shelve(request):
# this just means it isn't currently on the user's shelves # this just means it isn't currently on the user's shelves
pass pass
models.ShelfBook.objects.create( models.ShelfBook.objects.create(
book=book, shelf=desired_shelf, added_by=request.user) book=book, shelf=desired_shelf, user=request.user)
# post about "want to read" shelves # post about "want to read" shelves
if desired_shelf.identifier == 'to-read': if desired_shelf.identifier == 'to-read':