diff --git a/bookwyrm/activitypub/verbs.py b/bookwyrm/activitypub/verbs.py index 883eb895..c63cd5dc 100644 --- a/bookwyrm/activitypub/verbs.py +++ b/bookwyrm/activitypub/verbs.py @@ -69,7 +69,13 @@ class Undo(Verb): model = None if self.object.type == "Follow": model = apps.get_model("bookwyrm.UserFollows") - obj = self.object.to_model(model=model, save=False, allow_create=False) + obj = self.object.to_model(model=model, save=False, allow_create=False) + if not obj: + # this could be a folloq request not a follow proper + model = apps.get_model("bookwyrm.UserFollowRequest") + obj = self.object.to_model(model=model, save=False, allow_create=False) + else: + obj = self.object.to_model(model=model, save=False, allow_create=False) if not obj: # if we don't have the object, we can't undo it. happens a lot with boosts return diff --git a/bookwyrm/connectors/abstract_connector.py b/bookwyrm/connectors/abstract_connector.py index 4b118d64..00b5c5c9 100644 --- a/bookwyrm/connectors/abstract_connector.py +++ b/bookwyrm/connectors/abstract_connector.py @@ -262,9 +262,10 @@ class SearchResult: title: str key: str - author: str - year: str connector: object + author: str = None + year: str = None + cover: str = None confidence: int = 1 def __repr__(self): diff --git a/bookwyrm/connectors/bookwyrm_connector.py b/bookwyrm/connectors/bookwyrm_connector.py index 742d7e85..f7869d55 100644 --- a/bookwyrm/connectors/bookwyrm_connector.py +++ b/bookwyrm/connectors/bookwyrm_connector.py @@ -24,5 +24,4 @@ class Connector(AbstractMinimalConnector): return data def format_isbn_search_result(self, search_result): - search_result["connector"] = self - return SearchResult(**search_result) + return self.format_search_result(search_result) diff --git a/bookwyrm/connectors/openlibrary.py b/bookwyrm/connectors/openlibrary.py index c83a65d6..9be0266c 100644 --- a/bookwyrm/connectors/openlibrary.py +++ b/bookwyrm/connectors/openlibrary.py @@ -95,10 +95,12 @@ class Connector(AbstractConnector): url = "%s%s" % (self.base_url, author_id) yield self.get_or_create_author(url) - def get_cover_url(self, cover_blob): + def get_cover_url(self, cover_blob, size="L"): """ ask openlibrary for the cover """ + if not cover_blob: + return None cover_id = cover_blob[0] - image_name = "%s-L.jpg" % cover_id + image_name = "%s-%s.jpg" % (cover_id, size) return "%s/b/id/%s" % (self.covers_url, image_name) def parse_search_data(self, data): @@ -108,12 +110,15 @@ class Connector(AbstractConnector): # build the remote id from the openlibrary key key = self.books_url + search_result["key"] author = search_result.get("author_name") or ["Unknown"] + cover_blob = search_result.get("cover_i") + cover = self.get_cover_url([cover_blob], size="M") if cover_blob else None return SearchResult( title=search_result.get("title"), key=key, author=", ".join(author), connector=self, year=search_result.get("first_publish_year"), + cover=cover, ) def parse_isbn_search_data(self, data): diff --git a/bookwyrm/connectors/self_connector.py b/bookwyrm/connectors/self_connector.py index 60acb59b..500ffd74 100644 --- a/bookwyrm/connectors/self_connector.py +++ b/bookwyrm/connectors/self_connector.py @@ -67,20 +67,12 @@ class Connector(AbstractConnector): if search_result.published_date else None, connector=self, + cover="%s%s" % (self.covers_url, search_result.cover), confidence=search_result.rank if hasattr(search_result, "rank") else 1, ) def format_isbn_search_result(self, search_result): - return SearchResult( - title=search_result.title, - key=search_result.remote_id, - author=search_result.author_text, - year=search_result.published_date.year - if search_result.published_date - else None, - connector=self, - confidence=search_result.rank if hasattr(search_result, "rank") else 1, - ) + return self.format_search_result(search_result) def is_work_data(self, data): pass diff --git a/bookwyrm/management/commands/initdb.py b/bookwyrm/management/commands/initdb.py index 6b3f3762..d6101c87 100644 --- a/bookwyrm/management/commands/initdb.py +++ b/bookwyrm/management/commands/initdb.py @@ -76,7 +76,7 @@ def init_connectors(): connector_file="self_connector", base_url="https://%s" % DOMAIN, books_url="https://%s/book" % DOMAIN, - covers_url="https://%s/images/covers" % DOMAIN, + covers_url="https://%s/images/" % DOMAIN, search_url="https://%s/search?q=" % DOMAIN, isbn_search_url="https://%s/isbn/" % DOMAIN, priority=1, @@ -88,7 +88,7 @@ def init_connectors(): connector_file="bookwyrm_connector", base_url="https://bookwyrm.social", books_url="https://bookwyrm.social/book", - covers_url="https://bookwyrm.social/images/covers", + covers_url="https://bookwyrm.social/images/", search_url="https://bookwyrm.social/search?q=", isbn_search_url="https://bookwyrm.social/isbn/", priority=2, diff --git a/bookwyrm/templates/book.html b/bookwyrm/templates/book.html index 16bf1197..ee1ea270 100644 --- a/bookwyrm/templates/book.html +++ b/bookwyrm/templates/book.html @@ -78,13 +78,22 @@

