1
0
Fork 0

Merge branch 'main' into notifications

This commit is contained in:
Mouse Reeve 2022-07-06 14:41:15 -07:00
commit 5a6ef7ccd8
29 changed files with 441 additions and 96 deletions

View file

@ -45,6 +45,7 @@ def moderation_report_email(report):
"""a report was created""" """a report was created"""
data = email_data() data = email_data()
data["reporter"] = report.reporter.localname or report.reporter.username data["reporter"] = report.reporter.localname or report.reporter.username
if report.user:
data["reportee"] = report.user.localname or report.user.username data["reportee"] = report.user.localname or report.user.username
data["report_link"] = report.remote_id data["report_link"] = report.remote_id

View file

@ -10,3 +10,4 @@ from .landing import *
from .links import * from .links import *
from .lists import * from .lists import *
from .status import * from .status import *
from .user_admin import *

View file

@ -4,12 +4,6 @@ from .custom_form import CustomForm
# pylint: disable=missing-class-docstring # pylint: disable=missing-class-docstring
class UserGroupForm(CustomForm):
class Meta:
model = models.User
fields = ["groups"]
class GroupForm(CustomForm): class GroupForm(CustomForm):
class Meta: class Meta:
model = models.Group model = models.Group

View file

@ -0,0 +1,10 @@
""" using django model forms """
from bookwyrm import models
from .custom_form import CustomForm
# pylint: disable=missing-class-docstring
class UserGroupForm(CustomForm):
class Meta:
model = models.User
fields = ["groups"]

View file

@ -0,0 +1,24 @@
# Generated by Django 3.2.13 on 2022-07-05 23:54
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0150_readthrough_stopped_date"),
]
operations = [
migrations.AlterField(
model_name="report",
name="user",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT,
to=settings.AUTH_USER_MODEL,
),
),
]

View file

@ -0,0 +1,25 @@
# Generated by Django 3.2.13 on 2022-07-06 19:16
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0151_alter_report_user"),
]
operations = [
migrations.AlterField(
model_name="report",
name="user",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
to=settings.AUTH_USER_MODEL,
),
),
]

View file

