mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-11-22 17:41:08 +00:00
Merge pull request #928 from mouse-reeve/blocklist
Allow admins to upload domain blocklists
This commit is contained in:
commit
20cb173150
10 changed files with 175 additions and 12 deletions
12
README.md
12
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
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="id_csv_field">{% trans "Data file:" %}</label>
|
||||
<label class="label" for="id_csv_file">{% trans "Data file:" %}</label>
|
||||
{{ import_form.csv_file }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -9,6 +9,19 @@
|
|||
|
||||
{% block panel %}
|
||||
|
||||
<div class="tabs">
|
||||
<ul>
|
||||
{% url 'settings-import-blocklist' as url %}
|
||||
<li {% if url in request.path %}class="is-active" aria-current="page"{% endif %}>
|
||||
<a href="{{ url }}">{% trans "Import block list" %}</a>
|
||||
</li>
|
||||
{% url 'settings-add-federated-server' as url %}
|
||||
<li {% if url in request.path %}class="is-active" aria-current="page"{% endif %}>
|
||||
<a href="{{ url }}">{% trans "Add server" %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{% url 'settings-add-federated-server' %}">
|
||||
{% csrf_token %}
|
||||
<div class="columns">
|
||||
|
@ -24,8 +37,8 @@
|
|||
<label class="label" for="id_status">{% trans "Status:" %}</label>
|
||||
<div class="select">
|
||||
<select name="status" class="" id="id_status">
|
||||
<option value="federated" {% if form.status.value == "federated" or not form.status.value %}selected=""{% endif %}>{% trans "Federated" %}</option>
|
||||
<option value="blocked" {% if form.status.value == "blocked" %}selected{% endif %}>{% trans "Blocked" %}</option>
|
||||
<option value="federated" {% if form.status.value == "federated" %}selected=""{% endif %}>{% trans "Federated" %}</option>
|
||||
<option value="blocked" {% if form.status.value == "blocked" or not form.status.value %}selected{% endif %}>{% trans "Blocked" %}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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 @@
|
|||
</div>
|
||||
</header>
|
||||
{% if server.notes %}
|
||||
<p id="hide-edit-notes">{{ server.notes }}</p>
|
||||
<p id="hide-edit-notes">{{ server.notes|to_markdown|safe }}</p>
|
||||
{% endif %}
|
||||
<form class="box is-hidden" method="POST" action="{% url 'settings-federated-server' server.id %}" id="edit-notes">
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
{% block header %}{% trans "Federated Servers" %}{% endblock %}
|
||||
|
||||
{% block edit-button %}
|
||||
<a href="{% url 'settings-add-federated-server' %}">
|
||||
<a href="{% url 'settings-import-blocklist' %}">
|
||||
<span class="icon icon-plus" title="{% trans 'Add server' %}">
|
||||
<span class="is-sr-only">{% trans "Add server" %}</span>
|
||||
</span>
|
||||
|
|
67
bookwyrm/templates/settings/server_blocklist.html
Normal file
67
bookwyrm/templates/settings/server_blocklist.html
Normal file
|
@ -0,0 +1,67 @@
|
|||
{% extends 'settings/admin_layout.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Add server" %}{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
{% trans "Import Blocklist" %}
|
||||
<a href="{% url 'settings-federation' %}" class="has-text-weight-normal help">{% trans "Back to server list" %}</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
|
||||
<div class="tabs">
|
||||
<ul>
|
||||
{% url 'settings-import-blocklist' as url %}
|
||||
<li {% if url in request.path %}class="is-active" aria-current="page"{% endif %}>
|
||||
<a href="{{ url }}">{% trans "Import block list" %}</a>
|
||||
</li>
|
||||
{% url 'settings-add-federated-server' as url %}
|
||||
<li {% if url in request.path %}class="is-active" aria-current="page"{% endif %}>
|
||||
<a href="{{ url }}">{% trans "Add server" %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% if succeeded and not failed %}
|
||||
<p class="notification is-primary">{% trans "Success!" %}</p>
|
||||
{% elif succeeded or failed %}
|
||||
<div class="block content">
|
||||
{% if succeeded %}
|
||||
<p>{% trans "Successfully blocked:" %} {{ succeeded }}</p>
|
||||
{% endif %}
|
||||
<p>{% trans "Failed:" %}</p>
|
||||
<ul>
|
||||
{% for item in failed %}
|
||||
<li>
|
||||
<pre>
|
||||
{{ item }}
|
||||
</pre>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="POST" action="{% url 'settings-import-blocklist' %}" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<div class="field">
|
||||
<label class="label" for="id_file">JSON data:</label>
|
||||
<aside class="help">
|
||||
Expects a json file in the format provided by <a href="https://fediblock.org/" target="_blank" rel=”noopener”>FediBlock</a>, with a list of entries that have <code>instance</code> and <code>url</code> fields. For example:
|
||||
<pre>
|
||||
[
|
||||
{
|
||||
"instance": "example.server.com",
|
||||
"url": "https://link.to.more/info"
|
||||
},
|
||||
...
|
||||
]
|
||||
</pre>
|
||||
</aside>
|
||||
<input type="file" name="json_file" required="" id="id_file">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="button is-primary">{% trans "Import" %}</button>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
|
@ -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")
|
||||
|
|
|
@ -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"
|
||||
),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in a new issue