diff --git a/README.md b/README.md index 670d8b23..ffdf42de 100644 --- a/README.md +++ b/README.md @@ -32,27 +32,27 @@ Federation makes it possible to have small, self-determining communities, in con ### Features Since the project is still in its early stages, the features are growing every day, and there is plenty of room for suggestions and ideas. Open an [issue](https://github.com/mouse-reeve/bookwyrm/issues) to get the conversation going! - - Posting about books +- Posting about books - Compose reviews, with or without ratings, which are aggregated in the book page - Compose other kinds of statuses about books, such as: - - Comments on a book - - Quotes or excerpts + - Comments on a book + - Quotes or excerpts - Reply to statuses - View aggregate reviews of a book across connected BookWyrm instances - Differentiate local and federated reviews and rating in your activity feed - - Track reading activity +- Track reading activity - Shelve books on default "to-read," "currently reading," and "read" shelves - Create custom shelves - Store started reading/finished reading dates, as well as progress updates along the way - Update followers about reading activity (optionally, and with granular privacy controls) - Create lists of books which can be open to submissions from anyone, curated, or only edited by the creator - - Federation with ActivityPub +- Federation with ActivityPub - Broadcast and receive user statuses and activity - Share book data between instances to create a networked database of metadata - Identify shared books across instances and aggregate related content - Follow and interact with users across BookWyrm instances - Inter-operate with non-BookWyrm ActivityPub services (currently, Mastodon is supported) - - Granular privacy controls +- Granular privacy controls - Private, followers-only, and public privacy levels for posting, shelves, and lists - Option for users to manually approve followers - Allow blocking and flagging for moderation diff --git a/bookwyrm/templates/import.html b/bookwyrm/templates/import.html index a5405131..239b6914 100644 --- a/bookwyrm/templates/import.html +++ b/bookwyrm/templates/import.html @@ -26,7 +26,7 @@
- + {{ import_form.csv_file }}
diff --git a/bookwyrm/templates/settings/edit_server.html b/bookwyrm/templates/settings/edit_server.html index 6ae22789..c5702c84 100644 --- a/bookwyrm/templates/settings/edit_server.html +++ b/bookwyrm/templates/settings/edit_server.html @@ -9,6 +9,19 @@ {% block panel %} +
+ +
+
{% csrf_token %}
@@ -24,8 +37,8 @@
diff --git a/bookwyrm/templates/settings/federated_server.html b/bookwyrm/templates/settings/federated_server.html index 6996557d..14b78af0 100644 --- a/bookwyrm/templates/settings/federated_server.html +++ b/bookwyrm/templates/settings/federated_server.html @@ -1,6 +1,7 @@ {% extends 'settings/admin_layout.html' %} {% block title %}{{ server.server_name }}{% endblock %} {% load i18n %} +{% load bookwyrm_tags %} {% block header %} {{ server.server_name }} @@ -81,7 +82,7 @@ {% if server.notes %} -

{{ server.notes }}

+

{{ server.notes|to_markdown|safe }}

{% endif %} {% csrf_token %} diff --git a/bookwyrm/templates/settings/federation.html b/bookwyrm/templates/settings/federation.html index 99afb541..9eb0f8b1 100644 --- a/bookwyrm/templates/settings/federation.html +++ b/bookwyrm/templates/settings/federation.html @@ -5,7 +5,7 @@ {% block header %}{% trans "Federated Servers" %}{% endblock %} {% block edit-button %} - + {% trans "Add server" %} diff --git a/bookwyrm/templates/settings/server_blocklist.html b/bookwyrm/templates/settings/server_blocklist.html new file mode 100644 index 00000000..0de49acd --- /dev/null +++ b/bookwyrm/templates/settings/server_blocklist.html @@ -0,0 +1,67 @@ +{% extends 'settings/admin_layout.html' %} +{% load i18n %} +{% block title %}{% trans "Add server" %}{% endblock %} + +{% block header %} +{% trans "Import Blocklist" %} +{% trans "Back to server list" %} +{% endblock %} + +{% block panel %} + +
+ +
+ +{% if succeeded and not failed %} +

{% trans "Success!" %}

+{% elif succeeded or failed %} +
+ {% if succeeded %} +

{% trans "Successfully blocked:" %} {{ succeeded }}

+ {% endif %} +

{% trans "Failed:" %}

