diff --git a/bookwyrm/activitystreams.py b/bookwyrm/activitystreams.py index ff3c55fbd..a49a7ce4d 100644 --- a/bookwyrm/activitystreams.py +++ b/bookwyrm/activitystreams.py @@ -201,6 +201,19 @@ def add_status_on_create(sender, instance, created, *args, **kwargs): for stream in streams.values(): stream.add_status(instance) + if sender != models.Boost: + return + # remove the original post and other, earlier boosts + boosted = instance.boost.boosted_status + old_versions = models.Boost.objects.filter( + boosted_status__id=boosted.id, + created_date__lt=instance.created_date, + ) + for stream in streams.values(): + stream.remove_object_from_related_stores(boosted) + for status in old_versions: + stream.remove_object_from_related_stores(status) + @receiver(signals.post_delete, sender=models.Boost) # pylint: disable=unused-argument @@ -208,7 +221,10 @@ def remove_boost_on_delete(sender, instance, *args, **kwargs): """boosts are deleted""" # we're only interested in new statuses for stream in streams.values(): + # remove the boost stream.remove_object_from_related_stores(instance) + # re-add the original status + stream.add_status(instance.boosted_status) @receiver(signals.post_save, sender=models.UserFollows) diff --git a/bookwyrm/connectors/abstract_connector.py b/bookwyrm/connectors/abstract_connector.py index 606678150..22489af29 100644 --- a/bookwyrm/connectors/abstract_connector.py +++ b/bookwyrm/connectors/abstract_connector.py @@ -37,7 +37,7 @@ class AbstractMinimalConnector(ABC): for field in self_fields: setattr(self, field, getattr(info, field)) - def search(self, query, min_confidence=None): + def search(self, query, min_confidence=None, timeout=5): """free text search""" params = {} if min_confidence: @@ -46,6 +46,7 @@ class AbstractMinimalConnector(ABC): data = self.get_search_data( "%s%s" % (self.search_url, query), params=params, + timeout=timeout, ) results = [] @@ -218,7 +219,7 @@ def dict_from_mappings(data, mappings): return result -def get_data(url, params=None): +def get_data(url, params=None, timeout=10): """wrapper for request.get""" # check if the url is blocked if models.FederatedServer.is_blocked(url): @@ -234,6 +235,7 @@ def get_data(url, params=None): "Accept": "application/json; charset=utf-8", "User-Agent": settings.USER_AGENT, }, + timeout=timeout, ) except (RequestError, SSLError, ConnectionError) as e: logger.exception(e) @@ -250,7 +252,7 @@ def get_data(url, params=None): return data -def get_image(url): +def get_image(url, timeout=10): """wrapper for requesting an image""" try: resp = requests.get( @@ -258,6 +260,7 @@ def get_image(url): headers={ "User-Agent": settings.USER_AGENT, }, + timeout=timeout, ) except (RequestError, SSLError) as e: logger.exception(e) diff --git a/bookwyrm/connectors/connector_manager.py b/bookwyrm/connectors/connector_manager.py index 95c5959df..040e7fa5b 100644 --- a/bookwyrm/connectors/connector_manager.py +++ b/bookwyrm/connectors/connector_manager.py @@ -1,4 +1,5 @@ """ interface with whatever connectors the app has """ +from datetime import datetime import importlib import logging import re @@ -29,9 +30,11 @@ def search(query, min_confidence=0.1, return_first=False): isbn = re.sub(r"[\W_]", "", query) maybe_isbn = len(isbn) in [10, 13] # ISBN10 or ISBN13 + timeout = 15 + start_time = datetime.now() for connector in get_connectors(): result_set = None - if maybe_isbn and connector.isbn_search_url and connector.isbn_search_url == "": + if maybe_isbn and connector.isbn_search_url and connector.isbn_search_url != "": # Search on ISBN try: result_set = connector.isbn_search(isbn) @@ -59,6 +62,8 @@ def search(query, min_confidence=0.1, return_first=False): "results": result_set, } ) + if (datetime.now() - start_time).seconds >= timeout: + break if return_first: return None diff --git a/bookwyrm/connectors/self_connector.py b/bookwyrm/connectors/self_connector.py index a8f858345..5223390d8 100644 --- a/bookwyrm/connectors/self_connector.py +++ b/bookwyrm/connectors/self_connector.py @@ -3,7 +3,7 @@ from functools import reduce import operator from django.contrib.postgres.search import SearchRank, SearchVector -from django.db.models import Count, OuterRef, Subquery, F, Q +from django.db.models import OuterRef, Subquery, F, Q from bookwyrm import models from .abstract_connector import AbstractConnector, SearchResult @@ -122,6 +122,8 @@ def search_identifiers(query, *filters): results = models.Edition.objects.filter( *filters, reduce(operator.or_, (Q(**f) for f in or_filters)) ).distinct() + if results.count() <= 1: + return results # when there are multiple editions of the same work, pick the default. # it would be odd for this to happen. @@ -146,19 +148,15 @@ def search_title_author(query, min_confidence, *filters): ) results = ( - models.Edition.objects.annotate(search=vector) - .annotate(rank=SearchRank(vector, query)) + models.Edition.objects.annotate(rank=SearchRank(vector, query)) .filter(*filters, rank__gt=min_confidence) .order_by("-rank") ) # when there are multiple editions of the same work, pick the closest - editions_of_work = ( - results.values("parent_work") - .annotate(Count("parent_work")) - .values_list("parent_work") - ) + editions_of_work = results.values("parent_work__id").values_list("parent_work__id") + # filter out multiple editions of the same work for work_id in set(editions_of_work): editions = results.filter(parent_work=work_id) default = editions.order_by("-edition_rank").first() diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index caa22fcd3..379323b90 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -1,5 +1,6 @@ """ activitypub-aware django model fields """ from dataclasses import MISSING +import imghdr import re from uuid import uuid4 @@ -9,7 +10,7 @@ from django.contrib.postgres.fields import ArrayField as DjangoArrayField from django.core.exceptions import ValidationError from django.core.files.base import ContentFile from django.db import models -from django.forms import ClearableFileInput, ImageField +from django.forms import ClearableFileInput, ImageField as DjangoImageField from django.utils import timezone from django.utils.translation import gettext_lazy as _ from bookwyrm import activitypub @@ -334,10 +335,14 @@ class TagField(ManyToManyField): class ClearableFileInputWithWarning(ClearableFileInput): + """max file size warning""" + template_name = "widgets/clearable_file_input_with_warning.html" -class CustomImageField(ImageField): +class CustomImageField(DjangoImageField): + """overwrites image field for form""" + widget = ClearableFileInputWithWarning @@ -400,11 +405,12 @@ class ImageField(ActivitypubFieldMixin, models.ImageField): if not response: return None - image_name = str(uuid4()) + "." + url.split(".")[-1] image_content = ContentFile(response.content) + image_name = str(uuid4()) + "." + imghdr.what(None, image_content.read()) return [image_name, image_content] def formfield(self, **kwargs): + """special case for forms""" return super().formfield( **{ "form_class": CustomImageField, diff --git a/bookwyrm/templates/author/author.html b/bookwyrm/templates/author/author.html index f4f308f2d..0bc427758 100644 --- a/bookwyrm/templates/author/author.html +++ b/bookwyrm/templates/author/author.html @@ -15,7 +15,7 @@
- {% trans "Edit Author" %} + {% trans "Edit Author" %}
{% endif %} diff --git a/bookwyrm/templates/book/book.html b/bookwyrm/templates/book/book.html index a48120e94..62f09aa03 100644 --- a/bookwyrm/templates/book/book.html +++ b/bookwyrm/templates/book/book.html @@ -8,27 +8,36 @@
-

