mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-11-27 12:01:14 +00:00
Add to and edit lists
This commit is contained in:
parent
4883231347
commit
acd922970a
8 changed files with 149 additions and 60 deletions
45
bookwyrm/templates/lists/form.html
Normal file
45
bookwyrm/templates/lists/form.html
Normal file
|
@ -0,0 +1,45 @@
|
|||
{% csrf_token %}
|
||||
<input type="hidden" name="user" value="{{ request.user.id }}">
|
||||
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="field">
|
||||
<label class="label" for="id_name">Name:</label>
|
||||
{{ list_form.name }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="id_description">Description:</label>
|
||||
{{ list_form.description }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<fieldset class="field">
|
||||
<legend class="label">List curation:</legend>
|
||||
|
||||
<label class="field">
|
||||
<input type="radio" name="curation" value="closed"{% if not list or list.curation == 'closed' %} checked{% endif %}> Closed
|
||||
<p class="help mb-2">Only you can add and remove books to this list</p>
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<input type="radio" name="curation" value="curated"{% if list.curation == 'curated' %} checked{% endif %}> Curated
|
||||
<p class="help mb-2">Anyone can suggest books, subject to your approval</p>
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<input type="radio" name="curation" value="open"{% if list.curation == 'open' %} checked{% endif %}> Open
|
||||
<p class="help mb-2">Anyone can add books to this list, but only you can remove them</p>
|
||||
</label>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
{% include 'snippets/privacy_select.html' with current=list.privacy %}
|
||||
</div>
|
||||
<div class="control">
|
||||
<button type="submit" class="button is-primary">Save</button>
|
||||
{% include 'snippets/toggle/close_button.html' with controls_text='create-list' text="Cancel" %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,28 +1,66 @@
|
|||
{% extends 'layout.html' %}
|
||||
{% load bookwyrm_tags %}
|
||||
{% block content %}
|
||||
|
||||
<header class="columns content">
|
||||
<div class="column">
|
||||
<h1 class="title">{{ list.name }} <span class="subtitle">{% include 'snippets/privacy-icons.html' with item=list %}</span></h1>
|
||||
<p class="subtitle help">Created by {% include 'snippets/username.html' with user=list.user %}</p>
|
||||
{% include 'snippets/trimmed_text.html' with full=list.description %}
|
||||
</div>
|
||||
{% if request.user == list.user %}
|
||||
<div class="column is-narrow">
|
||||
{% include 'snippets/toggle/open_button.html' with text="Edit list" icon="pencil" controls_text="edit-list" %}
|
||||
{% include 'snippets/toggle/open_button.html' with text="Edit list" icon="pencil" controls_text="create-list" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</header>
|
||||
|
||||
<form name="create-list" method="post" action="{% url 'list' list.id %}" class="box hidden" id="create-list">
|
||||
<h3 class="title">Edit list</h3>
|
||||
{% include 'lists/form.html' %}
|
||||
</form>
|
||||
|
||||
|
||||
<div class="columns content">
|
||||
<section class="column">
|
||||
<h2>Books</h2>
|
||||
<section class="column is-three-quarters">
|
||||
{% if not list.books.exists %}
|
||||
<p>This list is currently empty</p>
|
||||
{% else %}
|
||||
{% for book in list.books.all %}
|
||||
{{ book }}
|
||||
<ol>
|
||||
{% for item in list.listitem_set.all %}
|
||||
<li class="block">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<a href="{{ book.local_path }}">{% include 'snippets/book_cover.html' with book=item.book size="medium" %}</a>
|
||||
<div class="card-header-title is-flex-direction-column is-align-items-self-start">
|
||||
<span>{% include 'snippets/book_titleby.html' with book=item.book %}</span>
|
||||
{% include 'snippets/stars.html' with rating=item.book|rating:request.user %}
|
||||
{% include 'snippets/shelve_button.html' with book=item.book %}
|
||||
</div>
|
||||
</header>
|
||||
{% if item.note %}
|
||||
<div class="card-content">
|
||||
{% include 'snippets/trimmed_text.html' with full=item.note %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="card-footer has-background-white-bis">
|
||||
<div class="card-footer-item">
|
||||
<p>Added by {% include 'snippets/username.html' with user=item.added_by %}</p>
|
||||
</div>
|
||||
<form name="add-book" method="post" action="{% url 'list-add-book' list.id %}" class="card-footer-item">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="book" value="{{ book.id }}">
|
||||
<button type="submit" class="button is-small is-danger">Remove</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
{% if not list.curation == 'closed' or request.user == list.user %}
|
||||
<section class="column is-one-quarter">
|
||||
<h2>Add Books</h2>
|
||||
{% for book in suggested_books %}
|
||||
|
@ -34,11 +72,13 @@
|
|||
<p>{% include 'snippets/book_titleby.html' with book=book %}</p>
|
||||
<form name="add-book" method="post" action="{% url 'list-add-book' list.id %}">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="book" value="{{ book.id }}">
|
||||
<button type="submit" class="button is-small is-link">Add</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -18,50 +18,7 @@
|
|||
|
||||
<form name="create-list" method="post" action="{% url 'lists' %}" class="box hidden" id="create-list">
|
||||
<h3 class="title">Create list</h3>
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="user" value="{{ request.user.id }}">
|
||||
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="field">
|
||||
<label class="label" for="id_name">Name:</label>
|
||||
{{ list_form.name }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="id_description">Description:</label>
|
||||
{{ list_form.description }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<fieldset class="field">
|
||||
<legend class="label">List curation:</legend>
|
||||
|
||||
<label class="field">
|
||||
<input type="radio" name="curation" value="closed" checked> Closed
|
||||
<p class="help mb-2">Only you can add and remove books to this list</p>
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<input type="radio" name="curation" value="curated"> Curated
|
||||
<p class="help mb-2">Anyone can suggest books, subject to your approval</p>
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<input type="radio" name="curation" value="open"> Open
|
||||
<p class="help mb-2">Anyone can add books to this list, but only you can remove them</p>
|
||||
</label>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
{% include 'snippets/privacy_select.html' %}
|
||||
</div>
|
||||
<div class="control">
|
||||
<button type="submit" class="button is-primary">Save</button>
|
||||
{% include 'snippets/toggle/close_button.html' with controls_text='create-list' text="Cancel" %}
|
||||
</div>
|
||||
</div>
|
||||
{% include 'lists/form.html' %}
|
||||
</form>
|
||||
|
||||
{% if request.user.list_set.exists %}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<label class="is-sr-only" for="rating-no-rating-{{ book.id }}">No rating</label>
|
||||
<input class="is-sr-only" type="radio" name="rating" value="" id="rating-no-rating-{{ book.id }}" checked>
|
||||
{% for i in '12345'|make_list %}
|
||||
<input class="is-sr-only" id="rating-book{{book.id}}-star-{{ forloop.counter }}" type="radio" name="rating" value="{{ forloop.counter }}" {% if book|rating:user == forloop.counter %}checked{% endif %}>
|
||||
<input class="is-sr-only" id="rating-book{{book.id}}-star-{{ forloop.counter }}" type="radio" name="rating" value="{{ forloop.counter }}" {% if book|user_rating:user == forloop.counter %}checked{% endif %}>
|
||||
<label class="icon icon-star-empty" for="rating-book{{book.id}}-star-{{ forloop.counter }}">
|
||||
<span class="is-sr-only">{{ forloop.counter }} star{{ forloop.counter | pluralize }}</span>
|
||||
</label>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<div class="stars">
|
||||
<p class="stars">
|
||||
<span class="is-sr-only">{% if rating %}{{ rating|floatformat }} star{{ rating|floatformat | pluralize }}{% else %}No rating{% endif %}</span>
|
||||
{% for i in '12345'|make_list %}
|
||||
<span class="icon icon-star-{% if rating >= forloop.counter %}full{% elif rating|floatformat:0 >= forloop.counter|floatformat:0 %}half{% else %}empty{% endif %}">
|
||||
<span class="icon icon-star-{% if rating >= forloop.counter %}full{% elif rating|floatformat:0 >= forloop.counter|floatformat:0 %}half{% else %}empty{% endif %}" aria-hidden="true">
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
</p>
|
||||
|
|
|
@ -4,9 +4,10 @@ from datetime import datetime
|
|||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from django import template
|
||||
from django.db.models import Avg
|
||||
from django.utils import timezone
|
||||
|
||||
from bookwyrm import models
|
||||
from bookwyrm import models, views
|
||||
from bookwyrm.views.status import to_markdown
|
||||
|
||||
|
||||
|
@ -20,6 +21,17 @@ def dict_key(d, k):
|
|||
|
||||
@register.filter(name='rating')
|
||||
def get_rating(book, user):
|
||||
''' get the overall rating of a book '''
|
||||
queryset = views.helpers.get_activity_feed(
|
||||
user,
|
||||
['public', 'followers', 'unlisted', 'direct'],
|
||||
queryset=models.Review.objects.filter(book=book),
|
||||
)
|
||||
return queryset.aggregate(Avg('rating'))['rating__avg']
|
||||
|
||||
|
||||
@register.filter(name='user_rating')
|
||||
def get_user_rating(book, user):
|
||||
''' get a user's rating of a book '''
|
||||
rating = models.Review.objects.filter(
|
||||
user=user,
|
||||
|
|
|
@ -91,7 +91,7 @@ urlpatterns = [
|
|||
re_path(r'^list/(?P<list_id>\d+)(.json)?/?$',
|
||||
views.List.as_view(), name='list'),
|
||||
re_path(r'^list/(?P<list_id>\d+)/add/?$',
|
||||
views.List.as_view(), name='list-add-book'),
|
||||
views.list.add_book, name='list-add-book'),
|
||||
|
||||
# preferences
|
||||
re_path(r'^preferences/profile/?$', views.EditUser.as_view()),
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
''' book list views'''
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db.models import Q
|
||||
from django.http import HttpResponseNotFound
|
||||
from django.http import HttpResponseNotFound, HttpResponseBadRequest
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
from django.views.decorators.http import require_POST
|
||||
|
||||
from bookwyrm import forms, models
|
||||
from bookwyrm.activitypub import ActivitypubResponse
|
||||
|
@ -35,7 +36,6 @@ class Lists(View):
|
|||
''' create a book_list '''
|
||||
form = forms.ListForm(request.POST)
|
||||
if not form.is_valid():
|
||||
print(form.errors)
|
||||
return redirect('lists')
|
||||
book_list = form.save()
|
||||
return redirect(book_list.local_path)
|
||||
|
@ -52,14 +52,15 @@ class List(View):
|
|||
if is_api_request(request):
|
||||
return ActivitypubResponse(book_list.to_activity())
|
||||
|
||||
suggestions = request.user.shelfbook_set.all().filter(
|
||||
~Q(book__in=book_list.books)
|
||||
suggestions = request.user.shelfbook_set.filter(
|
||||
~Q(book__in=book_list.books.all())
|
||||
)
|
||||
|
||||
data = {
|
||||
'title': '%s | Lists' % book_list.name,
|
||||
'list': book_list,
|
||||
'suggested_books': [s.book for s in suggestions[:5]],
|
||||
'list_form': forms.ListForm(instance=book_list),
|
||||
}
|
||||
return TemplateResponse(request, 'lists/list.html', data)
|
||||
|
||||
|
@ -69,4 +70,39 @@ class List(View):
|
|||
def post(self, request, list_id):
|
||||
''' edit a book_list '''
|
||||
book_list = get_object_or_404(models.List, id=list_id)
|
||||
form = forms.ListForm(request.POST, instance=book_list)
|
||||
if not form.is_valid():
|
||||
return redirect('list', book_list.id)
|
||||
book_list = form.save()
|
||||
return redirect(book_list.local_path)
|
||||
|
||||
|
||||
@require_POST
|
||||
def add_book(request, list_id):
|
||||
''' put a book on a list '''
|
||||
book_list = get_object_or_404(models.List, id=list_id)
|
||||
if not object_visible_to_user(request.user, book_list):
|
||||
return HttpResponseNotFound()
|
||||
|
||||
book = get_object_or_404(models.Edition, id=request.POST.get('book'))
|
||||
# do you have permission to add to the list?
|
||||
if request.user == book_list.user or book_list.curation == 'open':
|
||||
# go ahead and add it
|
||||
models.ListItem.objects.create(
|
||||
book=book,
|
||||
book_list=book_list,
|
||||
added_by=request.user,
|
||||
)
|
||||
elif book_list.curation == 'curated':
|
||||
# make a pending entry
|
||||
models.ListItem.objects.create(
|
||||
approved=False,
|
||||
book=book,
|
||||
book_list=book_list,
|
||||
added_by=request.user,
|
||||
)
|
||||
else:
|
||||
# you can't add to this list, what were you THINKING
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
return redirect('list', list_id)
|
||||
|
|
Loading…
Reference in a new issue