+ +
+{% endif %} + + + {% csrf_token %} +
+ + + +
+ + +
+ +{% endblock %} diff --git a/bookwyrm/tests/views/test_federation.py b/bookwyrm/tests/views/test_federation.py index 4dc5d048..2afdd6d3 100644 --- a/bookwyrm/tests/views/test_federation.py +++ b/bookwyrm/tests/views/test_federation.py @@ -1,5 +1,7 @@ """ test for app action functionality """ +import json from unittest.mock import patch +from django.core.files.uploadedfile import SimpleUploadedFile from django.template.response import TemplateResponse from django.test import TestCase from django.test.client import RequestFactory @@ -127,3 +129,39 @@ class FederationViews(TestCase): self.assertEqual(server.server_name, "remote.server") self.assertEqual(server.application_type, "coolsoft") self.assertEqual(server.status, "blocked") + + def test_import_blocklist(self): + """ load a json file with a list of servers to block """ + server = models.FederatedServer.objects.create(server_name="hi.there.com") + self.remote_user.federated_server = server + self.remote_user.save() + + data = [ + {"instance": "server.name", "url": "https://explanation.url"}, # new server + {"instance": "hi.there.com", "url": "https://explanation.url"}, # existing + {"a": "b"}, # invalid + ] + json.dump(data, open("file.json", "w")) + + view = views.ImportServerBlocklist.as_view() + request = self.factory.post( + "", + { + "json_file": SimpleUploadedFile( + "file.json", open("file.json", "rb").read() + ) + }, + ) + request.user = self.local_user + request.user.is_superuser = True + + view(request) + server.refresh_from_db() + self.remote_user.refresh_from_db() + + self.assertEqual(models.FederatedServer.objects.count(), 2) + self.assertEqual(server.status, "blocked") + self.assertFalse(self.remote_user.is_active) + created = models.FederatedServer.objects.get(server_name="server.name") + self.assertEqual(created.status, "blocked") + self.assertEqual(created.notes, "https://explanation.url") diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index c5c52800..cf3f877b 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -83,6 +83,11 @@ urlpatterns = [ views.AddFederatedServer.as_view(), name="settings-add-federated-server", ), + re_path( + r"^settings/federation/import/?$", + views.ImportServerBlocklist.as_view(), + name="settings-import-blocklist", + ), re_path( r"^settings/invites/?$", views.ManageInvites.as_view(), name="settings-invites" ), diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index d7bc4e13..9f8463b4 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -5,7 +5,8 @@ from .block import Block, unblock from .books import Book, EditBook, ConfirmEditBook, Editions from .books import upload_cover, add_description, switch_edition, resolve_book from .directory import Directory -from .federation import Federation, FederatedServer, AddFederatedServer +from .federation import Federation, FederatedServer +from .federation import AddFederatedServer, ImportServerBlocklist from .federation import block_server, unblock_server from .feed import DirectMessage, Feed, Replies, Status from .follow import follow, unfollow diff --git a/bookwyrm/views/federation.py b/bookwyrm/views/federation.py index f34f7d19..db54cd7a 100644 --- a/bookwyrm/views/federation.py +++ b/bookwyrm/views/federation.py @@ -1,6 +1,8 @@ """ manage federated servers """ +import json from django.contrib.auth.decorators import login_required, permission_required from django.core.paginator import Paginator +from django.db import transaction from django.shortcuts import get_object_or_404, redirect from django.template.response import TemplateResponse from django.utils.decorators import method_decorator @@ -63,6 +65,42 @@ class AddFederatedServer(View): return redirect("settings-federated-server", server.id) +@method_decorator(login_required, name="dispatch") +@method_decorator( + permission_required("bookwyrm.control_federation", raise_exception=True), + name="dispatch", +) +class ImportServerBlocklist(View): + """ manually add a server """ + + def get(self, request): + """ add server form """ + return TemplateResponse(request, "settings/server_blocklist.html") + + def post(self, request): + """ add a server from the admin panel """ + json_data = json.load(request.FILES["json_file"]) + failed = [] + success_count = 0 + for item in json_data: + server_name = item.get("instance") + if not server_name: + failed.append(item) + continue + info_link = item.get("url") + + with transaction.atomic(): + server, _ = models.FederatedServer.objects.get_or_create( + server_name=server_name, + ) + server.notes = info_link + server.save() + server.block() + success_count += 1 + data = {"failed": failed, "succeeded": success_count} + return TemplateResponse(request, "settings/server_blocklist.html", data) + + @method_decorator(login_required, name="dispatch") @method_decorator( permission_required("bookwyrm.control_federation", raise_exception=True),