- {% if book.physical_format and not book.pages %} - {{ book.physical_format | title }} - {% elif book.physical_format and book.pages %} - {% blocktrans with format=book.physical_format|title pages=book.pages %}{{ format }}, {{ pages }} pages{% endblocktrans %} - {% elif book.pages %} - {% blocktrans with pages=book.pages %}{{ pages }} pages{% endblocktrans %} - {% endif %} + {% if book.physical_format and not book.pages %} + {{ book.physical_format | title }} + {% elif book.physical_format and book.pages %} + {% blocktrans with format=book.physical_format|title pages=book.pages %}{{ format }}, {{ pages }} pages{% endblocktrans %} + {% elif book.pages %} + {% blocktrans with pages=book.pages %}{{ pages }} pages{% endblocktrans %} + {% endif %} +

+

+ {% if book.published_date and book.publishers %} + {% blocktrans with date=book.published_date|date:'M jS Y' publisher=book.publishers|join:', ' %}Published {{ date }} by {{ publisher }}.{% endblocktrans %} + {% elif book.published_date %} + {% blocktrans with date=book.published_date|date:'M jS Y' %}Published {{ date }}{% endblocktrans %} + {% else %} + {% blocktrans with publisher=book.publishers|join:', ' %}Published by {{ publisher }}.{% endblocktrans %} + {% endif %}

{% if book.openlibrary_key %} diff --git a/bookwyrm/templates/search_results.html b/bookwyrm/templates/search_results.html index 13497df8..6444cc18 100644 --- a/bookwyrm/templates/search_results.html +++ b/bookwyrm/templates/search_results.html @@ -19,7 +19,7 @@ @@ -50,12 +50,7 @@ diff --git a/bookwyrm/templates/snippets/follow_button.html b/bookwyrm/templates/snippets/follow_button.html index 419aa211..3df85a1a 100644 --- a/bookwyrm/templates/snippets/follow_button.html +++ b/bookwyrm/templates/snippets/follow_button.html @@ -1,18 +1,12 @@ {% load i18n %} {% if request.user == user or not request.user.is_authenticated %} -{% elif request.user in user.follower_requests.all %} - -
- {% trans "Follow request already sent." %} -
- {% elif user in request.user.blocks.all %} {% include 'snippets/block_button.html' %} {% else %}
- -
diff --git a/bookwyrm/templates/snippets/search_result_text.html b/bookwyrm/templates/snippets/search_result_text.html index 36009050..059b8e7e 100644 --- a/bookwyrm/templates/snippets/search_result_text.html +++ b/bookwyrm/templates/snippets/search_result_text.html @@ -1,3 +1,34 @@ {% load i18n %} -{% if link %}{{ result.title }}{% else %}{{ result.title }}{% endif %} -{% if result.author %} {% blocktrans with author=result.author %}by {{ author }}{% endblocktrans %}{% endif %}{% if result.year %} ({{ result.year }}){% endif %} +
+
+ {% if result.cover %} + + {% else %} +
+ +
+

