-
diff --git a/bookwyrm/templates/lists/list_layout.html b/bookwyrm/templates/lists/layout.html
similarity index 100%
rename from bookwyrm/templates/lists/list_layout.html
rename to bookwyrm/templates/lists/layout.html
diff --git a/bookwyrm/templates/lists/list.html b/bookwyrm/templates/lists/list.html
index 37f7079bd..35014a7b6 100644
--- a/bookwyrm/templates/lists/list.html
+++ b/bookwyrm/templates/lists/list.html
@@ -1,4 +1,4 @@
-{% extends 'lists/list_layout.html' %}
+{% extends 'lists/layout.html' %}
{% load i18n %}
{% load bookwyrm_tags %}
{% load markdown %}
diff --git a/bookwyrm/templates/user_admin/user.html b/bookwyrm/templates/user_admin/user.html
index c79c6971f..5c118175b 100644
--- a/bookwyrm/templates/user_admin/user.html
+++ b/bookwyrm/templates/user_admin/user.html
@@ -2,13 +2,15 @@
{% load i18n %}
{% block title %}{{ user.username }}{% endblock %}
-{% block header %}{{ user.username }}{% endblock %}
+{% block header %}
+{{ user.username }}
+
-
{% include 'user_admin/user_info.html' with user=user %}
{% include 'user_admin/user_moderation_actions.html' with user=user %}
diff --git a/bookwyrm/templates/user_admin/user_admin_filters.html b/bookwyrm/templates/user_admin/user_admin_filters.html
index 5283b4e9c..c9c7a93fc 100644
--- a/bookwyrm/templates/user_admin/user_admin_filters.html
+++ b/bookwyrm/templates/user_admin/user_admin_filters.html
@@ -1,7 +1,7 @@
{% extends 'snippets/filters_panel/filters_panel.html' %}
{% block filter_fields %}
-{% include 'user_admin/server_filter.html' %}
{% include 'user_admin/username_filter.html' %}
{% include 'directory/community_filter.html' %}
+{% include 'user_admin/server_filter.html' %}
{% endblock %}
diff --git a/bookwyrm/templates/user_admin/user_info.html b/bookwyrm/templates/user_admin/user_info.html
index 579b3af7e..69a84a664 100644
--- a/bookwyrm/templates/user_admin/user_info.html
+++ b/bookwyrm/templates/user_admin/user_info.html
@@ -1,8 +1,10 @@
{% load i18n %}
{% load markdown %}
+{% load humanize %}
+
+
+
{% trans "User details" %}
+
+
+ {% if user.local %}
+
+
- {% trans "Email:" %}
+ - {{ user.email }}
+
+ {% endif %}
+
+ {% with report_count=user.report_set.count %}
+
+ {% endwith %}
+
+
+
- {% trans "Blocked by count:" %}
+ - {{ user.blocked_by.count }}
+
+
+
+
- {% trans "Last active date:" %}
+ - {{ user.last_active_date }}
+
+
+
+
- {% trans "Manually approved followers:" %}
+ - {{ user.manually_approves_followers }}
+
+
+
+
- {% trans "Discoverable:" %}
+ - {{ user.discoverable }}
+
+
+ {% if not user.is_active %}
+
+
- {% trans "Deactivation reason:" %}
+ - {{ user.deactivation_reason }}
+
+ {% endif %}
+
+ {% if not user.is_active and user.deactivation_reason == "pending" %}
+
+
- {% trans "Confirmation code:" %}
+ - {{ user.confirmation_code }}
+
+ {% endif %}
+
+
+
+
{% if not user.local %}
{% with server=user.federated_server %}
diff --git a/bookwyrm/tests/views/inbox/test_inbox_delete.py b/bookwyrm/tests/views/inbox/test_inbox_delete.py
index 1566c05a2..f47d87374 100644
--- a/bookwyrm/tests/views/inbox/test_inbox_delete.py
+++ b/bookwyrm/tests/views/inbox/test_inbox_delete.py
@@ -40,14 +40,6 @@ class InboxActivities(TestCase):
remote_id="https://example.com/status/1",
)
- self.create_json = {
- "id": "hi",
- "type": "Create",
- "actor": "hi",
- "to": ["https://www.w3.org/ns/activitystreams#public"],
- "cc": ["https://example.com/user/mouse/followers"],
- "object": {},
- }
models.SiteSettings.objects.create()
def test_delete_status(self):
@@ -137,3 +129,30 @@ class InboxActivities(TestCase):
# nothing happens.
views.inbox.activity_task(activity)
self.assertEqual(models.User.objects.filter(is_active=True).count(), 2)
+
+ def test_delete_list(self):
+ """delete a list"""
+ book_list = models.List.objects.create(
+ name="test list",
+ user=self.remote_user,
+ remote_id="https://example.com/list/1",
+ )
+ activity = {
+ "@context": "https://www.w3.org/ns/activitystreams",
+ "id": "https://example.com/users/test-user#delete",
+ "type": "Delete",
+ "actor": "https://example.com/users/test-user",
+ "to": ["https://www.w3.org/ns/activitystreams#Public"],
+ "object": {
+ "id": book_list.remote_id,
+ "owner": self.remote_user.remote_id,
+ "type": "BookList",
+ "totalItems": 0,
+ "first": "",
+ "name": "test list",
+ "to": [],
+ "cc": [],
+ },
+ }
+ views.inbox.activity_task(activity)
+ self.assertFalse(models.List.objects.exists())
diff --git a/bookwyrm/tests/views/test_list_actions.py b/bookwyrm/tests/views/test_list_actions.py
index 2339427c6..84c5540c5 100644
--- a/bookwyrm/tests/views/test_list_actions.py
+++ b/bookwyrm/tests/views/test_list_actions.py
@@ -3,6 +3,7 @@ import json
from unittest.mock import patch
from django.contrib.auth.models import AnonymousUser
+from django.core.exceptions import PermissionDenied
from django.test import TestCase
from django.test.client import RequestFactory
@@ -66,6 +67,44 @@ class ListActionViews(TestCase):
self.anonymous_user.is_authenticated = False
models.SiteSettings.objects.create()
+ def test_delete_list(self):
+ """delete an entire list"""
+ with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
+ models.ListItem.objects.create(
+ book_list=self.list,
+ user=self.local_user,
+ book=self.book,
+ approved=True,
+ order=1,
+ )
+ models.ListItem.objects.create(
+ book_list=self.list,
+ user=self.local_user,
+ book=self.book_two,
+ approved=False,
+ order=2,
+ )
+ request = self.factory.post("")
+ request.user = self.local_user
+ with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock:
+ views.delete_list(request, self.list.id)
+ activity = json.loads(mock.call_args[0][1])
+ self.assertEqual(activity["type"], "Delete")
+ self.assertEqual(activity["actor"], self.local_user.remote_id)
+ self.assertEqual(activity["object"]["id"], self.list.remote_id)
+ self.assertEqual(activity["object"]["type"], "BookList")
+
+ self.assertEqual(mock.call_count, 1)
+ self.assertFalse(models.List.objects.exists())
+ self.assertFalse(models.ListItem.objects.exists())
+
+ def test_delete_list_permission_denied(self):
+ """delete an entire list"""
+ request = self.factory.post("")
+ request.user = self.rat
+ with self.assertRaises(PermissionDenied):
+ views.delete_list(request, self.list.id)
+
def test_curate_approve(self):
"""approve a pending item"""
view = views.Curate.as_view()
diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py
index 1dbd67a76..991114fad 100644
--- a/bookwyrm/urls.py
+++ b/bookwyrm/urls.py
@@ -220,6 +220,7 @@ urlpatterns = [
re_path(r"^list/?$", views.Lists.as_view(), name="lists"),
re_path(r"^list/saved/?$", views.SavedLists.as_view(), name="saved-lists"),
re_path(r"^list/(?P\d+)(.json)?/?$", views.List.as_view(), name="list"),
+ re_path(r"^list/delete/(?P\d+)/?$", views.delete_list, name="delete-list"),
re_path(r"^list/add-book/?$", views.list.add_book, name="list-add-book"),
re_path(
r"^list/(?P\d+)/remove/?$",
diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py
index f42049254..ca52800c4 100644
--- a/bookwyrm/views/__init__.py
+++ b/bookwyrm/views/__init__.py
@@ -27,6 +27,7 @@ from .isbn import Isbn
from .landing import About, Home, Landing
from .list import Lists, SavedLists, List, Curate, UserLists
from .list import save_list, unsave_list
+from .list import delete_list
from .notifications import Notifications
from .outbox import Outbox
from .reading import edit_readthrough, create_readthrough
diff --git a/bookwyrm/views/discover.py b/bookwyrm/views/discover.py
index 059365f97..2ae4a9303 100644
--- a/bookwyrm/views/discover.py
+++ b/bookwyrm/views/discover.py
@@ -16,6 +16,7 @@ class Discover(View):
def get(self, request):
"""tiled book activity page"""
+ # all activities in the "federated" feed associated with a book
activities = (
activitystreams.streams["local"]
.get_activity_stream(request.user)
@@ -29,13 +30,19 @@ class Discover(View):
large_activities = Paginator(
activities.filter(mention_books__isnull=True)
- .exclude(content=None, quotation__quote=None)
- .exclude(content=""),
+ # exclude statuses with no user-provided content for large panels
+ .exclude(
+ Q(Q(content="") | Q(content__isnull=True)) & Q(quotation__isnull=True),
+ ),
6,
)
small_activities = Paginator(
activities.filter(
- Q(mention_books__isnull=False) | Q(content=None) | Q(content="")
+ Q(mention_books__isnull=False)
+ | Q(
+ Q(Q(content="") | Q(content__isnull=True))
+ & Q(quotation__isnull=True),
+ )
),
4,
)
diff --git a/bookwyrm/views/list.py b/bookwyrm/views/list.py
index e6ef52ba5..af99e9f55 100644
--- a/bookwyrm/views/list.py
+++ b/bookwyrm/views/list.py
@@ -3,6 +3,7 @@ from typing import Optional
from urllib.parse import urlencode
from django.contrib.auth.decorators import login_required
+from django.core.exceptions import PermissionDenied
from django.core.paginator import Paginator
from django.db import IntegrityError, transaction
from django.db.models import Avg, Count, DecimalField, Q, Max
@@ -260,6 +261,20 @@ def unsave_list(request, list_id):
return redirect("list", list_id)
+@require_POST
+@login_required
+def delete_list(request, list_id):
+ """delete a list"""
+ book_list = get_object_or_404(models.List, id=list_id)
+
+ # only the owner or a moderator can delete a list
+ if book_list.user != request.user and not request.user.has_perm("moderate_post"):
+ raise PermissionDenied
+
+ book_list.delete()
+ return redirect("lists")
+
+
@require_POST
@login_required
def add_book(request):
diff --git a/bookwyrm/views/user_admin.py b/bookwyrm/views/user_admin.py
index 3a9ea3392..ac5357a28 100644
--- a/bookwyrm/views/user_admin.py
+++ b/bookwyrm/views/user_admin.py
@@ -31,8 +31,8 @@ class UserAdminList(View):
if username:
filters["username__icontains"] = username
scope = request.GET.get("scope")
- if scope:
- filters["local"] = scope == "local"
+ if scope and scope == "local":
+ filters["local"] = True
users = models.User.objects.filter(**filters)
diff --git a/nginx/production b/nginx/production
index 76f18f32f..3c8b2ea7d 100644
--- a/nginx/production
+++ b/nginx/production
@@ -24,7 +24,9 @@ server {
# listen 443 ssl http2;
#
# server_name your-domain.com;
-
+#
+# client_max_body_size 3M;
+#
# if ($host != "you-domain.com") {
# return 301 $scheme://your-domain.com$request_uri;
# }