From fdf5113143fa3ac9fac806f5547e0a03b8c63e3b Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 8 Apr 2021 16:00:57 -0700 Subject: [PATCH 01/47] Removes tag code --- bookwyrm/models/__init__.py | 2 - bookwyrm/models/tag.py | 63 ---------------- bookwyrm/tests/views/test_tag.py | 119 ------------------------------- bookwyrm/urls.py | 5 -- bookwyrm/views/__init__.py | 1 - bookwyrm/views/tag.py | 73 ------------------- 6 files changed, 263 deletions(-) delete mode 100644 bookwyrm/models/tag.py delete mode 100644 bookwyrm/tests/views/test_tag.py delete mode 100644 bookwyrm/views/tag.py diff --git a/bookwyrm/models/__init__.py b/bookwyrm/models/__init__.py index 35e32c2cf..2a25a5251 100644 --- a/bookwyrm/models/__init__.py +++ b/bookwyrm/models/__init__.py @@ -17,8 +17,6 @@ from .favorite import Favorite from .notification import Notification from .readthrough import ReadThrough, ProgressUpdate, ProgressMode -from .tag import Tag, UserTag - from .user import User, KeyPair, AnnualGoal from .relationship import UserFollows, UserFollowRequest, UserBlocks from .report import Report, ReportComment diff --git a/bookwyrm/models/tag.py b/bookwyrm/models/tag.py deleted file mode 100644 index 2c45b8f91..000000000 --- a/bookwyrm/models/tag.py +++ /dev/null @@ -1,63 +0,0 @@ -""" models for storing different kinds of Activities """ -import urllib.parse - -from django.apps import apps -from django.db import models - -from bookwyrm import activitypub -from bookwyrm.settings import DOMAIN -from .activitypub_mixin import CollectionItemMixin, OrderedCollectionMixin -from .base_model import BookWyrmModel -from . import fields - - -class Tag(OrderedCollectionMixin, BookWyrmModel): - """ freeform tags for books """ - - name = fields.CharField(max_length=100, unique=True) - identifier = models.CharField(max_length=100) - - @property - def books(self): - """ count of books associated with this tag """ - edition_model = apps.get_model("bookwyrm.Edition", require_ready=True) - return ( - edition_model.objects.filter(usertag__tag__identifier=self.identifier) - .order_by("-created_date") - .distinct() - ) - - collection_queryset = books - - def get_remote_id(self): - """ tag should use identifier not id in remote_id """ - base_path = "https://%s" % DOMAIN - return "%s/tag/%s" % (base_path, self.identifier) - - def save(self, *args, **kwargs): - """ create a url-safe lookup key for the tag """ - if not self.id: - # add identifiers to new tags - self.identifier = urllib.parse.quote_plus(self.name) - super().save(*args, **kwargs) - - -class UserTag(CollectionItemMixin, BookWyrmModel): - """ an instance of a tag on a book by a user """ - - user = fields.ForeignKey( - "User", on_delete=models.PROTECT, activitypub_field="actor" - ) - book = fields.ForeignKey( - "Edition", on_delete=models.PROTECT, activitypub_field="object" - ) - tag = fields.ForeignKey("Tag", on_delete=models.PROTECT, activitypub_field="target") - - activity_serializer = activitypub.Add - object_field = "book" - collection_field = "tag" - - class Meta: - """ unqiueness constraint """ - - unique_together = ("user", "book", "tag") diff --git a/bookwyrm/tests/views/test_tag.py b/bookwyrm/tests/views/test_tag.py deleted file mode 100644 index 6ad6ab254..000000000 --- a/bookwyrm/tests/views/test_tag.py +++ /dev/null @@ -1,119 +0,0 @@ -""" test for app action functionality """ -from unittest.mock import patch -from django.contrib.auth.models import Group, Permission -from django.contrib.contenttypes.models import ContentType -from django.template.response import TemplateResponse -from django.test import TestCase -from django.test.client import RequestFactory - -from bookwyrm import models, views -from bookwyrm.activitypub import ActivitypubResponse - - -class TagViews(TestCase): - """ tag views""" - - def setUp(self): - """ we need basic test data and mocks """ - self.factory = RequestFactory() - self.local_user = models.User.objects.create_user( - "mouse@local.com", - "mouse@mouse.com", - "mouseword", - local=True, - localname="mouse", - remote_id="https://example.com/users/mouse", - ) - self.group = Group.objects.create(name="editor") - self.group.permissions.add( - Permission.objects.create( - name="edit_book", - codename="edit_book", - content_type=ContentType.objects.get_for_model(models.User), - ).id - ) - self.work = models.Work.objects.create(title="Test Work") - self.book = models.Edition.objects.create( - title="Example Edition", - remote_id="https://example.com/book/1", - parent_work=self.work, - ) - models.SiteSettings.objects.create() - - def test_tag_page(self): - """ there are so many views, this just makes sure it LOADS """ - view = views.Tag.as_view() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): - tag = models.Tag.objects.create(name="hi there") - models.UserTag.objects.create(tag=tag, user=self.local_user, book=self.book) - request = self.factory.get("") - with patch("bookwyrm.views.tag.is_api_request") as is_api: - is_api.return_value = False - result = view(request, tag.identifier) - self.assertIsInstance(result, TemplateResponse) - result.render() - self.assertEqual(result.status_code, 200) - - request = self.factory.get("") - with patch("bookwyrm.views.tag.is_api_request") as is_api: - is_api.return_value = True - result = view(request, tag.identifier) - self.assertIsInstance(result, ActivitypubResponse) - self.assertEqual(result.status_code, 200) - - def test_tag_page_activitypub_page(self): - """ there are so many views, this just makes sure it LOADS """ - view = views.Tag.as_view() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): - tag = models.Tag.objects.create(name="hi there") - models.UserTag.objects.create(tag=tag, user=self.local_user, book=self.book) - request = self.factory.get("", {"page": 1}) - with patch("bookwyrm.views.tag.is_api_request") as is_api: - is_api.return_value = True - result = view(request, tag.identifier) - self.assertIsInstance(result, ActivitypubResponse) - self.assertEqual(result.status_code, 200) - - def test_tag(self): - """ add a tag to a book """ - view = views.AddTag.as_view() - request = self.factory.post( - "", - { - "name": "A Tag!?", - "book": self.book.id, - }, - ) - request.user = self.local_user - - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): - view(request) - - tag = models.Tag.objects.get() - user_tag = models.UserTag.objects.get() - self.assertEqual(tag.name, "A Tag!?") - self.assertEqual(tag.identifier, "A+Tag%21%3F") - self.assertEqual(user_tag.user, self.local_user) - self.assertEqual(user_tag.book, self.book) - - def test_untag(self): - """ remove a tag from a book """ - view = views.RemoveTag.as_view() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): - tag = models.Tag.objects.create(name="A Tag!?") - models.UserTag.objects.create(user=self.local_user, book=self.book, tag=tag) - request = self.factory.post( - "", - { - "user": self.local_user.id, - "book": self.book.id, - "name": tag.name, - }, - ) - request.user = self.local_user - - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): - view(request) - - self.assertTrue(models.Tag.objects.filter(name="A Tag!?").exists()) - self.assertFalse(models.UserTag.objects.exists()) diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index 463988065..0e48ef90c 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -245,11 +245,6 @@ urlpatterns = [ # author re_path(r"^author/(?P\d+)(.json)?/?$", views.Author.as_view()), re_path(r"^author/(?P\d+)/edit/?$", views.EditAuthor.as_view()), - # tags - re_path(r"^tag/(?P.+)\.json/?$", views.Tag.as_view()), - re_path(r"^tag/(?P.+)/?$", views.Tag.as_view()), - re_path(r"^tag/?$", views.AddTag.as_view()), - re_path(r"^untag/?$", views.RemoveTag.as_view()), # reading progress re_path(r"^edit-readthrough/?$", views.edit_readthrough, name="edit-readthrough"), re_path(r"^delete-readthrough/?$", views.delete_readthrough), diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index d053e971b..d73a79ed0 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -32,7 +32,6 @@ from .shelf import create_shelf, delete_shelf from .shelf import shelve, unshelve from .site import Site from .status import CreateStatus, DeleteStatus, DeleteAndRedraft -from .tag import Tag, AddTag, RemoveTag from .updates import get_notification_count, get_unread_status_count from .user import User, EditUser, Followers, Following from .user_admin import UserAdmin diff --git a/bookwyrm/views/tag.py b/bookwyrm/views/tag.py deleted file mode 100644 index a6bdf05a2..000000000 --- a/bookwyrm/views/tag.py +++ /dev/null @@ -1,73 +0,0 @@ -""" tagging views""" -from django.contrib.auth.decorators import login_required -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 bookwyrm import models -from bookwyrm.activitypub import ActivitypubResponse -from .helpers import is_api_request - - -# pylint: disable= no-self-use -class Tag(View): - """ tag page """ - - def get(self, request, tag_id): - """ see books related to a tag """ - tag_obj = get_object_or_404(models.Tag, identifier=tag_id) - - if is_api_request(request): - return ActivitypubResponse(tag_obj.to_activity(**request.GET)) - - books = models.Edition.objects.filter( - usertag__tag__identifier=tag_id - ).distinct() - data = { - "books": books, - "tag": tag_obj, - } - return TemplateResponse(request, "tag.html", data) - - -@method_decorator(login_required, name="dispatch") -class AddTag(View): - """ add a tag to a book """ - - def post(self, request): - """ tag a book """ - # I'm not using a form here because sometimes "name" is sent as a hidden - # field which doesn't validate - name = request.POST.get("name") - book_id = request.POST.get("book") - book = get_object_or_404(models.Edition, id=book_id) - tag_obj, _ = models.Tag.objects.get_or_create( - name=name, - ) - models.UserTag.objects.get_or_create( - user=request.user, - book=book, - tag=tag_obj, - ) - - return redirect("/book/%s" % book_id) - - -@method_decorator(login_required, name="dispatch") -class RemoveTag(View): - """ remove a user's tag from a book """ - - def post(self, request): - """ untag a book """ - name = request.POST.get("name") - tag_obj = get_object_or_404(models.Tag, name=name) - book_id = request.POST.get("book") - book = get_object_or_404(models.Edition, id=book_id) - - user_tag = get_object_or_404( - models.UserTag, tag=tag_obj, book=book, user=request.user - ) - user_tag.delete() - - return redirect("/book/%s" % book_id) From f01b7fbd1516262b1c6eaf9c1cc57f459840ca7f Mon Sep 17 00:00:00 2001 From: Joachim Date: Wed, 21 Apr 2021 18:34:04 +0200 Subject: [PATCH 02/47] Design and a11y fixes on Create Status forms - Use `field` CSS class to better respect Bulma styles - Fix buggy `id` on content field, causing a11y problems - Fix progress control & select styles - Various small style fixes --- .../snippets/create_status_form.html | 58 ++++++++++++------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/bookwyrm/templates/snippets/create_status_form.html b/bookwyrm/templates/snippets/create_status_form.html index 57e36d963..a74230ad1 100644 --- a/bookwyrm/templates/snippets/create_status_form.html +++ b/bookwyrm/templates/snippets/create_status_form.html @@ -6,14 +6,16 @@ {% if type == 'review' %} -
+
- +
+ +
{% endif %} -
+
{% if type != 'reply' and type != 'direct' %} -
+ + {# Supplemental fields #} {% if type == 'quotation' %} -
+
{% include 'snippets/content_warning_field.html' with parent_status=status %} - +
+ +
{% elif type == 'comment' %} -
+
{% active_shelf book as active_shelf %} {% if active_shelf.shelf.identifier == 'reading' and book.latest_readthrough %} @@ -58,11 +69,13 @@
-
- +
+
+ +
{% if readthrough.progress_mode == 'PG' and book.pages %} @@ -73,9 +86,12 @@ {% endif %}
{% endif %} - + + {# bottom bar #} -
+ + +
{% trans "Include spoiler alert" as button_text %} From 544dbda4ada69c08c30b56431fddaea997b44f2b Mon Sep 17 00:00:00 2001 From: Joachim Date: Wed, 21 Apr 2021 19:35:50 +0200 Subject: [PATCH 03/47] Move menu item styles to menu item --- .../templates/snippets/shelf_selector.html | 10 +++---- .../shelve_button/shelve_button_options.html | 28 ++++++++----------- .../snippets/status/status_options.html | 14 +++++----- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/bookwyrm/templates/snippets/shelf_selector.html b/bookwyrm/templates/snippets/shelf_selector.html index 0be4465f1..832a414b7 100644 --- a/bookwyrm/templates/snippets/shelf_selector.html +++ b/bookwyrm/templates/snippets/shelf_selector.html @@ -7,8 +7,8 @@ {% block dropdown-list %} {% for shelf in request.user.shelf_set.all %} -
  • -
  • {% endfor %} - -
  • - {% endif %} diff --git a/bookwyrm/templates/snippets/status/status_options.html b/bookwyrm/templates/snippets/status/status_options.html index a76cbc393..a327b43d2 100644 --- a/bookwyrm/templates/snippets/status/status_options.html +++ b/bookwyrm/templates/snippets/status/status_options.html @@ -11,8 +11,8 @@ {% block dropdown-list %} {% if status.user == request.user %} {# things you can do to your own statuses #} -
  • - {% if status.status_type != 'GeneratedNote' and status.status_type != 'Rating' %} -
  • -
  • {% endfor %} @@ -23,7 +23,7 @@ {% csrf_token %} - + {% endblock %} diff --git a/bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown.html b/bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown.html index c3f325bff..4439bfc28 100644 --- a/bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown.html +++ b/bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown.html @@ -7,5 +7,5 @@ {% endblock %} {% block dropdown-list %} -{% include 'snippets/shelve_button/shelve_button_options.html' with active_shelf=active_shelf shelves=request.user.shelf_set.all dropdown=True class="shelf-option is-fullwidth is-small" %} +{% include 'snippets/shelve_button/shelve_button_options.html' with active_shelf=active_shelf shelves=request.user.shelf_set.all dropdown=True class="shelf-option is-fullwidth is-small is-radiusless is-white" %} {% endblock %} diff --git a/bookwyrm/templates/snippets/status/status_options.html b/bookwyrm/templates/snippets/status/status_options.html index a327b43d2..1dbca910d 100644 --- a/bookwyrm/templates/snippets/status/status_options.html +++ b/bookwyrm/templates/snippets/status/status_options.html @@ -14,7 +14,7 @@
    diff --git a/bookwyrm/templates/tag.html b/bookwyrm/templates/tag.html deleted file mode 100644 index b6fa67783..000000000 --- a/bookwyrm/templates/tag.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends 'layout.html' %} -{% load i18n %} -{% load bookwyrm_tags %} - -{% block title %}{{ tag.name }}{% endblock %} - -{% block content %} -
    -

    {% blocktrans %}Books tagged "{{ tag.name }}"{% endblocktrans %}

    - {% include 'snippets/book_tiles.html' with books=books.all %} -
    -{% endblock %} - - diff --git a/bookwyrm/views/books.py b/bookwyrm/views/books.py index 0c61d1a20..34ebe0b68 100644 --- a/bookwyrm/views/books.py +++ b/bookwyrm/views/books.py @@ -57,12 +57,7 @@ class Book(View): ) reviews_page = paginated.get_page(request.GET.get("page")) - user_tags = readthroughs = user_shelves = other_edition_shelves = [] if request.user.is_authenticated: - user_tags = models.UserTag.objects.filter( - book=book, user=request.user - ).values_list("tag__identifier", flat=True) - readthroughs = models.ReadThrough.objects.filter( user=request.user, book=book, @@ -87,11 +82,9 @@ class Book(View): "review_count": reviews.count(), "ratings": reviews.filter(Q(content__isnull=True) | Q(content="")), "rating": reviews.aggregate(Avg("rating"))["rating__avg"], - "tags": models.UserTag.objects.filter(book=book), "lists": privacy_filter( request.user, book.list_set.filter(listitem__approved=True) ), - "user_tags": user_tags, "user_shelves": user_shelves, "other_edition_shelves": other_edition_shelves, "readthroughs": readthroughs, From f9c40aadd95e30ee852503d9cb975d7eeb7d86ff Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 22 Apr 2021 18:30:58 -0700 Subject: [PATCH 38/47] Python formatting --- .../migrations/0070_auto_20210423_0121.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/bookwyrm/migrations/0070_auto_20210423_0121.py b/bookwyrm/migrations/0070_auto_20210423_0121.py index 90540ae12..0b04c3ca2 100644 --- a/bookwyrm/migrations/0070_auto_20210423_0121.py +++ b/bookwyrm/migrations/0070_auto_20210423_0121.py @@ -6,30 +6,30 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('bookwyrm', '0069_auto_20210422_1604'), + ("bookwyrm", "0069_auto_20210422_1604"), ] operations = [ migrations.AlterUniqueTogether( - name='usertag', + name="usertag", unique_together=None, ), migrations.RemoveField( - model_name='usertag', - name='book', + model_name="usertag", + name="book", ), migrations.RemoveField( - model_name='usertag', - name='tag', + model_name="usertag", + name="tag", ), migrations.RemoveField( - model_name='usertag', - name='user', + model_name="usertag", + name="user", ), migrations.DeleteModel( - name='Tag', + name="Tag", ), migrations.DeleteModel( - name='UserTag', + name="UserTag", ), ] From b457446f2ff69f850e927ae40e4bb025c3d9c260 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 22 Apr 2021 19:36:27 -0700 Subject: [PATCH 39/47] Don't save duplicate boosts --- bookwyrm/models/activitypub_mixin.py | 6 ++---- bookwyrm/models/status.py | 8 ++++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/bookwyrm/models/activitypub_mixin.py b/bookwyrm/models/activitypub_mixin.py index 2e248fbe3..02cfafc0e 100644 --- a/bookwyrm/models/activitypub_mixin.py +++ b/bookwyrm/models/activitypub_mixin.py @@ -204,10 +204,8 @@ class ObjectMixin(ActivitypubMixin): created = created or not bool(self.id) # first off, we want to save normally no matter what super().save(*args, **kwargs) - if ( - not broadcast - or hasattr(self, "status_type") - and self.status_type == "Announce" + if not broadcast or ( + hasattr(self, "status_type") and self.status_type == "Announce" ): return diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py index 360288e93..65bf71d28 100644 --- a/bookwyrm/models/status.py +++ b/bookwyrm/models/status.py @@ -351,6 +351,14 @@ class Boost(ActivityMixin, Status): def save(self, *args, **kwargs): """ save and notify """ + # This constraint can't work as it would cross tables. + # class Meta: + # unique_together = ('user', 'boosted_status') + if Boost.objects.filter( + boosted_status=self.boosted_status, user=self.user + ).exists(): + return + super().save(*args, **kwargs) if not self.boosted_status.user.local or self.boosted_status.user == self.user: return From 32e694032b2f4d24623da0d25db1e7744efc08c8 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 23 Apr 2021 10:49:17 -0700 Subject: [PATCH 40/47] Fixes duplicate boost model verification --- bookwyrm/models/status.py | 2 +- .../tests/views/inbox/test_inbox_announce.py | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py index 65bf71d28..3784680fb 100644 --- a/bookwyrm/models/status.py +++ b/bookwyrm/models/status.py @@ -356,7 +356,7 @@ class Boost(ActivityMixin, Status): # unique_together = ('user', 'boosted_status') if Boost.objects.filter( boosted_status=self.boosted_status, user=self.user - ).exists(): + ).exclude(id=self.id).exists(): return super().save(*args, **kwargs) diff --git a/bookwyrm/tests/views/inbox/test_inbox_announce.py b/bookwyrm/tests/views/inbox/test_inbox_announce.py index 954d4e647..dbe4ec568 100644 --- a/bookwyrm/tests/views/inbox/test_inbox_announce.py +++ b/bookwyrm/tests/views/inbox/test_inbox_announce.py @@ -51,7 +51,7 @@ class InboxActivities(TestCase): models.SiteSettings.objects.create() @patch("bookwyrm.activitystreams.ActivityStream.add_status") - def test_handle_boost(self, _): + def test_boost(self, redis_mock): """ boost a status """ self.assertEqual(models.Notification.objects.count(), 0) activity = { @@ -66,16 +66,23 @@ class InboxActivities(TestCase): with patch("bookwyrm.models.status.Status.ignore_activity") as discarder: discarder.return_value = False views.inbox.activity_task(activity) + + # boost added to redis activitystreams + self.assertTrue(redis_mock.called) + + # boost created of correct status boost = models.Boost.objects.get() self.assertEqual(boost.boosted_status, self.status) + + # notification sent to original poster notification = models.Notification.objects.get() self.assertEqual(notification.user, self.local_user) self.assertEqual(notification.related_status, self.status) @responses.activate @patch("bookwyrm.activitystreams.ActivityStream.add_status") - def test_handle_boost_remote_status(self, redis_mock): - """ boost a status """ + def test_boost_remote_status(self, redis_mock): + """ boost a status from a remote server """ work = models.Work.objects.create(title="work title") book = models.Edition.objects.create( title="Test", @@ -123,7 +130,7 @@ class InboxActivities(TestCase): self.assertEqual(boost.boosted_status.comment.book, book) @responses.activate - def test_handle_discarded_boost(self): + def test_discarded_boost(self): """ test a boost of a mastodon status that will be discarded """ status = models.Status( content="hi", @@ -146,7 +153,7 @@ class InboxActivities(TestCase): views.inbox.activity_task(activity) self.assertEqual(models.Boost.objects.count(), 0) - def test_handle_unboost(self): + def test_unboost(self): """ undo a boost """ with patch("bookwyrm.activitystreams.ActivityStream.add_status"): boost = models.Boost.objects.create( @@ -175,7 +182,7 @@ class InboxActivities(TestCase): self.assertTrue(redis_mock.called) self.assertFalse(models.Boost.objects.exists()) - def test_handle_unboost_unknown_boost(self): + def test_unboost_unknown_boost(self): """ undo a boost """ activity = { "type": "Undo", From 79424f7bfb6b4315c440a95b83ec0ab5eb115f0c Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 23 Apr 2021 10:56:17 -0700 Subject: [PATCH 41/47] Python formatting --- bookwyrm/models/status.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py index 3784680fb..0dee0b756 100644 --- a/bookwyrm/models/status.py +++ b/bookwyrm/models/status.py @@ -354,9 +354,11 @@ class Boost(ActivityMixin, Status): # This constraint can't work as it would cross tables. # class Meta: # unique_together = ('user', 'boosted_status') - if Boost.objects.filter( - boosted_status=self.boosted_status, user=self.user - ).exclude(id=self.id).exists(): + if ( + Boost.objects.filter(boosted_status=self.boosted_status, user=self.user) + .exclude(id=self.id) + .exists() + ): return super().save(*args, **kwargs) From c907b1ff091449b7d79c8c1a26ec594c81c6f32c Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 23 Apr 2021 11:07:19 -0700 Subject: [PATCH 42/47] Test to replicate character encoding bug --- bookwyrm/tests/models/test_status_model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bookwyrm/tests/models/test_status_model.py b/bookwyrm/tests/models/test_status_model.py index 4263c4572..52d6608a2 100644 --- a/bookwyrm/tests/models/test_status_model.py +++ b/bookwyrm/tests/models/test_status_model.py @@ -269,7 +269,7 @@ class Status(TestCase): def test_review_to_pure_activity(self, *_): """ subclass of the base model version with a "pure" serializer """ status = models.Review.objects.create( - name="Review name", + name="Review's name", content="test content", rating=3.0, user=self.local_user, @@ -280,7 +280,7 @@ class Status(TestCase): self.assertEqual(activity["type"], "Article") self.assertEqual( activity["name"], - 'Review of "%s" (3 stars): Review name' % self.book.title, + 'Review of "%s" (3 stars): Review\'s name' % self.book.title, ) self.assertEqual(activity["content"], "test content") self.assertEqual(activity["attachment"][0].type, "Document") From 607e98b33155adddea151f12bd9a938810eeb686 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 23 Apr 2021 11:53:45 -0700 Subject: [PATCH 43/47] Fixes character encoding error --- .../templates/snippets/generated_status/review_pure_name.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bookwyrm/templates/snippets/generated_status/review_pure_name.html b/bookwyrm/templates/snippets/generated_status/review_pure_name.html index 90a6936ed..259601914 100644 --- a/bookwyrm/templates/snippets/generated_status/review_pure_name.html +++ b/bookwyrm/templates/snippets/generated_status/review_pure_name.html @@ -1,10 +1,10 @@ {% load i18n %} {% if rating %} -{% blocktrans with book_title=book.title display_rating=rating|floatformat:"0" review_title=name count counter=rating %}Review of "{{ book_title }}" ({{ display_rating }} star): {{ review_title }}{% plural %}Review of "{{ book_title }}" ({{ display_rating }} stars): {{ review_title }}{% endblocktrans %} +{% blocktrans with book_title=book.title|safe display_rating=rating|floatformat:"0" review_title=name|safe count counter=rating %}Review of "{{ book_title }}" ({{ display_rating }} star): {{ review_title }}{% plural %}Review of "{{ book_title }}" ({{ display_rating }} stars): {{ review_title }}{% endblocktrans %} {% else %} -{% blocktrans with book_title=book.title review_title=name %}Review of "{{ book_title }}": {{ review_title }}{% endblocktrans %} +{% blocktrans with book_title=book.title|safe review_title=name|safe %}Review of "{{ book_title }}": {{ review_title }}{% endblocktrans %} {% endif %} From 9ba0aec6d914137e8b12b3250279f5ae2119763b Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 23 Apr 2021 14:58:48 -0700 Subject: [PATCH 44/47] Show subtitles when titles are very short --- bookwyrm/templates/snippets/book_titleby.html | 5 +++-- bookwyrm/templates/snippets/status/status_header.html | 4 ++-- bookwyrm/templatetags/bookwyrm_tags.py | 8 ++++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/bookwyrm/templates/snippets/book_titleby.html b/bookwyrm/templates/snippets/book_titleby.html index e561a8a33..80127fb72 100644 --- a/bookwyrm/templates/snippets/book_titleby.html +++ b/bookwyrm/templates/snippets/book_titleby.html @@ -1,7 +1,8 @@ {% load i18n %} +{% load bookwyrm_tags %} {% if book.authors %} -{% blocktrans with path=book.local_path title=book.title %}{{ title }} by {% endblocktrans %}{% include 'snippets/authors.html' with book=book %} +{% blocktrans with path=book.local_path title=book|title %}{{ title }} by {% endblocktrans %}{% include 'snippets/authors.html' with book=book %} {% else %} -{{ book.title }} +{{ book|title }} {% endif %} diff --git a/bookwyrm/templates/snippets/status/status_header.html b/bookwyrm/templates/snippets/status/status_header.html index 0fa74ddd1..7a04b44a3 100644 --- a/bookwyrm/templates/snippets/status/status_header.html +++ b/bookwyrm/templates/snippets/status/status_header.html @@ -43,7 +43,7 @@ {% if status.book %} {% if status.status_type == 'GeneratedNote' or status.status_type == 'Rating' %} - {{ status.book.title }}{% if status.status_type == 'Rating' %}: + {{ status.book|title }}{% if status.status_type == 'Rating' %}: {{ status.mention_books.first.title }} + {{ status.mention_books.first|title }} {% endif %} {% if status.progress %} diff --git a/bookwyrm/templatetags/bookwyrm_tags.py b/bookwyrm/templatetags/bookwyrm_tags.py index 649a0dfa1..0180d1e05 100644 --- a/bookwyrm/templatetags/bookwyrm_tags.py +++ b/bookwyrm/templatetags/bookwyrm_tags.py @@ -167,6 +167,14 @@ def get_next_shelf(current_shelf): return "read" return "to-read" +@register.filter(name="title") +def get_title(book): + """ display the subtitle if the title is short """ + title = book.title + if len(title) < 6 and book.subtitle: + title = "{:s}: {:s}".format(title, book.subtitle) + return title + @register.simple_tag(takes_context=False) def related_status(notification): From a49925916373cf9f4b791746ee6f4240ccc5b93f Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 23 Apr 2021 15:01:35 -0700 Subject: [PATCH 45/47] Fixes python formatting --- bookwyrm/templatetags/bookwyrm_tags.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bookwyrm/templatetags/bookwyrm_tags.py b/bookwyrm/templatetags/bookwyrm_tags.py index 0180d1e05..2d95904f0 100644 --- a/bookwyrm/templatetags/bookwyrm_tags.py +++ b/bookwyrm/templatetags/bookwyrm_tags.py @@ -167,6 +167,7 @@ def get_next_shelf(current_shelf): return "read" return "to-read" + @register.filter(name="title") def get_title(book): """ display the subtitle if the title is short """ From 179ba241155592045b8cb9b1674d9c194c2f8a4f Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 23 Apr 2021 15:29:55 -0700 Subject: [PATCH 46/47] Safely handle invalid book --- bookwyrm/templatetags/bookwyrm_tags.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bookwyrm/templatetags/bookwyrm_tags.py b/bookwyrm/templatetags/bookwyrm_tags.py index 2d95904f0..bd14a410f 100644 --- a/bookwyrm/templatetags/bookwyrm_tags.py +++ b/bookwyrm/templatetags/bookwyrm_tags.py @@ -171,6 +171,8 @@ def get_next_shelf(current_shelf): @register.filter(name="title") def get_title(book): """ display the subtitle if the title is short """ + if not book: + return "" title = book.title if len(title) < 6 and book.subtitle: title = "{:s}: {:s}".format(title, book.subtitle) From 32fb06c9e63cb0f46b6e196127b895d7f978f4c2 Mon Sep 17 00:00:00 2001 From: Fabien Basmaison Date: Sat, 24 Apr 2021 13:23:03 +0200 Subject: [PATCH 47/47] Prevent stars from wrapping on multiple lines. --- bookwyrm/static/css/bookwyrm.css | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bookwyrm/static/css/bookwyrm.css b/bookwyrm/static/css/bookwyrm.css index b4abd6907..f29219447 100644 --- a/bookwyrm/static/css/bookwyrm.css +++ b/bookwyrm/static/css/bookwyrm.css @@ -85,6 +85,13 @@ body { } } +/** Stars + ******************************************************************************/ + +.stars { + white-space: nowrap; +} + /** Stars in a review form * * Specificity makes hovering taking over checked inputs.