diff --git a/bookwyrm/forms.py b/bookwyrm/forms.py
index 5a04af8c..e442dbf4 100644
--- a/bookwyrm/forms.py
+++ b/bookwyrm/forms.py
@@ -447,7 +447,7 @@ class ListForm(CustomForm):
class ListItemForm(CustomForm):
class Meta:
model = models.ListItem
- fields = ["notes"]
+ fields = ["user", "book", "book_list", "notes"]
class GroupForm(CustomForm):
diff --git a/bookwyrm/models/list.py b/bookwyrm/models/list.py
index 733ee044..7dff7214 100644
--- a/bookwyrm/models/list.py
+++ b/bookwyrm/models/list.py
@@ -2,6 +2,7 @@
import uuid
from django.apps import apps
+from django.core.exceptions import PermissionDenied
from django.db import models
from django.db.models import Q
from django.utils import timezone
@@ -74,6 +75,22 @@ class List(OrderedCollectionMixin, BookWyrmModel):
return
super().raise_not_editable(viewer)
+ def raise_not_submittable(self, viewer):
+ """can the user submit a book to the list?"""
+ # if you can't view the list you can't submit to it
+ self.raise_visible_to_user(viewer)
+
+ # all good if you're the owner or the list is open
+ if self.user == viewer or self.curation in ["open", "curated"]:
+ return
+ if self.curation == "group":
+ is_group_member = GroupMember.objects.filter(
+ group=self.group, user=viewer
+ ).exists()
+ if is_group_member:
+ return
+ raise PermissionDenied()
+
@classmethod
def followers_filter(cls, queryset, viewer):
"""Override filter for "followers" privacy level to allow non-following
diff --git a/bookwyrm/templates/lists/add_item_modal.html b/bookwyrm/templates/lists/add_item_modal.html
new file mode 100644
index 00000000..5c210d46
--- /dev/null
+++ b/bookwyrm/templates/lists/add_item_modal.html
@@ -0,0 +1,45 @@
+{% extends 'components/modal.html' %}
+{% load i18n %}
+{% load utilities %}
+{% load group_tags %}
+
+{% block modal-title %}
+{% if list.curation == 'open' or request.user == list.user or list.group|is_member:request.user %}
+ {% blocktrans trimmed with title=book|book_title %}
+ Add "{{ title }}" to this list
+ {% endblocktrans %}
+{% else %}
+ {% blocktrans trimmed with title=book|book_title %}
+ Suggest "{{ title }}" for this list
+ {% endblocktrans %}
+{% endif %}
+{% endblock %}
+
+{% block modal-form-open %}
+
- {% trans "Edit notes:" %}
-
+
+ {% trans "Edit notes" %}
+
+
-
+ {% include "lists/edit_item_form.html" %}
{% endif %}
@@ -119,24 +107,12 @@
- {% trans "Add notes:" %}
-
+
+ {% trans "Add notes" %}
+
+
-
+ {% include "lists/edit_item_form.html" %}
{% endif %}
@@ -261,23 +237,19 @@
{% include 'snippets/book_titleby.html' with book=book %}
-
+ {% if list.curation == 'open' or request.user == list.user or list.group|is_member:request.user %}
+ {% trans "Add" %}
+ {% else %}
+ {% trans "Suggest" %}
+ {% endif %}
+
+ {% include "lists/add_item_modal.html" with id=modal_id %}
{% endfor %}
diff --git a/bookwyrm/views/list/list.py b/bookwyrm/views/list/list.py
index 296b7e9f..fa92142a 100644
--- a/bookwyrm/views/list/list.py
+++ b/bookwyrm/views/list/list.py
@@ -217,53 +217,35 @@ def delete_list(request, list_id):
@require_POST
@login_required
+@transaction.atomic
def add_book(request):
"""put a book on a list"""
- book_list = get_object_or_404(models.List, id=request.POST.get("list"))
- is_group_member = False
- if book_list.curation == "group":
- is_group_member = models.GroupMember.objects.filter(
- group=book_list.group, user=request.user
- ).exists()
+ book_list = get_object_or_404(models.List, id=request.POST.get("book_list"))
+ # make sure the user is allowed to submit to this list
+ book_list.raise_not_submittable(request.user)
- book_list.raise_visible_to_user(request.user)
+ form = forms.ListItemForm(request.POST)
+ if not form.is_valid():
+ # this shouldn't happen, there aren't validated fields
+ raise Exception(form.errors)
+ item = form.save(commit=False)
+
+ if book_list.curation == "curated":
+ # make a pending entry at the end of the list
+ order_max = (book_list.listitem_set.aggregate(Max("order"))["order__max"]) or 0
+ item.approved = False
+ else:
+ # add the book at the latest order of approved books, before pending books
+ order_max = (
+ book_list.listitem_set.filter(approved=True).aggregate(Max("order"))[
+ "order__max"
+ ]
+ ) or 0
+ increment_order_in_reverse(book_list.id, order_max + 1)
+ item.order = order_max + 1
- book = get_object_or_404(models.Edition, id=request.POST.get("book"))
- # do you have permission to add to the list?
try:
- if (
- request.user == book_list.user
- or is_group_member
- or book_list.curation == "open"
- ):
- # add the book at the latest order of approved books, before pending books
- order_max = (
- book_list.listitem_set.filter(approved=True).aggregate(Max("order"))[
- "order__max"
- ]
- ) or 0
- increment_order_in_reverse(book_list.id, order_max + 1)
- models.ListItem.objects.create(
- book=book,
- book_list=book_list,
- user=request.user,
- order=order_max + 1,
- )
- elif book_list.curation == "curated":
- # make a pending entry at the end of the list
- order_max = (
- book_list.listitem_set.aggregate(Max("order"))["order__max"]
- ) or 0
- models.ListItem.objects.create(
- approved=False,
- book=book,
- book_list=book_list,
- user=request.user,
- order=order_max + 1,
- )
- else:
- # you can't add to this list, what were you THINKING
- return HttpResponseBadRequest()
+ item.save()
except IntegrityError:
# if the book is already on the list, don't flip out
pass