mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2025-02-18 03:55:19 +00:00
Adds endorsement functionality
This commit is contained in:
parent
5f906e3597
commit
12291c9e50
9 changed files with 86 additions and 18 deletions
|
@ -215,16 +215,23 @@ class AbstractListItem(CollectionItemMixin, BookWyrmModel):
|
|||
activity_serializer = activitypub.ListItem
|
||||
collection_field = "book_list"
|
||||
|
||||
def endorse(self, user):
|
||||
"""another user supports this suggestion"""
|
||||
# you can't endorse your own contribution, silly
|
||||
if user == self.user:
|
||||
return
|
||||
self.endorsement.add(user)
|
||||
|
||||
def unendorse(self, user):
|
||||
"""the user rescinds support this suggestion"""
|
||||
if user == self.user:
|
||||
return
|
||||
self.endorsement.remove(user)
|
||||
|
||||
def raise_not_deletable(self, viewer):
|
||||
"""the associated user OR the list owner can delete"""
|
||||
if self.book_list.user == viewer:
|
||||
return
|
||||
# group members can delete items in group lists
|
||||
is_group_member = GroupMember.objects.filter(
|
||||
group=self.book_list.group, user=viewer
|
||||
).exists()
|
||||
if is_group_member:
|
||||
return
|
||||
super().raise_not_deletable(viewer)
|
||||
|
||||
class Meta:
|
||||
|
@ -243,6 +250,16 @@ class ListItem(AbstractListItem):
|
|||
approved = models.BooleanField(default=True)
|
||||
order = fields.IntegerField()
|
||||
|
||||
def raise_not_deletable(self, viewer):
|
||||
"""the associated user OR the list owner can delete"""
|
||||
# group members can delete items in group lists
|
||||
is_group_member = GroupMember.objects.filter(
|
||||
group=self.book_list.group, user=viewer
|
||||
).exists()
|
||||
if is_group_member:
|
||||
return
|
||||
super().raise_not_deletable(viewer)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""Update the list's date"""
|
||||
super().save(*args, **kwargs)
|
||||
|
|
|
@ -36,9 +36,7 @@
|
|||
</p>
|
||||
</div>
|
||||
<div class="card-footer-item">
|
||||
<p>
|
||||
TODO :: endorsements
|
||||
</p>
|
||||
{% include "book/suggestion_list/endorsement_button.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
{% load i18n %}
|
||||
<form class="" action="{% url "suggestion-endorse" book.id item.id %}" method="POST">
|
||||
{% csrf_token %}
|
||||
<button class="tag is-info {% if request.user not in item.endorsement.all and request.user != item.user %}is-light{% endif %}" type="submit" {% if request.user == item.user %}disabled{% endif %}>
|
||||
<span class="icon icon-star-empty" aria-label="{% trans 'Endorsements' %}"></span>
|
||||
<span>
|
||||
{{ item.endorsement.count|add:1 }}
|
||||
</span>
|
||||
</button>
|
||||
</form>
|
|
@ -18,8 +18,6 @@
|
|||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
{% with book.suggestion_list.suggestionlistitem_set.all|slice:3 as items %}
|
||||
|
||||
{% if items|length == 0 %}
|
||||
<section class="section has-background-light is-flex is-flex-direction-column is-align-items-center">
|
||||
<div class="column is-full is-centered">
|
||||
|
@ -39,7 +37,6 @@
|
|||
{% include "book/suggestion_list/search.html" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<section class="section is-medium has-background-light">
|
||||
<form name="create-list" method="post" action="{% url 'suggestion-list' book_id=book.id %}#suggestions-section" class="has-text-centered">
|
||||
|
|
|
@ -91,6 +91,11 @@
|
|||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
{% if list.suggests_for %}
|
||||
<div class="card-footer-item">
|
||||
{% include "book/suggestion_list/endorsement_button.html" with book=list.suggests_for %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if list.user == request.user or list.group|is_member:request.user %}
|
||||
<form
|
||||
name="set-position-{{ item.id }}"
|
||||
|
|
|
@ -775,6 +775,11 @@ urlpatterns = [
|
|||
views.book_remove_suggestion,
|
||||
name="book-remove-suggestion",
|
||||
),
|
||||
re_path(
|
||||
rf"{BOOK_PATH}/suggestions/endorse/(?P<item_id>\d+)/?$",
|
||||
views.endorse_suggestion,
|
||||
name="suggestion-endorse",
|
||||
),
|
||||
re_path(
|
||||
r"^author/(?P<author_id>\d+)/update/(?P<connector_identifier>[\w\.]+)/?$",
|
||||
views.update_author_from_remote,
|
||||
|
|
|
@ -107,7 +107,11 @@ from .list.list import (
|
|||
|
||||
# suggestion lists
|
||||
from .suggestion_list import SuggestionList
|
||||
from .suggestion_list import book_add_suggestion, book_remove_suggestion
|
||||
from .suggestion_list import (
|
||||
book_add_suggestion,
|
||||
book_remove_suggestion,
|
||||
endorse_suggestion,
|
||||
)
|
||||
|
||||
# misc views
|
||||
from .author import Author, EditAuthor, update_author_from_remote
|
||||
|
|
|
@ -4,7 +4,7 @@ from uuid import uuid4
|
|||
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.core.paginator import Paginator
|
||||
from django.db.models import Avg, Q
|
||||
from django.db.models import Avg, Q, Count
|
||||
from django.http import Http404
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.template.response import TemplateResponse
|
||||
|
@ -136,6 +136,15 @@ class Book(View):
|
|||
"quotation_count": book.quotation_set.filter(**filters).count(),
|
||||
}
|
||||
if hasattr(book, "suggestion_list"):
|
||||
|
||||
data["items"] = (
|
||||
book.suggestion_list.suggestionlistitem_set.prefetch_related(
|
||||
"user", "book", "book__authors"
|
||||
)
|
||||
.annotate(endorsement_count=Count("endorsement"))
|
||||
.order_by("-endorsement_count")[:3]
|
||||
)
|
||||
|
||||
data["suggested_books"] = get_list_suggestions(
|
||||
book.suggestion_list,
|
||||
request.user,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.paginator import Paginator
|
||||
from django.db import transaction
|
||||
from django.db.models import Count
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.urls import reverse
|
||||
|
@ -30,8 +31,12 @@ class SuggestionList(View):
|
|||
if is_api_request(request):
|
||||
return ActivitypubResponse(book_list.to_activity(**request.GET))
|
||||
|
||||
items = book_list.suggestionlistitem_set.prefetch_related(
|
||||
"user", "book", "book__authors"
|
||||
items = (
|
||||
book_list.suggestionlistitem_set.prefetch_related(
|
||||
"user", "book", "book__authors"
|
||||
)
|
||||
.annotate(endorsement_count=Count("endorsement"))
|
||||
.order_by("-endorsement_count")
|
||||
)
|
||||
|
||||
paginated = Paginator(items, PAGE_LENGTH)
|
||||
|
@ -103,12 +108,30 @@ def book_add_suggestion(request, book_id):
|
|||
|
||||
@require_POST
|
||||
@login_required
|
||||
def book_remove_suggestion(request, _):
|
||||
def book_remove_suggestion(request, book_id):
|
||||
"""remove a book from a suggestion list"""
|
||||
item = get_object_or_404(models.SuggestionListItem, id=request.POST.get("item"))
|
||||
item = get_object_or_404(
|
||||
models.SuggestionListItem,
|
||||
id=request.POST.get("item"),
|
||||
book_list__suggests_for=book_id,
|
||||
)
|
||||
item.raise_not_deletable(request.user)
|
||||
|
||||
with transaction.atomic():
|
||||
item.delete()
|
||||
|
||||
return redirect_to_referer(request)
|
||||
|
||||
|
||||
@require_POST
|
||||
@login_required
|
||||
def endorse_suggestion(request, book_id, item_id):
|
||||
"""endorse a suggestion"""
|
||||
item = get_object_or_404(
|
||||
models.SuggestionListItem, id=item_id, book_list__suggests_for=book_id
|
||||
)
|
||||
if request.user not in item.endorsement.all():
|
||||
item.endorse(request.user)
|
||||
else:
|
||||
item.unendorse(request.user)
|
||||
return redirect_to_referer(request)
|
||||
|
|
Loading…
Reference in a new issue