diff --git a/bookwyrm/incoming.py b/bookwyrm/incoming.py
index 3581ed87b..1e42d32ae 100644
--- a/bookwyrm/incoming.py
+++ b/bookwyrm/incoming.py
@@ -3,7 +3,6 @@ import json
from urllib.parse import urldefrag
import django.db.utils
-from django.db.models import Q
from django.http import HttpResponse
from django.http import HttpResponseBadRequest, HttpResponseNotFound
from django.views.decorators.csrf import csrf_exempt
@@ -64,6 +63,7 @@ def shared_inbox(request):
'Follow': handle_unfollow,
'Like': handle_unfavorite,
'Announce': handle_unboost,
+ 'Block': handle_unblock,
},
'Update': {
'Person': handle_update_user,
@@ -185,10 +185,24 @@ def handle_follow_reject(activity):
def handle_block(activity):
''' blocking a user '''
# create "block" databse entry
- block = activitypub.Block(**activity).to_model(models.UserBlocks)
+ activitypub.Block(**activity).to_model(models.UserBlocks)
# the removing relationships is handled in post-save hook in model
+@app.task
+def handle_unblock(activity):
+ ''' undoing a block '''
+ try:
+ block_id = activity['object']['id']
+ except KeyError:
+ return
+ try:
+ block = models.UserBlocks.objects.get(remote_id=block_id)
+ except models.UserBlocks.DoesNotExist:
+ return
+ block.delete()
+
+
@app.task
def handle_create(activity):
''' someone did something, good on them '''
diff --git a/bookwyrm/templates/snippets/status_options.html b/bookwyrm/templates/snippets/status_options.html
index 2e2e5d35b..b5887b1d8 100644
--- a/bookwyrm/templates/snippets/status_options.html
+++ b/bookwyrm/templates/snippets/status_options.html
@@ -19,7 +19,7 @@
{% else %}
- {% include 'snippets/block_button.html' with user=status.user %}
+ {% include 'snippets/block_button.html' with user=status.user class="is-fullwidth" %}
{% endif %}
{% endblock %}
diff --git a/bookwyrm/templates/snippets/user_options.html b/bookwyrm/templates/snippets/user_options.html
index 9515d9128..2c163034e 100644
--- a/bookwyrm/templates/snippets/user_options.html
+++ b/bookwyrm/templates/snippets/user_options.html
@@ -9,6 +9,6 @@
{% block dropdown-list %}
- {% include 'snippets/block_button.html' with user=user %}
+ {% include 'snippets/block_button.html' with user=user class="is-fullwidth" %}
{% endblock %}
diff --git a/bookwyrm/tests/test_incoming.py b/bookwyrm/tests/test_incoming.py
index 024c8e253..1ee7c59ec 100644
--- a/bookwyrm/tests/test_incoming.py
+++ b/bookwyrm/tests/test_incoming.py
@@ -566,3 +566,20 @@ class Incoming(TestCase):
self.assertFalse(models.UserFollows.objects.exists())
self.assertFalse(models.UserFollowRequest.objects.exists())
+
+ def test_handle_unblock(self):
+ ''' undoing a block '''
+ activity = {
+ "@context": "https://www.w3.org/ns/activitystreams",
+ "id": "https://friend.camp/users/tripofmice#blocks/1155/undo",
+ "type": "Undo",
+ "actor": "https://friend.camp/users/tripofmice",
+ "object": {
+ "id": "https://friend.camp/0a7d85f7-6359-4c03-8ab6-74e61a8fb678",
+ "type": "Block",
+ "actor": "https://friend.camp/users/tripofmice",
+ "object": "https://1b1a78582461.ngrok.io/user/mouse"
+ }
+ }
+
+ self.remote_user.blocks.add(self.local_user)
diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py
index 0569fd9ca..4f9a43ea3 100644
--- a/bookwyrm/urls.py
+++ b/bookwyrm/urls.py
@@ -139,4 +139,5 @@ urlpatterns = [
re_path(r'^block/?$', views.Block.as_view()),
re_path(r'^block/(?P\d+)/?$', views.Block.as_view()),
+ re_path(r'^unblock/(?P\d+)/?$', views.unblock),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py
index 1521b2682..e3ac29c84 100644
--- a/bookwyrm/views/__init__.py
+++ b/bookwyrm/views/__init__.py
@@ -1,7 +1,7 @@
''' make sure all our nice views are available '''
from .authentication import Login, Register, Logout
from .author import Author, EditAuthor
-from .block import Block
+from .block import Block, unblock
from .books import Book, EditBook, Editions
from .books import upload_cover, add_description, switch_edition, resolve_book
from .direct_message import DirectMessage
diff --git a/bookwyrm/views/block.py b/bookwyrm/views/block.py
index 6158f373c..fb95479af 100644
--- a/bookwyrm/views/block.py
+++ b/bookwyrm/views/block.py
@@ -1,9 +1,11 @@
''' views for actions you can take in the application '''
from django.contrib.auth.decorators import login_required
+from django.http import HttpResponseNotFound
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 django.views.decorators.http import require_POST
from bookwyrm import models
from bookwyrm.broadcast import broadcast
@@ -30,3 +32,27 @@ class Block(View):
direct_recipients=[to_block]
)
return redirect('/block')
+
+
+@require_POST
+@login_required
+def unblock(request, user_id):
+ ''' undo a block '''
+ to_unblock = get_object_or_404(models.User, id=user_id)
+ try:
+ block = models.UserBlocks.objects.get(
+ user_subject=request.user,
+ user_object=to_unblock,
+ )
+ except models.UserBlocks.DoesNotExist:
+ return HttpResponseNotFound()
+
+ if not to_unblock.local:
+ broadcast(
+ request.user,
+ block.to_undo_activity(request.user),
+ privacy='direct',
+ direct_recipients=[to_unblock]
+ )
+ block.delete()
+ return redirect('/block')