- - {{ book.title }}{% if book.subtitle %}: - {{ book.subtitle }} - {% endif %} - - - {% if book.series %} - - - - - ({{ book.series }}{% if book.series_number %} #{{ book.series_number }}{% endif %}) - -
- {% endif %} +

+ {{ book.title }}

+ + {% if book.subtitle or book.series %} +

+ {% if book.subtitle %} + + + + {{ book.subtitle }} + + {% endif %} + + {% if book.series %} + + + + ({{ book.series }}{% if book.series_number %} #{{ book.series_number }}{% endif %}) + {% endif %} +

+ {% endif %} + {% if book.authors %} -

- {% trans "by" %} {% include 'snippets/authors.html' with book=book %} -

+
+ {% trans "by" %} {% include 'snippets/authors.html' with book=book %} +
{% endif %}
@@ -36,7 +45,7 @@ {% endif %} @@ -84,7 +93,7 @@
-

+

{% with full=book|book_description itemprop='abstract' %} {% include 'snippets/trimmed_text.html' %} @@ -180,7 +189,7 @@

{% trans "You don't have any reading activity for this book." %}

{% endif %} {% for readthrough in readthroughs %} - {% include 'snippets/readthrough.html' with readthrough=readthrough %} + {% include 'book/readthrough.html' with readthrough=readthrough %} {% endfor %} diff --git a/bookwyrm/templates/book/editions.html b/bookwyrm/templates/book/editions.html index 0d5c10447..e2a0bdda5 100644 --- a/bookwyrm/templates/book/editions.html +++ b/bookwyrm/templates/book/editions.html @@ -40,7 +40,7 @@
- {% include 'snippets/shelve_button/shelve_button.html' with book=book switch_mode=True %} + {% include 'snippets/shelve_button/shelve_button.html' with book=book switch_mode=True right=True %}
{% endfor %} diff --git a/bookwyrm/templates/snippets/readthrough.html b/bookwyrm/templates/book/readthrough.html similarity index 97% rename from bookwyrm/templates/snippets/readthrough.html rename to bookwyrm/templates/book/readthrough.html index d5e79b864..751407461 100644 --- a/bookwyrm/templates/snippets/readthrough.html +++ b/bookwyrm/templates/book/readthrough.html @@ -1,8 +1,8 @@ {% load i18n %} {% load humanize %} {% load tz %} -
-
+
+
{% trans "Progress Updates:" %} diff --git a/bookwyrm/templates/directory/directory.html b/bookwyrm/templates/directory/directory.html index 88a7b15c3..b900cb188 100644 --- a/bookwyrm/templates/directory/directory.html +++ b/bookwyrm/templates/directory/directory.html @@ -20,8 +20,8 @@ {% csrf_token %}

- {% url 'settings-profile' as path %} - {% blocktrans %}You can opt-out at any time in your profile settings.{% endblocktrans %} + {% url 'prefs-profile' as path %} + {% blocktrans with path=path %}You can opt-out at any time in your profile settings.{% endblocktrans %}

diff --git a/bookwyrm/templates/notifications.html b/bookwyrm/templates/notifications.html index a51b2fdb0..ae5cd67b1 100644 --- a/bookwyrm/templates/notifications.html +++ b/bookwyrm/templates/notifications.html @@ -56,7 +56,7 @@ {% endif %}
-
+

{# DESCRIPTION #} @@ -137,7 +137,7 @@ {# PREVIEW #}

-
+
{% include 'snippets/status_preview.html' with status=related_status %}
diff --git a/bookwyrm/templates/settings/federation.html b/bookwyrm/templates/settings/federation.html index 208ecf8d2..e0a9ba73a 100644 --- a/bookwyrm/templates/settings/federation.html +++ b/bookwyrm/templates/settings/federation.html @@ -7,7 +7,7 @@ {% block edit-button %} - {% trans "Add instance" %} + {% trans "Add instance" %} {% endblock %} diff --git a/bookwyrm/templates/snippets/form_rate_stars.html b/bookwyrm/templates/snippets/form_rate_stars.html index a53733df1..e1e36477a 100644 --- a/bookwyrm/templates/snippets/form_rate_stars.html +++ b/bookwyrm/templates/snippets/form_rate_stars.html @@ -11,7 +11,7 @@ class="is-sr-only" type="radio" name="rating" - value="0" + value="" {% if default_rating == 0 or not default_rating %}checked{% endif %} > diff --git a/bookwyrm/templates/user/user.html b/bookwyrm/templates/user/user.html index d2e9be3e2..2c9d7277a 100644 --- a/bookwyrm/templates/user/user.html +++ b/bookwyrm/templates/user/user.html @@ -13,7 +13,7 @@ {% endif %} @@ -26,7 +26,7 @@

{% include 'user/shelf/books_header.html' %}

-
+
{% for shelf in shelves %}

{{ shelf.name }} @@ -60,7 +60,7 @@

diff --git a/bookwyrm/templatetags/utilities.py b/bookwyrm/templatetags/utilities.py index febcc39d9..23b6685d6 100644 --- a/bookwyrm/templatetags/utilities.py +++ b/bookwyrm/templatetags/utilities.py @@ -2,6 +2,7 @@ import os from uuid import uuid4 from django import template +from django.utils.translation import gettext_lazy as _ register = template.Library() @@ -20,13 +21,16 @@ def get_user_identifier(user): @register.filter(name="book_title") -def get_title(book): +def get_title(book, too_short=5): """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) + if len(title) <= too_short and book.subtitle: + title = _("%(title)s: %(subtitle)s") % { + "title": title, + "subtitle": book.subtitle, + } return title diff --git a/bookwyrm/tests/models/test_status_model.py b/bookwyrm/tests/models/test_status_model.py index 4c8930bc4..355caab9b 100644 --- a/bookwyrm/tests/models/test_status_model.py +++ b/bookwyrm/tests/models/test_status_model.py @@ -16,6 +16,7 @@ from bookwyrm import activitypub, models, settings # pylint: disable=too-many-public-methods @patch("bookwyrm.models.Status.broadcast") @patch("bookwyrm.activitystreams.ActivityStream.add_status") +@patch("bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores") class Status(TestCase): """lotta types of statuses""" @@ -384,7 +385,8 @@ class Status(TestCase): user=self.local_user, notification_type="GLORB" ) - def test_create_broadcast(self, _, broadcast_mock): + # pylint: disable=unused-argument + def test_create_broadcast(self, one, two, broadcast_mock, *_): """should send out two verions of a status on create""" models.Comment.objects.create( content="hi", user=self.local_user, book=self.book diff --git a/bookwyrm/tests/test_templatetags.py b/bookwyrm/tests/test_templatetags.py index 4b1aa5946..eccfb2987 100644 --- a/bookwyrm/tests/test_templatetags.py +++ b/bookwyrm/tests/test_templatetags.py @@ -16,6 +16,7 @@ from bookwyrm.templatetags import ( @patch("bookwyrm.activitystreams.ActivityStream.add_status") +@patch("bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores") class TemplateTags(TestCase): """lotta different things here""" @@ -38,28 +39,28 @@ class TemplateTags(TestCase): ) self.book = models.Edition.objects.create(title="Test Book") - def test_get_user_rating(self, _): + def test_get_user_rating(self, *_): """get a user's most recent rating of a book""" with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): models.Review.objects.create(user=self.user, book=self.book, rating=3) self.assertEqual(bookwyrm_tags.get_user_rating(self.book, self.user), 3) - def test_get_user_rating_doesnt_exist(self, _): + def test_get_user_rating_doesnt_exist(self, *_): """there is no rating available""" self.assertEqual(bookwyrm_tags.get_user_rating(self.book, self.user), 0) - def test_get_user_identifer_local(self, _): + def test_get_user_identifer_local(self, *_): """fall back to the simplest uid available""" self.assertNotEqual(self.user.username, self.user.localname) self.assertEqual(utilities.get_user_identifier(self.user), "mouse") - def test_get_user_identifer_remote(self, _): + def test_get_user_identifer_remote(self, *_): """for a remote user, should be their full username""" self.assertEqual( utilities.get_user_identifier(self.remote_user), "rat@example.com" ) - def test_get_replies(self, _): + def test_get_replies(self, *_): """direct replies to a status""" with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): parent = models.Review.objects.create( @@ -87,7 +88,7 @@ class TemplateTags(TestCase): self.assertTrue(second_child in replies) self.assertFalse(third_child in replies) - def test_get_parent(self, _): + def test_get_parent(self, *_): """get the reply parent of a status""" with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): parent = models.Review.objects.create( @@ -101,7 +102,7 @@ class TemplateTags(TestCase): self.assertEqual(result, parent) self.assertIsInstance(result, models.Review) - def test_get_user_liked(self, _): + def test_get_user_liked(self, *_): """did a user like a status""" status = models.Review.objects.create(user=self.remote_user, book=self.book) @@ -110,7 +111,7 @@ class TemplateTags(TestCase): models.Favorite.objects.create(user=self.user, status=status) self.assertTrue(interaction.get_user_liked(self.user, status)) - def test_get_user_boosted(self, _): + def test_get_user_boosted(self, *_): """did a user boost a status""" status = models.Review.objects.create(user=self.remote_user, book=self.book) @@ -119,7 +120,7 @@ class TemplateTags(TestCase): models.Boost.objects.create(user=self.user, boosted_status=status) self.assertTrue(interaction.get_user_boosted(self.user, status)) - def test_get_boosted(self, _): + def test_get_boosted(self, *_): """load a boosted status""" with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): status = models.Review.objects.create(user=self.remote_user, book=self.book) @@ -128,7 +129,7 @@ class TemplateTags(TestCase): self.assertIsInstance(boosted, models.Review) self.assertEqual(boosted, status) - def test_get_book_description(self, _): + def test_get_book_description(self, *_): """grab it from the edition or the parent""" work = models.Work.objects.create(title="Test Work") self.book.parent_work = work @@ -144,12 +145,12 @@ class TemplateTags(TestCase): self.book.save() self.assertEqual(bookwyrm_tags.get_book_description(self.book), "hello") - def test_get_uuid(self, _): + def test_get_uuid(self, *_): """uuid functionality""" uuid = utilities.get_uuid("hi") self.assertTrue(re.match(r"hi[A-Za-z0-9\-]", uuid)) - def test_get_markdown(self, _): + def test_get_markdown(self, *_): """mardown format data""" result = markdown.get_markdown("_hi_") self.assertEqual(result, "

hi

") @@ -157,13 +158,13 @@ class TemplateTags(TestCase): result = markdown.get_markdown("_hi_") self.assertEqual(result, "

hi

") - def test_get_mentions(self, _): + def test_get_mentions(self, *_): """list of people mentioned""" status = models.Status.objects.create(content="hi", user=self.remote_user) result = status_display.get_mentions(status, self.user) self.assertEqual(result, "@rat@example.com ") - def test_related_status(self, _): + def test_related_status(self, *_): """gets the subclass model for a notification status""" with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): status = models.Status.objects.create(content="hi", user=self.user) diff --git a/bookwyrm/tests/views/inbox/test_inbox_announce.py b/bookwyrm/tests/views/inbox/test_inbox_announce.py index 4243dc78e..fdb84d7c5 100644 --- a/bookwyrm/tests/views/inbox/test_inbox_announce.py +++ b/bookwyrm/tests/views/inbox/test_inbox_announce.py @@ -51,7 +51,8 @@ class InboxActivities(TestCase): models.SiteSettings.objects.create() @patch("bookwyrm.activitystreams.ActivityStream.add_status") - def test_boost(self, redis_mock): + @patch("bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores") + def test_boost(self, redis_mock, _): """boost a status""" self.assertEqual(models.Notification.objects.count(), 0) activity = { @@ -81,7 +82,8 @@ class InboxActivities(TestCase): @responses.activate @patch("bookwyrm.activitystreams.ActivityStream.add_status") - def test_boost_remote_status(self, redis_mock): + @patch("bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores") + 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( @@ -153,12 +155,13 @@ class InboxActivities(TestCase): views.inbox.activity_task(activity) self.assertEqual(models.Boost.objects.count(), 0) - def test_unboost(self): + @patch("bookwyrm.activitystreams.ActivityStream.add_status") + @patch("bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores") + def test_unboost(self, *_): """undo a boost""" - with patch("bookwyrm.activitystreams.ActivityStream.add_status"): - boost = models.Boost.objects.create( - boosted_status=self.status, user=self.remote_user - ) + boost = models.Boost.objects.create( + boosted_status=self.status, user=self.remote_user + ) activity = { "type": "Undo", "actor": "hi", @@ -175,11 +178,7 @@ class InboxActivities(TestCase): "published": "Mon, 25 May 2020 19:31:20 GMT", }, } - with patch( - "bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores" - ) as redis_mock: - views.inbox.activity_task(activity) - self.assertTrue(redis_mock.called) + views.inbox.activity_task(activity) self.assertFalse(models.Boost.objects.exists()) def test_unboost_unknown_boost(self): diff --git a/bookwyrm/tests/views/test_interaction.py b/bookwyrm/tests/views/test_interaction.py index 876d6053c..3867f57d5 100644 --- a/bookwyrm/tests/views/test_interaction.py +++ b/bookwyrm/tests/views/test_interaction.py @@ -8,6 +8,7 @@ from bookwyrm import models, views @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores") class InteractionViews(TestCase): """viewing and creating statuses""" @@ -40,7 +41,7 @@ class InteractionViews(TestCase): parent_work=work, ) - def test_favorite(self, _): + def test_favorite(self, *_): """create and broadcast faving a status""" view = views.Favorite.as_view() request = self.factory.post("") @@ -58,7 +59,7 @@ class InteractionViews(TestCase): self.assertEqual(notification.user, self.local_user) self.assertEqual(notification.related_user, self.remote_user) - def test_unfavorite(self, _): + def test_unfavorite(self, *_): """unfav a status""" view = views.Unfavorite.as_view() request = self.factory.post("") @@ -75,7 +76,7 @@ class InteractionViews(TestCase): self.assertEqual(models.Favorite.objects.count(), 0) self.assertEqual(models.Notification.objects.count(), 0) - def test_boost(self, _): + def test_boost(self, *_): """boost a status""" view = views.Boost.as_view() request = self.factory.post("") @@ -97,7 +98,7 @@ class InteractionViews(TestCase): self.assertEqual(notification.related_user, self.remote_user) self.assertEqual(notification.related_status, status) - def test_self_boost(self, _): + def test_self_boost(self, *_): """boost your own status""" view = views.Boost.as_view() request = self.factory.post("") @@ -121,7 +122,7 @@ class InteractionViews(TestCase): self.assertFalse(models.Notification.objects.exists()) - def test_boost_unlisted(self, _): + def test_boost_unlisted(self, *_): """boost a status""" view = views.Boost.as_view() request = self.factory.post("") @@ -136,7 +137,7 @@ class InteractionViews(TestCase): boost = models.Boost.objects.get() self.assertEqual(boost.privacy, "unlisted") - def test_boost_private(self, _): + def test_boost_private(self, *_): """boost a status""" view = views.Boost.as_view() request = self.factory.post("") @@ -149,7 +150,7 @@ class InteractionViews(TestCase): view(request, status.id) self.assertFalse(models.Boost.objects.exists()) - def test_boost_twice(self, _): + def test_boost_twice(self, *_): """boost a status""" view = views.Boost.as_view() request = self.factory.post("") @@ -161,13 +162,17 @@ class InteractionViews(TestCase): view(request, status.id) self.assertEqual(models.Boost.objects.count(), 1) - def test_unboost(self, _): + @patch("bookwyrm.activitystreams.ActivityStream.add_status") + def test_unboost(self, *_): """undo a boost""" view = views.Unboost.as_view() request = self.factory.post("") request.user = self.remote_user - with patch("bookwyrm.activitystreams.ActivityStream.add_status"): - status = models.Status.objects.create(user=self.local_user, content="hi") + status = models.Status.objects.create(user=self.local_user, content="hi") + + with patch( + "bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores" + ): views.Boost.as_view()(request, status.id) self.assertEqual(models.Boost.objects.count(), 1) diff --git a/bookwyrm/views/invite.py b/bookwyrm/views/invite.py index 92f930f45..3b9fd17c5 100644 --- a/bookwyrm/views/invite.py +++ b/bookwyrm/views/invite.py @@ -37,8 +37,12 @@ class ManageInvites(View): PAGE_LENGTH, ) + page = paginated.get_page(request.GET.get("page")) data = { - "invites": paginated.get_page(request.GET.get("page")), + "invites": page, + "page_range": paginated.get_elided_page_range( + page.number, on_each_side=2, on_ends=1 + ), "form": forms.CreateInviteForm(), } return TemplateResponse(request, "settings/manage_invites.html", data) @@ -118,15 +122,16 @@ class ManageInviteRequests(View): reduce(operator.or_, (Q(**f) for f in filters)) ).distinct() - paginated = Paginator( - requests, - PAGE_LENGTH, - ) + paginated = Paginator(requests, PAGE_LENGTH) + page = paginated.get_page(request.GET.get("page")) data = { "ignored": ignored, "count": paginated.count, - "requests": paginated.get_page(request.GET.get("page")), + "requests": page, + "page_range": paginated.get_elided_page_range( + page.number, on_each_side=2, on_ends=1 + ), "sort": sort, } return TemplateResponse(request, "settings/manage_invite_requests.html", data) diff --git a/locale/fr_FR/LC_MESSAGES/django.mo b/locale/fr_FR/LC_MESSAGES/django.mo index fa138f785..3d622ac66 100644 Binary files a/locale/fr_FR/LC_MESSAGES/django.mo and b/locale/fr_FR/LC_MESSAGES/django.mo differ diff --git a/locale/fr_FR/LC_MESSAGES/django.po b/locale/fr_FR/LC_MESSAGES/django.po index 53b4a54af..d29afc1cf 100644 --- a/locale/fr_FR/LC_MESSAGES/django.po +++ b/locale/fr_FR/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 0.1.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-06-07 14:09+0000\n" +"POT-Creation-Date: 2021-06-09 16:46+0000\n" "PO-Revision-Date: 2021-04-05 12:44+0100\n" "Last-Translator: Fabien Basmaison \n" "Language-Team: Mouse Reeve \n" @@ -91,23 +91,23 @@ msgstr "nom du compte :" msgid "A user with that username already exists." msgstr "Ce nom est déjà associé à un compte." -#: bookwyrm/settings.py:160 +#: bookwyrm/settings.py:156 msgid "English" msgstr "English" -#: bookwyrm/settings.py:161 +#: bookwyrm/settings.py:157 msgid "German" msgstr "Deutsch" -#: bookwyrm/settings.py:162 +#: bookwyrm/settings.py:158 msgid "Spanish" msgstr "Español" -#: bookwyrm/settings.py:163 +#: bookwyrm/settings.py:159 msgid "French" msgstr "Français" -#: bookwyrm/settings.py:164 +#: bookwyrm/settings.py:160 msgid "Simplified Chinese" msgstr "简化字" @@ -154,12 +154,12 @@ msgid "Wikipedia" msgstr "Wikipedia" #: bookwyrm/templates/author/author.html:69 -#: bookwyrm/templates/book/book.html:78 +#: bookwyrm/templates/book/book.html:87 msgid "View on OpenLibrary" msgstr "Voir sur OpenLibrary" #: bookwyrm/templates/author/author.html:77 -#: bookwyrm/templates/book/book.html:81 +#: bookwyrm/templates/book/book.html:90 msgid "View on Inventaire" msgstr "Voir sur Inventaire" @@ -252,7 +252,7 @@ msgid "Goodreads key:" msgstr "Clé Goodreads :" #: bookwyrm/templates/author/edit_author.html:116 -#: bookwyrm/templates/book/book.html:124 +#: bookwyrm/templates/book/book.html:133 #: bookwyrm/templates/book/edit_book.html:321 #: bookwyrm/templates/lists/form.html:42 #: bookwyrm/templates/preferences/edit_user.html:70 @@ -269,7 +269,7 @@ msgid "Save" msgstr "Enregistrer" #: bookwyrm/templates/author/edit_author.html:117 -#: bookwyrm/templates/book/book.html:125 bookwyrm/templates/book/book.html:174 +#: bookwyrm/templates/book/book.html:134 bookwyrm/templates/book/book.html:183 #: bookwyrm/templates/book/cover_modal.html:32 #: bookwyrm/templates/book/edit_book.html:322 #: bookwyrm/templates/moderation/report_modal.html:34 @@ -284,98 +284,98 @@ msgstr "Enregistrer" msgid "Cancel" msgstr "Annuler" -#: bookwyrm/templates/book/book.html:31 +#: bookwyrm/templates/book/book.html:40 #: bookwyrm/templates/discover/large-book.html:25 #: bookwyrm/templates/discover/small-book.html:19 msgid "by" msgstr "par" -#: bookwyrm/templates/book/book.html:39 bookwyrm/templates/book/book.html:40 +#: bookwyrm/templates/book/book.html:48 bookwyrm/templates/book/book.html:49 msgid "Edit Book" msgstr "Modifier le livre" -#: bookwyrm/templates/book/book.html:57 +#: bookwyrm/templates/book/book.html:66 #: bookwyrm/templates/book/cover_modal.html:5 msgid "Add cover" msgstr "Ajouter une couverture" -#: bookwyrm/templates/book/book.html:61 +#: bookwyrm/templates/book/book.html:70 msgid "Failed to load cover" msgstr "La couverture n’a pu être chargée" -#: bookwyrm/templates/book/book.html:101 +#: bookwyrm/templates/book/book.html:110 #, python-format msgid "(%(review_count)s review)" msgid_plural "(%(review_count)s reviews)" msgstr[0] "(%(review_count)s critique)" msgstr[1] "(%(review_count)s critiques)" -#: bookwyrm/templates/book/book.html:113 +#: bookwyrm/templates/book/book.html:122 msgid "Add Description" msgstr "Ajouter une description" -#: bookwyrm/templates/book/book.html:120 +#: bookwyrm/templates/book/book.html:129 #: bookwyrm/templates/book/edit_book.html:136 #: bookwyrm/templates/lists/form.html:12 msgid "Description:" msgstr "Description :" -#: bookwyrm/templates/book/book.html:134 +#: bookwyrm/templates/book/book.html:143 #, python-format msgid "%(count)s editions" msgstr "%(count)s éditions" -#: bookwyrm/templates/book/book.html:142 +#: bookwyrm/templates/book/book.html:151 #, python-format msgid "This edition is on your %(shelf_name)s shelf." msgstr "Cette édition est sur votre étagère %(shelf_name)s." -#: bookwyrm/templates/book/book.html:148 +#: bookwyrm/templates/book/book.html:157 #, python-format msgid "A different edition of this book is on your %(shelf_name)s shelf." msgstr "Une édition différente de ce livre existe sur votre étagère %(shelf_name)s." -#: bookwyrm/templates/book/book.html:159 +#: bookwyrm/templates/book/book.html:168 msgid "Your reading activity" msgstr "Votre activité de lecture" -#: bookwyrm/templates/book/book.html:162 +#: bookwyrm/templates/book/book.html:171 msgid "Add read dates" msgstr "Ajouter des dates de lecture" -#: bookwyrm/templates/book/book.html:171 +#: bookwyrm/templates/book/book.html:180 msgid "Create" msgstr "Créer" -#: bookwyrm/templates/book/book.html:181 +#: bookwyrm/templates/book/book.html:190 msgid "You don't have any reading activity for this book." msgstr "Vous n’avez aucune activité de lecture pour ce livre" -#: bookwyrm/templates/book/book.html:200 +#: bookwyrm/templates/book/book.html:209 msgid "Reviews" msgstr "Critiques" -#: bookwyrm/templates/book/book.html:205 +#: bookwyrm/templates/book/book.html:214 msgid "Your reviews" msgstr "Vos critiques" -#: bookwyrm/templates/book/book.html:211 +#: bookwyrm/templates/book/book.html:220 msgid "Your comments" msgstr "Vos commentaires" -#: bookwyrm/templates/book/book.html:217 +#: bookwyrm/templates/book/book.html:226 msgid "Your quotes" msgstr "Vos citations" -#: bookwyrm/templates/book/book.html:253 +#: bookwyrm/templates/book/book.html:262 msgid "Subjects" msgstr "Sujets" -#: bookwyrm/templates/book/book.html:265 +#: bookwyrm/templates/book/book.html:274 msgid "Places" msgstr "Lieux" -#: bookwyrm/templates/book/book.html:276 bookwyrm/templates/layout.html:61 +#: bookwyrm/templates/book/book.html:285 bookwyrm/templates/layout.html:61 #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/search/layout.html:25 #: bookwyrm/templates/search/layout.html:50 @@ -383,11 +383,11 @@ msgstr "Lieux" msgid "Lists" msgstr "Listes" -#: bookwyrm/templates/book/book.html:287 +#: bookwyrm/templates/book/book.html:296 msgid "Add to list" msgstr "Ajouter à la liste" -#: bookwyrm/templates/book/book.html:297 +#: bookwyrm/templates/book/book.html:306 #: bookwyrm/templates/book/cover_modal.html:31 #: bookwyrm/templates/lists/list.html:179 msgid "Add" @@ -2921,6 +2921,11 @@ msgstr "Niveau d’accès :" msgid "File exceeds maximum size: 10MB" msgstr "Ce fichier dépasse la taille limite : 10 Mo" +#: bookwyrm/templatetags/utilities.py:30 +#, python-format +msgid "%(title)s: %(subtitle)s" +msgstr "%(title)s (%(subtitle)s)" + #: bookwyrm/views/import_data.py:67 msgid "Not a valid csv file" msgstr "Fichier CSV non valide"