@ -11,7 +11,7 @@ class Report(BookWyrmModel):
"User", related_name="reporter", on_delete=models.PROTECT "User", related_name="reporter", on_delete=models.PROTECT
) )
note = models.TextField(null=True, blank=True) note = models.TextField(null=True, blank=True)
user = models.ForeignKey("User", on_delete=models.PROTECT) user = models.ForeignKey("User", on_delete=models.PROTECT, null=True, blank=True)
status = models.ForeignKey( status = models.ForeignKey(
"Status", "Status",
null=True, null=True,

View file

@ -42,7 +42,11 @@
<a href="{{ link.url }}" target="_blank" rel="noopener noreferrer">{{ link.url }}</a> <a href="{{ link.url }}" target="_blank" rel="noopener noreferrer">{{ link.url }}</a>
</td> </td>
<td> <td>
{% if link.added_by %}
<a href="{% url 'user-feed' link.added_by.id %}">{{ link.added_by.display_name }}</a> <a href="{% url 'user-feed' link.added_by.id %}">{{ link.added_by.display_name }}</a>
{% else %}
<em>{% trans "Unknown user" %}</em>
{% endif %}
</td> </td>
<td> <td>
{{ link.filelink.filetype }} {{ link.filelink.filetype }}
@ -50,7 +54,7 @@
<td> <td>
{{ link.domain.name }} {{ link.domain.name }}
<p> <p>
<a href="{% url 'report-link' link.added_by.id link.id %}">{% trans "Report spam" %}</a> <a href="{% url 'report-link' link.id %}">{% trans "Report spam" %}</a>
</p> </p>
</td> </td>
<td> <td>

View file

@ -19,7 +19,7 @@ Is that where you'd like to go?
{% block modal-footer %} {% block modal-footer %}
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<div class="is-flex-grow-1"> <div class="is-flex-grow-1">
<a href="{% url 'report-link' link.added_by.id link.id %}">{% trans "Report spam" %}</a> <a href="{% url 'report-link' link.id %}">{% trans "Report spam" %}</a>
</div> </div>
<button type="button" class="button" data-modal-close>{% trans "Cancel" %}</button> <button type="button" class="button" data-modal-close>{% trans "Cancel" %}</button>

View file

@ -3,7 +3,19 @@
{% block content %} {% block content %}
<p> <p>
{% blocktrans %}@{{ reporter }} has flagged behavior by @{{ reportee }} for moderation. {% endblocktrans %} {% if report_link %}
{% blocktrans trimmed %}
@{{ reporter }} has flagged a link domain for moderation.
{% endblocktrans %}
{% else %}
{% blocktrans trimmed %}
@{{ reporter }} has flagged behavior by @{{ reportee }} for moderation.
{% endblocktrans %}
{% endif %}
</p> </p>
{% trans "View report" as text %} {% trans "View report" as text %}

View file

@ -2,7 +2,15 @@
{% load i18n %} {% load i18n %}
{% block content %} {% block content %}
{% blocktrans %}@{{ reporter}} has flagged behavior by @{{ reportee }} for moderation. {% endblocktrans %} {% if report_link %}
{% blocktrans trimmed %}
@{{ reporter }} has flagged a link domain for moderation.
{% endblocktrans %}
{% else %}
{% blocktrans trimmed %}
@{{ reporter }} has flagged behavior by @{{ reportee }} for moderation.
{% endblocktrans %}
{% endif %}
{% trans "View report" %} {% trans "View report" %}
{{ report_link }} {{ report_link }}

View file

@ -15,7 +15,11 @@
<a href="{{ link.url }}" target="_blank" rel="noopener noreferrer">{{ link.url }}</a> <a href="{{ link.url }}" target="_blank" rel="noopener noreferrer">{{ link.url }}</a>
</td> </td>
<td> <td>
{% if link.added_by %}
<a href="{% url 'settings-user' link.added_by.id %}">@{{ link.added_by|username }}</a> <a href="{% url 'settings-user' link.added_by.id %}">@{{ link.added_by|username }}</a>
{% else %}
<em>{% trans "Unknown user" %}</em>
{% endif %}
</td> </td>
<td> <td>
{% if link.filelink.filetype %} {% if link.filelink.filetype %}

View file

@ -55,9 +55,11 @@
</div> </div>
{% endif %} {% endif %}
{% if report.user %}
{% include 'settings/users/user_info.html' with user=report.user %} {% include 'settings/users/user_info.html' with user=report.user %}
{% include 'settings/users/user_moderation_actions.html' with user=report.user %} {% include 'settings/users/user_moderation_actions.html' with user=report.user %}
{% endif %}
<div class="block"> <div class="block">
<h3 class="title is-4">{% trans "Moderator Comments" %}</h3> <h3 class="title is-4">{% trans "Moderator Comments" %}</h3>

View file

@ -9,9 +9,15 @@ Report #{{ report_id }}: Status posted by @{{ username }}
{% elif report.links.exists %} {% elif report.links.exists %}
{% blocktrans trimmed with report_id=report.id username=report.user|username %} {% if report.user %}
Report #{{ report_id }}: Link added by @{{ username }} {% blocktrans trimmed with report_id=report.id username=report.user|username %}
{% endblocktrans %} Report #{{ report_id }}: Link added by @{{ username }}
{% endblocktrans %}
{% else %}
{% blocktrans trimmed with report_id=report.id %}
Report #{{ report_id }}: Link domain
{% endblocktrans %}
{% endif %}
{% else %} {% else %}

View file

@ -72,6 +72,12 @@
<span class="icon icon-check"></span> <span class="icon icon-check"></span>
</span> </span>
{% trans "Active" %} {% trans "Active" %}
{% elif user.deactivation_reason == "moderator_deletion" or user.deactivation_reason == "self_deletion" %}
<span class="tag is-danger" aria-hidden="true">
<span class="icon icon-x"></span>
</span>
{% trans "Deleted" %}
<span class="help">({{ user.get_deactivation_reason_display }})</span>
{% else %} {% else %}
<span class="tag is-warning" aria-hidden="true"> <span class="tag is-warning" aria-hidden="true">
<span class="icon icon-x"></span> <span class="icon icon-x"></span>

View file

@ -53,7 +53,7 @@ def comparison_bool(str1, str2, reverse=False):
@register.filter(is_safe=True) @register.filter(is_safe=True)
def truncatepath(value, arg): def truncatepath(value, arg):
"""Truncate a path by removing all directories except the first and truncating .""" """Truncate a path by removing all directories except the first and truncating"""
path = os.path.normpath(value.name) path = os.path.normpath(value.name)
path_list = path.split(os.sep) path_list = path.split(os.sep)
try: try:

View file

@ -1,4 +1,5 @@
""" style fixes and lookups for templates """ """ style fixes and lookups for templates """
from collections import namedtuple
import re import re
from unittest.mock import patch from unittest.mock import patch
@ -61,3 +62,18 @@ class UtilitiesTags(TestCase):
self.assertEqual(utilities.get_title(self.book), "Test Book") self.assertEqual(utilities.get_title(self.book), "Test Book")
book = models.Edition.objects.create(title="Oh", subtitle="oh my") book = models.Edition.objects.create(title="Oh", subtitle="oh my")
self.assertEqual(utilities.get_title(book), "Oh: oh my") self.assertEqual(utilities.get_title(book), "Oh: oh my")
def test_comparison_bool(self, *_):
"""just a simple comparison"""
self.assertTrue(utilities.comparison_bool("a", "a"))
self.assertFalse(utilities.comparison_bool("a", "b"))
self.assertFalse(utilities.comparison_bool("a", "a", reverse=True))
self.assertTrue(utilities.comparison_bool("a", "b", reverse=True))
def test_truncatepath(self, *_):
"""truncate a path"""
ValueMock = namedtuple("Value", ("name"))
value = ValueMock("home/one/two/three/four")
self.assertEqual(utilities.truncatepath(value, 2), "home/…ur")
self.assertEqual(utilities.truncatepath(value, "a"), "four")

View file

@ -4,6 +4,7 @@ from unittest.mock import patch
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.test import TestCase from django.test import TestCase
from django.test.client import RequestFactory from django.test.client import RequestFactory
from django_celery_beat.models import PeriodicTask, IntervalSchedule
from bookwyrm import forms, models, views from bookwyrm import forms, models, views
from bookwyrm.tests.validate_html import validate_html from bookwyrm.tests.validate_html import validate_html
@ -28,6 +29,43 @@ class AutomodViews(TestCase):
models.SiteSettings.objects.create() models.SiteSettings.objects.create()
def test_automod_rules_get(self): def test_automod_rules_get(self):
"""there are so many views, this just makes sure it LOADS"""
schedule = IntervalSchedule.objects.create(every=1, period="days")
PeriodicTask.objects.create(
interval=schedule,
name="automod-task",
task="bookwyrm.models.antispam.automod_task",
)
models.AutoMod.objects.create(created_by=self.local_user, string_match="hello")
view = views.AutoMod.as_view()
request = self.factory.get("")
request.user = self.local_user
request.user.is_superuser = True
result = view(request)
self.assertIsInstance(result, TemplateResponse)
validate_html(result.render())
self.assertEqual(result.status_code, 200)
def test_automod_rules_get_empty_with_schedule(self):
"""there are so many views, this just makes sure it LOADS"""
schedule = IntervalSchedule.objects.create(every=1, period="days")
PeriodicTask.objects.create(
interval=schedule,
name="automod-task",
task="bookwyrm.models.antispam.automod_task",
)
view = views.AutoMod.as_view()
request = self.factory.get("")
request.user = self.local_user
request.user.is_superuser = True
result = view(request)
self.assertIsInstance(result, TemplateResponse)
validate_html(result.render())
self.assertEqual(result.status_code, 200)
def test_automod_rules_get_empty_without_schedule(self):
"""there are so many views, this just makes sure it LOADS""" """there are so many views, this just makes sure it LOADS"""
view = views.AutoMod.as_view() view = views.AutoMod.as_view()
request = self.factory.get("") request = self.factory.get("")
@ -45,7 +83,7 @@ class AutomodViews(TestCase):
form.data["string_match"] = "hello" form.data["string_match"] = "hello"
form.data["flag_users"] = True form.data["flag_users"] = True
form.data["flag_statuses"] = False form.data["flag_statuses"] = False
form.data["created_by"] = self.local_user form.data["created_by"] = self.local_user.id
view = views.AutoMod.as_view() view = views.AutoMod.as_view()
request = self.factory.post("", form.data) request = self.factory.post("", form.data)
@ -53,6 +91,27 @@ class AutomodViews(TestCase):
request.user.is_superuser = True request.user.is_superuser = True
result = view(request) result = view(request)
self.assertIsInstance(result, TemplateResponse) self.assertIsInstance(result, TemplateResponse)
validate_html(result.render()) validate_html(result.render())
self.assertEqual(result.status_code, 200) self.assertEqual(result.status_code, 200)
rule = models.AutoMod.objects.get()
self.assertTrue(rule.flag_users)
self.assertFalse(rule.flag_statuses)
def test_schedule_automod_task(self):
"""Schedule the task"""
self.assertFalse(IntervalSchedule.objects.exists())
form = forms.IntervalScheduleForm()
form.data["every"] = 1
form.data["period"] = "days"
request = self.factory.post("", form.data)
request.user = self.local_user
request.user.is_superuser = True
response = views.schedule_automod_task(request)
self.assertEqual(response.status_code, 302)
self.assertTrue(IntervalSchedule.objects.exists())

View file

@ -6,7 +6,7 @@ from django.template.response import TemplateResponse
from django.test import TestCase from django.test import TestCase
from django.test.client import RequestFactory from django.test.client import RequestFactory
from bookwyrm import forms, models, views from bookwyrm import models, views
from bookwyrm.tests.validate_html import validate_html from bookwyrm.tests.validate_html import validate_html
@ -89,52 +89,6 @@ class ReportViews(TestCase):
self.assertEqual(comment.note, "hi") self.assertEqual(comment.note, "hi")
self.assertEqual(comment.report, report) self.assertEqual(comment.report, report)
def test_report_modal_view(self):
"""a user reports another user"""
request = self.factory.get("")
request.user = self.local_user
result = views.Report.as_view()(request, self.local_user.id)
validate_html(result.render())
def test_make_report(self):
"""a user reports another user"""
form = forms.ReportForm()
form.data["reporter"] = self.local_user.id
form.data["user"] = self.rat.id
request = self.factory.post("", form.data)
request.user = self.local_user
views.Report.as_view()(request)
report = models.Report.objects.get()
self.assertEqual(report.reporter, self.local_user)
self.assertEqual(report.user, self.rat)
def test_report_link(self):
"""a user reports a link as spam"""
book = models.Edition.objects.create(title="hi")
link = models.FileLink.objects.create(
book=book, added_by=self.local_user, url="https://skdjfs.sdf"
)
domain = link.domain
domain.status = "approved"
domain.save()
form = forms.ReportForm()
form.data["reporter"] = self.local_user.id
form.data["user"] = self.rat.id
form.data["links"] = link.id
request = self.factory.post("", form.data)
request.user = self.local_user
views.Report.as_view()(request)
report = models.Report.objects.get()
domain.refresh_from_db()
self.assertEqual(report.links.first().id, link.id)
self.assertEqual(domain.status, "pending")
def test_resolve_report(self): def test_resolve_report(self):
"""toggle report resolution status""" """toggle report resolution status"""
report = models.Report.objects.create(reporter=self.local_user, user=self.rat) report = models.Report.objects.create(reporter=self.local_user, user=self.rat)

View file

@ -48,7 +48,7 @@ class EditBookViews(TestCase):
models.SiteSettings.objects.create() models.SiteSettings.objects.create()
def test_edit_book_page(self): def test_edit_book_get(self):
"""there are so many views, this just makes sure it LOADS""" """there are so many views, this just makes sure it LOADS"""
view = views.EditBook.as_view() view = views.EditBook.as_view()
request = self.factory.get("") request = self.factory.get("")
@ -59,18 +59,7 @@ class EditBookViews(TestCase):
validate_html(result.render()) validate_html(result.render())
self.assertEqual(result.status_code, 200) self.assertEqual(result.status_code, 200)
def test_edit_book_create_page(self): def test_edit_book_post(self):
"""there are so many views, this just makes sure it LOADS"""
view = views.CreateBook.as_view()
request = self.factory.get("")
request.user = self.local_user
request.user.is_superuser = True
result = view(request)
self.assertIsInstance(result, TemplateResponse)
validate_html(result.render())
self.assertEqual(result.status_code, 200)
def test_edit_book(self):
"""lets a user edit a book""" """lets a user edit a book"""
view = views.EditBook.as_view() view = views.EditBook.as_view()
self.local_user.groups.add(self.group) self.local_user.groups.add(self.group)
@ -86,6 +75,23 @@ class EditBookViews(TestCase):
self.book.refresh_from_db() self.book.refresh_from_db()
self.assertEqual(self.book.title, "New Title") self.assertEqual(self.book.title, "New Title")
def test_edit_book_post_invalid(self):
"""book form is invalid"""
view = views.EditBook.as_view()
self.local_user.groups.add(self.group)
form = forms.EditionForm(instance=self.book)
form.data["title"] = ""
form.data["last_edited_by"] = self.local_user.id
request = self.factory.post("", form.data)
request.user = self.local_user
result = view(request, self.book.id)
validate_html(result.render())
self.assertEqual(result.status_code, 200)
# Title is unchanged
self.book.refresh_from_db()
self.assertEqual(self.book.title, "Example Edition")
def test_edit_book_add_author(self): def test_edit_book_add_author(self):
"""lets a user edit a book with new authors""" """lets a user edit a book with new authors"""
view = views.EditBook.as_view() view = views.EditBook.as_view()
@ -234,3 +240,49 @@ class EditBookViews(TestCase):
self.assertEqual(len(result["author_matches"]), 2) self.assertEqual(len(result["author_matches"]), 2)
self.assertEqual(result["author_matches"][0]["name"], "Sappho") self.assertEqual(result["author_matches"][0]["name"], "Sappho")
self.assertEqual(result["author_matches"][1]["name"], "Some Guy") self.assertEqual(result["author_matches"][1]["name"], "Some Guy")
def test_create_book_get(self):
"""there are so many views, this just makes sure it LOADS"""
view = views.CreateBook.as_view()
request = self.factory.get("")
request.user = self.local_user
request.user.is_superuser = True
result = view(request)
self.assertIsInstance(result, TemplateResponse)
validate_html(result.render())
self.assertEqual(result.status_code, 200)
def test_create_book_post_existing_work(self):
"""Adding an edition to an existing work"""
author = models.Author.objects.create(name="Sappho")
view = views.CreateBook.as_view()
form = forms.EditionForm()
form.data["title"] = "A Title"
form.data["parent_work"] = self.work.id
form.data["authors"] = [author.id]
form.data["last_edited_by"] = self.local_user.id
request = self.factory.post("", form.data)
request.user = self.local_user
request.user.is_superuser = True
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
result = view(request)
self.assertEqual(result.status_code, 302)
new_edition = models.Edition.objects.get(title="A Title")
self.assertEqual(new_edition.parent_work, self.work)
self.assertEqual(new_edition.authors.first(), author)
def test_create_book_post_invalid(self):
"""book form is invalid"""
view = views.CreateBook.as_view()
self.local_user.groups.add(self.group)
form = forms.EditionForm(instance=self.book)
form.data["title"] = ""
form.data["last_edited_by"] = self.local_user.id
request = self.factory.post("", form.data)
request.user = self.local_user
result = view(request)
validate_html(result.render())
self.assertEqual(result.status_code, 200)

View file

@ -112,7 +112,17 @@ class ViewsHelpers(TestCase):
request = self.factory.get("", {"q": "Test Book"}, HTTP_USER_AGENT=USER_AGENT) request = self.factory.get("", {"q": "Test Book"}, HTTP_USER_AGENT=USER_AGENT)
self.assertTrue(views.helpers.is_bookwyrm_request(request)) self.assertTrue(views.helpers.is_bookwyrm_request(request))
def test_existing_user(self, *_): def test_handle_remote_webfinger_invalid(self, *_):
"""Various ways you can send a bad query"""
# if there's no query, there's no result
result = views.helpers.handle_remote_webfinger(None)
self.assertIsNone(result)
# malformed user
result = views.helpers.handle_remote_webfinger("noatsymbol")
self.assertIsNone(result)
def test_handle_remote_webfinger_existing_user(self, *_):
"""simple database lookup by username""" """simple database lookup by username"""
result = views.helpers.handle_remote_webfinger("@mouse@local.com") result = views.helpers.handle_remote_webfinger("@mouse@local.com")
self.assertEqual(result, self.local_user) self.assertEqual(result, self.local_user)
@ -124,7 +134,19 @@ class ViewsHelpers(TestCase):
self.assertEqual(result, self.local_user) self.assertEqual(result, self.local_user)
@responses.activate @responses.activate
def test_load_user(self, *_): def test_handle_remote_webfinger_load_user_invalid_result(self, *_):
"""find a remote user using webfinger, but fail"""
username = "mouse@example.com"
responses.add(
responses.GET,
f"https://example.com/.well-known/webfinger?resource=acct:{username}",
status=500,
)
result = views.helpers.handle_remote_webfinger("@mouse@example.com")
self.assertIsNone(result)
@responses.activate
def test_handle_remote_webfinger_load_user(self, *_):
"""find a remote user using webfinger""" """find a remote user using webfinger"""
username = "mouse@example.com" username = "mouse@example.com"
wellknown = { wellknown = {
@ -154,7 +176,7 @@ class ViewsHelpers(TestCase):
self.assertIsInstance(result, models.User) self.assertIsInstance(result, models.User)
self.assertEqual(result.username, "mouse@example.com") self.assertEqual(result.username, "mouse@example.com")
def test_user_on_blocked_server(self, *_): def test_handler_remote_webfinger_user_on_blocked_server(self, *_):
"""find a remote user using webfinger""" """find a remote user using webfinger"""
models.FederatedServer.objects.create( models.FederatedServer.objects.create(
server_name="example.com", status="blocked" server_name="example.com", status="blocked"
@ -163,6 +185,38 @@ class ViewsHelpers(TestCase):
result = views.helpers.handle_remote_webfinger("@mouse@example.com") result = views.helpers.handle_remote_webfinger("@mouse@example.com")
self.assertIsNone(result) self.assertIsNone(result)
@responses.activate
def test_subscribe_remote_webfinger(self, *_):
"""remote subscribe templates"""
query = "mouse@example.com"
response = {
"subject": f"acct:{query}",
"links": [
{
"rel": "self",
"type": "application/activity+json",
"href": "https://example.com/user/mouse",
"template": "hi",
},
{
"rel": "http://ostatus.org/schema/1.0/subscribe",
"type": "application/activity+json",
"href": "https://example.com/user/mouse",
"template": "hello",
},
],
}
responses.add(
responses.GET,
f"https://example.com/.well-known/webfinger?resource=acct:{query}",
json=response,
status=200,
)
template = views.helpers.subscribe_remote_webfinger(query)
self.assertEqual(template, "hello")
template = views.helpers.subscribe_remote_webfinger(f"@{query}")
self.assertEqual(template, "hello")
def test_handle_reading_status_to_read(self, *_): def test_handle_reading_status_to_read(self, *_):
"""posts shelve activities""" """posts shelve activities"""
shelf = self.local_user.shelf_set.get(identifier="to-read") shelf = self.local_user.shelf_set.get(identifier="to-read")

View file

@ -0,0 +1,109 @@
""" test for app action functionality """
from unittest.mock import patch
from django.test import TestCase
from django.test.client import RequestFactory
from bookwyrm import forms, models, views
from bookwyrm.tests.validate_html import validate_html
class ReportViews(TestCase):
"""every response to a get request, html or json"""
def setUp(self):
"""we need basic test data and mocks"""
self.factory = RequestFactory()
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
"bookwyrm.activitystreams.populate_stream_task.delay"
), patch("bookwyrm.lists_stream.populate_lists_task.delay"):
self.local_user = models.User.objects.create_user(
"mouse@local.com",
"mouse@mouse.mouse",
"password",
local=True,
localname="mouse",
)
self.rat = models.User.objects.create_user(
"rat@local.com",
"rat@mouse.mouse",
"password",
local=True,
localname="rat",
)
with patch(
"bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
), patch("bookwyrm.activitystreams.add_status_task.delay"):
self.status = models.Status.objects.create(
user=self.local_user,
content="Test status",
)
models.SiteSettings.objects.create()
def test_report_modal_view(self):
"""a user reports another user"""
request = self.factory.get("")
request.user = self.local_user
result = views.Report.as_view()(request, self.local_user.id)
validate_html(result.render())
def test_report_modal_view_with_status(self):
"""a user reports another user"""
request = self.factory.get("")
request.user = self.local_user
result = views.Report.as_view()(
request, user_id=self.local_user.id, status_id=self.status.id
)
validate_html(result.render())
def test_report_modal_view_with_link_domain(self):
"""a user reports another user"""
link = models.Link.objects.create(
url="http://example.com/hi",
added_by=self.local_user,
)
request = self.factory.get("")
request.user = self.local_user
result = views.Report.as_view()(request, link_id=link.id)
validate_html(result.render())
def test_make_report(self):
"""a user reports another user"""
form = forms.ReportForm()
form.data["reporter"] = self.local_user.id
form.data["user"] = self.rat.id
request = self.factory.post("", form.data)
request.user = self.local_user
views.Report.as_view()(request)
report = models.Report.objects.get()
self.assertEqual(report.reporter, self.local_user)
self.assertEqual(report.user, self.rat)
def test_report_link(self):
"""a user reports a link as spam"""
book = models.Edition.objects.create(title="hi")
link = models.FileLink.objects.create(
book=book, added_by=self.local_user, url="https://skdjfs.sdf"
)
domain = link.domain
domain.status = "approved"
domain.save()
form = forms.ReportForm()
form.data["reporter"] = self.local_user.id
form.data["user"] = self.rat.id
form.data["links"] = link.id
request = self.factory.post("", form.data)
request.user = self.local_user
views.Report.as_view()(request)
report = models.Report.objects.get()
domain.refresh_from_db()
self.assertEqual(report.links.first().id, link.id)
self.assertEqual(domain.status, "pending")

View file

@ -287,7 +287,7 @@ urlpatterns = [
name="report-status", name="report-status",
), ),
re_path( re_path(
r"^report/(?P<user_id>\d+)/link/(?P<link_id>\d+)?$", r"^report/link/(?P<link_id>\d+)?$",
views.Report.as_view(), views.Report.as_view(),
name="report-link", name="report-link",
), ),

View file

@ -33,8 +33,7 @@ class AutoMod(View):
def post(self, request): def post(self, request):
"""add rule""" """add rule"""
form = forms.AutoModRuleForm(request.POST) form = forms.AutoModRuleForm(request.POST)
success = form.is_valid() if form.is_valid():
if success:
form.save() form.save()
form = forms.AutoModRuleForm() form = forms.AutoModRuleForm()

View file

@ -77,6 +77,11 @@ class UserAdmin(View):
def post(self, request, user): def post(self, request, user):
"""update user group""" """update user group"""
user = get_object_or_404(models.User, id=user) user = get_object_or_404(models.User, id=user)
if request.POST.get("groups") == "":
user.groups.set([])
form = forms.UserGroupForm(instance=user)
else:
form = forms.UserGroupForm(request.POST, instance=user) form = forms.UserGroupForm(request.POST, instance=user)
if form.is_valid(): if form.is_valid():
form.save() form.save()

View file

@ -148,13 +148,6 @@ def handle_reading_status(user, shelf, book, privacy):
status.save() status.save()
def is_blocked(viewer, user):
"""is this viewer blocked by the user?"""
if viewer.is_authenticated and viewer in user.blocks.all():
return True
return False
def load_date_in_user_tz_as_utc(date_str: str, user: models.User) -> datetime: def load_date_in_user_tz_as_utc(date_str: str, user: models.User) -> datetime:
"""ensures that data is stored consistently in the UTC timezone""" """ensures that data is stored consistently in the UTC timezone"""
if not date_str: if not date_str:

View file

@ -13,9 +13,13 @@ from bookwyrm import emailing, forms, models
class Report(View): class Report(View):
"""Make reports""" """Make reports"""
def get(self, request, user_id, status_id=None, link_id=None): def get(self, request, user_id=None, status_id=None, link_id=None):
"""static view of report modal""" """static view of report modal"""
data = {"user": get_object_or_404(models.User, id=user_id)} data = {"user": None}
if user_id:
# but normally we should have an error if the user is not found
data["user"] = get_object_or_404(models.User, id=user_id)
if status_id: if status_id:
data["status"] = status_id data["status"] = status_id
if link_id: if link_id:

3
bw-dev
View file

@ -103,6 +103,9 @@ case "$CMD" in
pytest) pytest)
runweb pytest --no-cov-on-fail "$@" runweb pytest --no-cov-on-fail "$@"
;; ;;
pytest_coverage_report)
runweb pytest -n 3 --cov-report term-missing "$@"
;;
collectstatic) collectstatic)
runweb python manage.py collectstatic --no-input runweb python manage.py collectstatic --no-input
;; ;;

View file

@ -2,7 +2,7 @@ aiohttp==3.8.1
bleach==5.0.1 bleach==5.0.1
celery==5.2.2 celery==5.2.2
colorthief==0.2.1 colorthief==0.2.1
Django==3.2.13 Django==3.2.14
django-celery-beat==2.2.1 django-celery-beat==2.2.1
django-compressor==2.4.1 django-compressor==2.4.1
django-imagekit==4.1.0 django-imagekit==4.1.0