Use form for saving list items
This commit is contained in:
parent
a43731b335
commit
f85158191b
|
@ -447,7 +447,7 @@ class ListForm(CustomForm):
|
||||||
class ListItemForm(CustomForm):
|
class ListItemForm(CustomForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.ListItem
|
model = models.ListItem
|
||||||
fields = ["notes"]
|
fields = ["user", "book", "book_list", "notes"]
|
||||||
|
|
||||||
|
|
||||||
class GroupForm(CustomForm):
|
class GroupForm(CustomForm):
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
@ -74,6 +75,22 @@ class List(OrderedCollectionMixin, BookWyrmModel):
|
||||||
return
|
return
|
||||||
super().raise_not_editable(viewer)
|
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
|
@classmethod
|
||||||
def followers_filter(cls, queryset, viewer):
|
def followers_filter(cls, queryset, viewer):
|
||||||
"""Override filter for "followers" privacy level to allow non-following
|
"""Override filter for "followers" privacy level to allow non-following
|
||||||
|
|
|
@ -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 "<em>{{ title }}</em>" to this list
|
||||||
|
{% endblocktrans %}
|
||||||
|
{% else %}
|
||||||
|
{% blocktrans trimmed with title=book|book_title %}
|
||||||
|
Suggest "<em>{{ title }}</em>" for this list
|
||||||
|
{% endblocktrans %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block modal-form-open %}
|
||||||
|
<form
|
||||||
|
name="add-book-{{ book.id }}"
|
||||||
|
method="POST"
|
||||||
|
action="{% url 'list-add-book' %}{% if query %}?q={{ query }}{% endif %}"
|
||||||
|
>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block modal-body %}
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="book" value="{{ book.id }}">
|
||||||
|
<input type="hidden" name="book_list" value="{{ list.id }}">
|
||||||
|
<input type="hidden" name="user" value="{{ request.user.id }}">
|
||||||
|
{% include "lists/item_notes_field.html" with form_id=id show_label=True %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block modal-footer %}
|
||||||
|
<button type="submit" class="button is-link">
|
||||||
|
{% if list.curation == 'open' or request.user == list.user or list.group|is_member:request.user %}
|
||||||
|
{% trans "Add" %}
|
||||||
|
{% else %}
|
||||||
|
{% trans "Suggest" %}
|
||||||
|
{% endif %}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="button" data-modal-close>{% trans "Cancel" %}</button>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block modal-form-close %}</form>{% endblock %}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{% load i18n %}
|
||||||
|
<form
|
||||||
|
name="edit-notes-{{ item.id }}"
|
||||||
|
method="POST"
|
||||||
|
action="{% url 'list-item' list.id item.id %}"
|
||||||
|
>
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="book" value="{{ book.id }}">
|
||||||
|
<input type="hidden" name="book_list" value="{{ list.id }}">
|
||||||
|
<input type="hidden" name="user" value="{{ request.user.id }}">
|
||||||
|
{% include "lists/item_notes_field.html" with form_id=item.id %}
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<button type="submit" class="button is-success">
|
||||||
|
{% trans "Save" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
{% load i18n %}
|
||||||
|
<div class="field">
|
||||||
|
<label
|
||||||
|
for="id_notes_{{ form_id }}"
|
||||||
|
class="{% if show_label %}label{% else %}is-sr-only{% endif %}"
|
||||||
|
>
|
||||||
|
{% trans "Notes:" %}
|
||||||
|
</label>
|
||||||
|
<div class="control">
|
||||||
|
<textarea
|
||||||
|
class="textarea"
|
||||||
|
id="id_notes_{{ form_id }}"
|
||||||
|
maxlength="300"
|
||||||
|
name="notes"
|
||||||
|
aria-described-by="notes_description_{{ form_id }}"
|
||||||
|
>{{ item.notes|default:'' }}</textarea>
|
||||||
|
</div>
|
||||||
|
<p class="help" id="notes_description_{{ form_id }}">
|
||||||
|
{% trans "An optional note that will be displayed with the book." %}
|
||||||
|
</p>
|
||||||
|
</div>
|
|
@ -1,8 +1,8 @@
|
||||||
{% extends 'lists/layout.html' %}
|
{% extends 'lists/layout.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load rating_tags %}
|
{% load rating_tags %}
|
||||||
{% load book_display_tags %}
|
|
||||||
{% load group_tags %}
|
{% load group_tags %}
|
||||||
|
{% load book_display_tags %}
|
||||||
{% load markdown %}
|
{% load markdown %}
|
||||||
{% load utilities %}
|
{% load utilities %}
|
||||||
|
|
||||||
|
@ -92,24 +92,12 @@
|
||||||
<div>
|
<div>
|
||||||
<details class="details-panel box">
|
<details class="details-panel box">
|
||||||
<summary>
|
<summary>
|
||||||
{% trans "Edit notes:" %}
|
<span role="heading" aria-level="3">
|
||||||
<span class="details-close icon icon-pencil" aria-hidden></span>
|
{% trans "Edit notes" %}
|
||||||
|
<span class="details-close icon icon-pencil" aria-hidden></span>
|
||||||
|
</span>
|
||||||
</summary>
|
</summary>
|
||||||
<form name="edit-notes-{{ item.id }}" method="POST" action="{% url 'list-item' list.id item.id %}">
|
{% include "lists/edit_item_form.html" %}
|
||||||
{% csrf_token %}
|
|
||||||
<div class="field">
|
|
||||||
<div class="control">
|
|
||||||
<textarea class="textarea" maxlength="300" name="notes">{{ item.notes|default:'' }}</textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="control">
|
|
||||||
<button type="submit" class="button is-success">
|
|
||||||
{% trans "Save" %}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -119,24 +107,12 @@
|
||||||
<div>
|
<div>
|
||||||
<details class="details-panel box">
|
<details class="details-panel box">
|
||||||
<summary>
|
<summary>
|
||||||
{% trans "Add notes:" %}
|
<span role="heading" aria-level="3">
|
||||||
<span class="details-close icon icon-plus" aria-hidden></span>
|
{% trans "Add notes" %}
|
||||||
|
<span class="details-close icon icon-plus" aria-hidden></span>
|
||||||
|
</span>
|
||||||
</summary>
|
</summary>
|
||||||
<form name="add-notes-{{ item.id }}" method="POST" action="{% url 'list-item' list.id item.id %}">
|
{% include "lists/edit_item_form.html" %}
|
||||||
{% csrf_token %}
|
|
||||||
<div class="field">
|
|
||||||
<div class="control">
|
|
||||||
<textarea class="textarea" maxlength="300" name="notes">{{ item.notes|default:'' }}</textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="control">
|
|
||||||
<button type="submit" class="button is-success">
|
|
||||||
{% trans "Save" %}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -261,23 +237,19 @@
|
||||||
<div class="column ml-3">
|
<div class="column ml-3">
|
||||||
<p>{% include 'snippets/book_titleby.html' with book=book %}</p>
|
<p>{% include 'snippets/book_titleby.html' with book=book %}</p>
|
||||||
|
|
||||||
<form
|
{% join "add_item" list.id book.id as modal_id %}
|
||||||
class="mt-1"
|
<button
|
||||||
name="add-book-{{ book.id }}"
|
type="button"
|
||||||
method="post"
|
class="button is-small is-link"
|
||||||
action="{% url 'list-add-book' %}{% if query %}?q={{ query }}{% endif %}"
|
data-modal-open="{{ modal_id }}"
|
||||||
>
|
>
|
||||||
{% csrf_token %}
|
{% if list.curation == 'open' or request.user == list.user or list.group|is_member:request.user %}
|
||||||
<input type="hidden" name="book" value="{{ book.id }}">
|
{% trans "Add" %}
|
||||||
<input type="hidden" name="list" value="{{ list.id }}">
|
{% else %}
|
||||||
<button type="submit" class="button is-small is-link">
|
{% trans "Suggest" %}
|
||||||
{% if list.curation == 'open' or request.user == list.user or list.group|is_member:request.user %}
|
{% endif %}
|
||||||
{% trans "Add" %}
|
</button>
|
||||||
{% else %}
|
{% include "lists/add_item_modal.html" with id=modal_id %}
|
||||||
{% trans "Suggest" %}
|
|
||||||
{% endif %}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -217,53 +217,35 @@ def delete_list(request, list_id):
|
||||||
|
|
||||||
@require_POST
|
@require_POST
|
||||||
@login_required
|
@login_required
|
||||||
|
@transaction.atomic
|
||||||
def add_book(request):
|
def add_book(request):
|
||||||
"""put a book on a list"""
|
"""put a book on a list"""
|
||||||
book_list = get_object_or_404(models.List, id=request.POST.get("list"))
|
book_list = get_object_or_404(models.List, id=request.POST.get("book_list"))
|
||||||
is_group_member = False
|
# make sure the user is allowed to submit to this list
|
||||||
if book_list.curation == "group":
|
book_list.raise_not_submittable(request.user)
|
||||||
is_group_member = models.GroupMember.objects.filter(
|
|
||||||
group=book_list.group, user=request.user
|
|
||||||
).exists()
|
|
||||||
|
|
||||||
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:
|
try:
|
||||||
if (
|
item.save()
|
||||||
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()
|
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
# if the book is already on the list, don't flip out
|
# if the book is already on the list, don't flip out
|
||||||
pass
|
pass
|
||||||
|
|
Loading…
Reference in New Issue