{% trans "No cover" %}

+
+
+ {% endif %} +
+ +
+

+ + {{ result.title }} + + {% if result.author %} + {% blocktrans with author=result.author %}by {{ author }}{% endblocktrans %}{% endif %}{% if result.year %} ({{ result.year }}) + {% endif %} +

+ + {% if remote_result %} +
+ {% csrf_token %} + + +
+ {% endif %} +
+
diff --git a/bookwyrm/tests/views/test_follow.py b/bookwyrm/tests/views/test_follow.py index 67ac0f0b..6b4de05d 100644 --- a/bookwyrm/tests/views/test_follow.py +++ b/bookwyrm/tests/views/test_follow.py @@ -1,5 +1,7 @@ """ test for app action functionality """ +import json from unittest.mock import patch + from django.contrib.auth.models import Group, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -117,6 +119,8 @@ class BookViews(TestCase): with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: views.unfollow(request) self.assertEqual(mock.call_count, 1) + activity = json.loads(mock.call_args_list[0][0][1]) + self.assertEqual(activity["type"], "Undo") self.assertEqual(self.remote_user.followers.count(), 0) diff --git a/bookwyrm/tests/views/test_inbox.py b/bookwyrm/tests/views/test_inbox.py index 3e52b475..b681b961 100644 --- a/bookwyrm/tests/views/test_inbox.py +++ b/bookwyrm/tests/views/test_inbox.py @@ -275,6 +275,36 @@ class Inbox(TestCase): follow = models.UserFollows.objects.all() self.assertEqual(list(follow), []) + def test_handle_undo_follow_request(self): + """ the requester cancels a follow request """ + self.local_user.manually_approves_followers = True + self.local_user.save(broadcast=False) + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + request = models.UserFollowRequest.objects.create( + user_subject=self.remote_user, user_object=self.local_user + ) + self.assertTrue(self.local_user.follower_requests.exists()) + + activity = { + "type": "Undo", + "id": "bleh", + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "cc": ["https://example.com/user/mouse/followers"], + "actor": self.remote_user.remote_id, + "@context": "https://www.w3.org/ns/activitystreams", + "object": { + "@context": "https://www.w3.org/ns/activitystreams", + "id": request.remote_id, + "type": "Follow", + "actor": "https://example.com/users/rat", + "object": "https://example.com/user/mouse", + }, + } + + views.inbox.activity_task(activity) + + self.assertFalse(self.local_user.follower_requests.exists()) + def test_handle_unfollow(self): """ remove a relationship """ with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index b645e943..3ef2a79b 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -188,8 +188,8 @@ urlpatterns = [ re_path(r"^start-reading/(?P\d+)/?$", views.start_reading), re_path(r"^finish-reading/(?P\d+)/?$", views.finish_reading), # following - re_path(r"^follow/?$", views.follow), - re_path(r"^unfollow/?$", views.unfollow), + re_path(r"^follow/?$", views.follow, name="follow"), + re_path(r"^unfollow/?$", views.unfollow, name="unfollow"), re_path(r"^accept-follow-request/?$", views.accept_follow_request), re_path(r"^delete-follow-request/?$", views.delete_follow_request), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/bookwyrm/views/follow.py b/bookwyrm/views/follow.py index 515bf325..d9f455eb 100644 --- a/bookwyrm/views/follow.py +++ b/bookwyrm/views/follow.py @@ -40,10 +40,22 @@ def unfollow(request): except models.User.DoesNotExist: return HttpResponseBadRequest() - models.UserFollows.objects.get( - user_subject=request.user, user_object=to_unfollow - ).delete() - return redirect(to_unfollow.local_path) + try: + models.UserFollows.objects.get( + user_subject=request.user, user_object=to_unfollow + ).delete() + except models.UserFollows.DoesNotExist: + pass + + try: + models.UserFollowRequest.objects.get( + user_subject=request.user, user_object=to_unfollow + ).delete() + except models.UserFollowRequest.DoesNotExist: + pass + + # this is handled with ajax so it shouldn't really matter + return redirect(request.headers.get("Referer", "/")) @login_required