mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-10-31 22:19:00 +00:00
Merge pull request #2854 from bookwyrm-social/report-actions
Record report actions
This commit is contained in:
commit
15e82ece07
18 changed files with 313 additions and 70 deletions
36
bookwyrm/migrations/0179_reportcomment_comment_type.py
Normal file
36
bookwyrm/migrations/0179_reportcomment_comment_type.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
# Generated by Django 3.2.18 on 2023-05-16 16:02
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("bookwyrm", "0178_auto_20230328_2132"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="reportcomment",
|
||||
name="action_type",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("comment", "Comment"),
|
||||
("resolve", "Resolved report"),
|
||||
("reopen", "Re-opened report"),
|
||||
("message_reporter", "Messaged reporter"),
|
||||
("message_offender", "Messaged reported user"),
|
||||
("user_suspension", "Suspended user"),
|
||||
("user_unsuspension", "Un-suspended user"),
|
||||
("user_perms", "Changed user permission level"),
|
||||
("user_deletion", "Deleted user account"),
|
||||
("block_domain", "Blocked domain"),
|
||||
("approve_domain", "Approved domain"),
|
||||
("delete_item", "Deleted item"),
|
||||
],
|
||||
default="comment",
|
||||
max_length=20,
|
||||
),
|
||||
),
|
||||
migrations.RenameModel("ReportComment", "ReportAction"),
|
||||
]
|
17
bookwyrm/migrations/0180_alter_reportaction_options.py
Normal file
17
bookwyrm/migrations/0180_alter_reportaction_options.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 3.2.18 on 2023-06-21 22:01
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("bookwyrm", "0179_reportcomment_comment_type"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="reportaction",
|
||||
options={"ordering": ("created_date",)},
|
||||
),
|
||||
]
|
13
bookwyrm/migrations/0181_merge_20230806_2302.py
Normal file
13
bookwyrm/migrations/0181_merge_20230806_2302.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Generated by Django 3.2.20 on 2023-08-06 23:02
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("bookwyrm", "0180_alter_reportaction_options"),
|
||||
("bookwyrm", "0180_alter_user_preferred_language"),
|
||||
]
|
||||
|
||||
operations = []
|
|
@ -20,7 +20,7 @@ from .readthrough import ReadThrough, ProgressUpdate, ProgressMode
|
|||
from .user import User, KeyPair
|
||||
from .annual_goal import AnnualGoal
|
||||
from .relationship import UserFollows, UserFollowRequest, UserBlocks
|
||||
from .report import Report, ReportComment
|
||||
from .report import Report, ReportAction
|
||||
from .federated_server import FederatedServer
|
||||
|
||||
from .group import Group, GroupMember, GroupMemberInvitation
|
||||
|
|
|
@ -1,11 +1,27 @@
|
|||
""" flagged for moderation """
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from bookwyrm.settings import DOMAIN
|
||||
from .base_model import BookWyrmModel
|
||||
|
||||
|
||||
# Report action enums
|
||||
COMMENT = "comment"
|
||||
RESOLVE = "resolve"
|
||||
REOPEN = "reopen"
|
||||
MESSAGE_REPORTER = "message_reporter"
|
||||
MESSAGE_OFFENDER = "message_offender"
|
||||
USER_SUSPENSION = "user_suspension"
|
||||
USER_UNSUSPENSION = "user_unsuspension"
|
||||
USER_DELETION = "user_deletion"
|
||||
USER_PERMS = "user_perms"
|
||||
BLOCK_DOMAIN = "block_domain"
|
||||
APPROVE_DOMAIN = "approve_domain"
|
||||
DELETE_ITEM = "delete_item"
|
||||
|
||||
|
||||
class Report(BookWyrmModel):
|
||||
"""reported status or user"""
|
||||
|
||||
|
@ -32,20 +48,65 @@ class Report(BookWyrmModel):
|
|||
def get_remote_id(self):
|
||||
return f"https://{DOMAIN}/settings/reports/{self.id}"
|
||||
|
||||
def comment(self, user, note):
|
||||
"""comment on a report"""
|
||||
ReportAction.objects.create(
|
||||
action_type=COMMENT, user=user, note=note, report=self
|
||||
)
|
||||
|
||||
def resolve(self, user):
|
||||
"""Mark a report as complete"""
|
||||
self.resolved = True
|
||||
self.save()
|
||||
ReportAction.objects.create(action_type=RESOLVE, user=user, report=self)
|
||||
|
||||
def reopen(self, user):
|
||||
"""Wait! This report isn't complete after all"""
|
||||
self.resolved = False
|
||||
self.save()
|
||||
ReportAction.objects.create(action_type=REOPEN, user=user, report=self)
|
||||
|
||||
@classmethod
|
||||
def record_action(cls, report_id: int, action: str, user):
|
||||
"""Note that someone did something"""
|
||||
if not report_id:
|
||||
return
|
||||
report = cls.objects.get(id=report_id)
|
||||
ReportAction.objects.create(action_type=action, user=user, report=report)
|
||||
|
||||
class Meta:
|
||||
"""set order by default"""
|
||||
|
||||
ordering = ("-created_date",)
|
||||
|
||||
|
||||
class ReportComment(BookWyrmModel):
|
||||
ReportActionTypes = [
|
||||
(COMMENT, _("Comment")),
|
||||
(RESOLVE, _("Resolved report")),
|
||||
(REOPEN, _("Re-opened report")),
|
||||
(MESSAGE_REPORTER, _("Messaged reporter")),
|
||||
(MESSAGE_OFFENDER, _("Messaged reported user")),
|
||||
(USER_SUSPENSION, _("Suspended user")),
|
||||
(USER_UNSUSPENSION, _("Un-suspended user")),
|
||||
(USER_PERMS, _("Changed user permission level")),
|
||||
(USER_DELETION, _("Deleted user account")),
|
||||
(BLOCK_DOMAIN, _("Blocked domain")),
|
||||
(APPROVE_DOMAIN, _("Approved domain")),
|
||||
(DELETE_ITEM, _("Deleted item")),
|
||||
]
|
||||
|
||||
|
||||
class ReportAction(BookWyrmModel):
|
||||
"""updates on a report"""
|
||||
|
||||
user = models.ForeignKey("User", on_delete=models.PROTECT)
|
||||
action_type = models.CharField(
|
||||
max_length=20, blank=False, default="comment", choices=ReportActionTypes
|
||||
)
|
||||
note = models.TextField()
|
||||
report = models.ForeignKey(Report, on_delete=models.PROTECT)
|
||||
|
||||
class Meta:
|
||||
"""sort comments"""
|
||||
|
||||
ordering = ("-created_date",)
|
||||
ordering = ("created_date",)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{% extends 'settings/layout.html' %}
|
||||
{% load i18n %}
|
||||
{% load humanize %}
|
||||
{% load utilities %}
|
||||
{% load feed_page_tags %}
|
||||
|
||||
{% block title %}
|
||||
|
@ -21,7 +22,7 @@
|
|||
<div class="block">
|
||||
<details class="details-panel box">
|
||||
<summary>
|
||||
<span class="title is-4">{% trans "Message reporter" %}</span>
|
||||
<span class="title is-6">{% trans "Message reporter" %}</span>
|
||||
<span class="details-close icon icon-x" aria-hidden="true"></span>
|
||||
</summary>
|
||||
<div class="box">
|
||||
|
@ -61,30 +62,59 @@
|
|||
{% include 'settings/users/user_moderation_actions.html' with user=report.user %}
|
||||
{% endif %}
|
||||
|
||||
<div class="block">
|
||||
<h3 class="title is-4">{% trans "Moderator Comments" %}</h3>
|
||||
{% for comment in report.reportcomment_set.all %}
|
||||
<div class="card block">
|
||||
<p class="card-content">{{ comment.note }}</p>
|
||||
<div class="card-footer">
|
||||
<div class="card-footer-item">
|
||||
<a href="{{ comment.user.local_path }}">{{ comment.user.display_name }}</a>
|
||||
<div class="block content">
|
||||
<h3 class="title is-4">{% trans "Moderation Activity" %}</h3>
|
||||
|
||||
<div class="box">
|
||||
<ul class="mt-0">
|
||||
<li class="mb-2">
|
||||
<div class="is-flex">
|
||||
<p class="mb-0 is-flex-grow-1">
|
||||
{% blocktrans trimmed with user=report.reporter|username user_link=report.reporter.local_path %}
|
||||
<a href="{{ user_link }}">{{ user}}</a> opened this report
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<span class="tag">{{ report.created_date }}</span>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
{% for comment in report.reportaction_set.all %}
|
||||
<li class="mb-2">
|
||||
<div class="is-flex">
|
||||
<p class="mb-0 is-flex-grow-1">
|
||||
{% if comment.action_type == "comment" %}
|
||||
{% blocktrans trimmed with user=comment.user|username user_link=comment.user.local_path %}
|
||||
<a href="{{ user_link }}">{{ user}}</a> commented on this report:
|
||||
{% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans trimmed with user=comment.user|username user_link=comment.user.local_path %}
|
||||
<a href="{{ user_link }}">{{ user}}</a> took an action on this report:
|
||||
{% endblocktrans %}
|
||||
<span class="has-text-weight-bold">
|
||||
{{ comment.get_action_type_display }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
<span class="tag">{{ comment.created_date }}</span>
|
||||
</div>
|
||||
|
||||
{% if comment.note %}
|
||||
<blockquote>{{ comment.note }}</blockquote>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<form class="block" name="report-comment" method="post" action="{% url 'settings-report' report.id %}">
|
||||
{% csrf_token %}
|
||||
<div class="field">
|
||||
<label for="report_comment" class="label">Comment on report</label>
|
||||
<textarea name="note" id="report_comment" class="textarea"></textarea>
|
||||
</div>
|
||||
<div class="card-footer-item">
|
||||
{{ comment.created_date|naturaltime }}
|
||||
<div class="field">
|
||||
<button class="button">{% trans "Comment" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<form class="block" name="report-comment" method="post" action="{% url 'settings-report' report.id %}">
|
||||
{% csrf_token %}
|
||||
<div class="field">
|
||||
<label for="report_comment" class="label">Comment on report</label>
|
||||
<textarea name="note" id="report_comment" class="textarea"></textarea>
|
||||
</div>
|
||||
<div class="field">
|
||||
<button class="button">{% trans "Comment" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -13,8 +13,18 @@
|
|||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<form>
|
||||
{% if link.domain.status != "approved" %}
|
||||
<form method="POST" action="{% url 'settings-link-domain-status' link.domain.id 'approved' report.id %}">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="button is-success is-light">{% trans "Approve domain" %}</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
{% if link.domain.status != "blocked" %}
|
||||
<form method="POST" action="{% url 'settings-link-domain-status' link.domain.id 'blocked' report.id %}">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="button is-danger is-light">{% trans "Block domain" %}</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endblock %}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block form %}
|
||||
<form name="delete-user" action="{% url 'settings-delete-user' user.id %}" method="post">
|
||||
<form name="delete-user" action="{% url 'settings-delete-user' user.id report.id %}" method="post">
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
{% blocktrans trimmed with username=user.localname %}
|
||||
|
|
|
@ -22,12 +22,12 @@
|
|||
</form>
|
||||
{% endif %}
|
||||
{% if user.is_active or user.deactivation_reason == "pending" %}
|
||||
<form name="suspend" method="post" action="{% url 'settings-report-suspend' user.id %}" class="mr-1">
|
||||
<form name="suspend" method="post" action="{% url 'settings-report-suspend' user.id report.id %}" class="mr-1">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="button is-danger is-light">{% trans "Suspend user" %}</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<form name="unsuspend" method="post" action="{% url 'settings-report-unsuspend' user.id %}" class="mr-1">
|
||||
<form name="unsuspend" method="post" action="{% url 'settings-report-unsuspend' user.id report.id %}" class="mr-1">
|
||||
{% csrf_token %}
|
||||
<button class="button">{% trans "Un-suspend user" %}</button>
|
||||
</form>
|
||||
|
@ -49,7 +49,7 @@
|
|||
|
||||
{% if user.local %}
|
||||
<div>
|
||||
<form name="permission" method="post" action="{% url 'settings-user' user.id %}">
|
||||
<form name="permission" method="post" action="{% url 'settings-user' user.id report.id %}">
|
||||
{% csrf_token %}
|
||||
<label class="label" for="id_user_group">{% trans "Access level:" %}</label>
|
||||
{% if group_form.non_field_errors %}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
<div class="card-footer-item">
|
||||
{# moderation options #}
|
||||
<form name="delete-{{ status.id }}" action="/delete-status/{{ status.id }}" method="post">
|
||||
<form name="delete-{{ status.id }}" action="/delete-status/{{ status.id }}/{{ report.id }}" method="post">
|
||||
{% csrf_token %}
|
||||
<button class="button is-danger is-light" type="submit">
|
||||
{% trans "Delete status" %}
|
||||
|
|
|
@ -78,8 +78,8 @@ class ReportViews(TestCase):
|
|||
validate_html(result.render())
|
||||
self.assertEqual(result.status_code, 200)
|
||||
|
||||
def test_report_comment(self):
|
||||
"""comment on a report"""
|
||||
def test_report_action(self):
|
||||
"""action on a report"""
|
||||
view = views.ReportAdmin.as_view()
|
||||
request = self.factory.post("", {"note": "hi"})
|
||||
request.user = self.local_user
|
||||
|
@ -87,15 +87,17 @@ class ReportViews(TestCase):
|
|||
|
||||
view(request, report.id)
|
||||
|
||||
comment = models.ReportComment.objects.get()
|
||||
self.assertEqual(comment.user, self.local_user)
|
||||
self.assertEqual(comment.note, "hi")
|
||||
self.assertEqual(comment.report, report)
|
||||
action = models.ReportAction.objects.get()
|
||||
self.assertEqual(action.user, self.local_user)
|
||||
self.assertEqual(action.note, "hi")
|
||||
self.assertEqual(action.report, report)
|
||||
self.assertEqual(action.action_type, "comment")
|
||||
|
||||
def test_resolve_report(self):
|
||||
"""toggle report resolution status"""
|
||||
report = models.Report.objects.create(reporter=self.local_user, user=self.rat)
|
||||
self.assertFalse(report.resolved)
|
||||
self.assertFalse(models.ReportAction.objects.exists())
|
||||
request = self.factory.post("")
|
||||
request.user = self.local_user
|
||||
|
||||
|
@ -104,11 +106,25 @@ class ReportViews(TestCase):
|
|||
report.refresh_from_db()
|
||||
self.assertTrue(report.resolved)
|
||||
|
||||
# check that the action was noted
|
||||
self.assertTrue(
|
||||
models.ReportAction.objects.filter(
|
||||
report=report, action_type="resolve", user=self.local_user
|
||||
).exists()
|
||||
)
|
||||
|
||||
# un-resolve
|
||||
views.resolve_report(request, report.id)
|
||||
report.refresh_from_db()
|
||||
self.assertFalse(report.resolved)
|
||||
|
||||
# check that the action was noted
|
||||
self.assertTrue(
|
||||
models.ReportAction.objects.filter(
|
||||
report=report, action_type="reopen", user=self.local_user
|
||||
).exists()
|
||||
)
|
||||
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
@patch("bookwyrm.activitystreams.populate_stream_task.delay")
|
||||
@patch("bookwyrm.suggested_users.remove_user_task.delay")
|
||||
|
|
|
@ -7,6 +7,7 @@ from django.test import TestCase
|
|||
from django.test.client import RequestFactory
|
||||
|
||||
from bookwyrm import models, views
|
||||
from bookwyrm.models.report import USER_PERMS
|
||||
from bookwyrm.management.commands import initdb
|
||||
from bookwyrm.tests.validate_html import validate_html
|
||||
|
||||
|
@ -79,3 +80,37 @@ class UserAdminViews(TestCase):
|
|||
self.assertEqual(
|
||||
list(self.local_user.groups.values_list("name", flat=True)), ["editor"]
|
||||
)
|
||||
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
@patch("bookwyrm.activitystreams.populate_stream_task.delay")
|
||||
@patch("bookwyrm.suggested_users.remove_user_task.delay")
|
||||
def test_user_admin_page_post_with_report(self, *_):
|
||||
"""set the user's group"""
|
||||
group = Group.objects.get(name="editor")
|
||||
self.assertEqual(
|
||||
list(self.local_user.groups.values_list("name", flat=True)), ["moderator"]
|
||||
)
|
||||
|
||||
report = models.Report.objects.create(
|
||||
user=self.local_user, reporter=self.local_user
|
||||
)
|
||||
|
||||
view = views.UserAdmin.as_view()
|
||||
request = self.factory.post("", {"groups": [group.id]})
|
||||
request.user = self.local_user
|
||||
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
|
||||
result = view(request, self.local_user.id, report.id)
|
||||
|
||||
self.assertIsInstance(result, TemplateResponse)
|
||||
validate_html(result.render())
|
||||
|
||||
self.assertEqual(
|
||||
list(self.local_user.groups.values_list("name", flat=True)), ["editor"]
|
||||
)
|
||||
# make sure a report action was created
|
||||
self.assertTrue(
|
||||
models.ReportAction.objects.filter(
|
||||
report=report, action_type=USER_PERMS
|
||||
).exists()
|
||||
)
|
||||
|
|
|
@ -141,12 +141,12 @@ urlpatterns = [
|
|||
name="settings-users",
|
||||
),
|
||||
re_path(
|
||||
r"^settings/users/(?P<user>\d+)/?$",
|
||||
r"^settings/users/(?P<user_id>\d+)/(?P<report_id>\d+)?$",
|
||||
views.UserAdmin.as_view(),
|
||||
name="settings-user",
|
||||
),
|
||||
re_path(
|
||||
r"^settings/users/(?P<user>\d+)/activate/?$",
|
||||
r"^settings/users/(?P<user_id>\d+)/activate/?$",
|
||||
views.ActivateUserAdmin.as_view(),
|
||||
name="settings-activate-user",
|
||||
),
|
||||
|
@ -231,7 +231,7 @@ urlpatterns = [
|
|||
name="settings-link-domain",
|
||||
),
|
||||
re_path(
|
||||
r"^setting/link-domains/(?P<domain_id>\d+)/(?P<status>(pending|approved|blocked))/?$",
|
||||
r"^setting/link-domains/(?P<domain_id>\d+)/(?P<status>(pending|approved|blocked))/(?P<report_id>\d+)?$",
|
||||
views.update_domain_status,
|
||||
name="settings-link-domain-status",
|
||||
),
|
||||
|
@ -275,17 +275,17 @@ urlpatterns = [
|
|||
name="settings-report",
|
||||
),
|
||||
re_path(
|
||||
r"^settings/reports/(?P<user_id>\d+)/suspend/?$",
|
||||
r"^settings/reports/(?P<user_id>\d+)/suspend/(?P<report_id>\d+)?$",
|
||||
views.suspend_user,
|
||||
name="settings-report-suspend",
|
||||
),
|
||||
re_path(
|
||||
r"^settings/reports/(?P<user_id>\d+)/unsuspend/?$",
|
||||
r"^settings/reports/(?P<user_id>\d+)/unsuspend/(?P<report_id>\d+)?$",
|
||||
views.unsuspend_user,
|
||||
name="settings-report-unsuspend",
|
||||
),
|
||||
re_path(
|
||||
r"^settings/reports/(?P<user_id>\d+)/delete/?$",
|
||||
r"^settings/reports/(?P<user_id>\d+)/delete/(?P<report_id>\d+)?$",
|
||||
views.moderator_delete_user,
|
||||
name="settings-delete-user",
|
||||
),
|
||||
|
@ -633,7 +633,7 @@ urlpatterns = [
|
|||
name="create-status",
|
||||
),
|
||||
re_path(
|
||||
r"^delete-status/(?P<status_id>\d+)/?$",
|
||||
r"^delete-status/(?P<status_id>\d+)/?(?P<report_id>\d+)?$",
|
||||
views.DeleteStatus.as_view(),
|
||||
name="delete-status",
|
||||
),
|
||||
|
|
|
@ -7,6 +7,8 @@ from django.views import View
|
|||
from django.views.decorators.http import require_POST
|
||||
|
||||
from bookwyrm import forms, models
|
||||
from bookwyrm.models.report import APPROVE_DOMAIN, BLOCK_DOMAIN
|
||||
from bookwyrm.views.helpers import redirect_to_referer
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
|
@ -46,11 +48,17 @@ class LinkDomain(View):
|
|||
@require_POST
|
||||
@login_required
|
||||
@permission_required("bookwyrm.moderate_user")
|
||||
def update_domain_status(request, domain_id, status):
|
||||
def update_domain_status(request, domain_id, status, report_id=None):
|
||||
"""This domain seems fine"""
|
||||
domain = get_object_or_404(models.LinkDomain, id=domain_id)
|
||||
domain.raise_not_editable(request.user)
|
||||
|
||||
domain.status = status
|
||||
domain.save()
|
||||
return redirect("settings-link-domain", status="pending")
|
||||
|
||||
if status == "approved":
|
||||
models.Report.record_action(report_id, APPROVE_DOMAIN, request.user)
|
||||
elif status == "blocked":
|
||||
models.Report.record_action(report_id, BLOCK_DOMAIN, request.user)
|
||||
|
||||
return redirect_to_referer(request, "settings-link-domain", status="pending")
|
||||
|
|
|
@ -8,6 +8,7 @@ from django.utils.decorators import method_decorator
|
|||
from django.views import View
|
||||
|
||||
from bookwyrm import forms, models
|
||||
from bookwyrm.models.report import USER_SUSPENSION, USER_UNSUSPENSION, USER_DELETION
|
||||
from bookwyrm.views.helpers import redirect_to_referer
|
||||
from bookwyrm.settings import PAGE_LENGTH
|
||||
|
||||
|
@ -81,41 +82,42 @@ class ReportAdmin(View):
|
|||
def post(self, request, report_id):
|
||||
"""comment on a report"""
|
||||
report = get_object_or_404(models.Report, id=report_id)
|
||||
models.ReportComment.objects.create(
|
||||
user=request.user,
|
||||
report=report,
|
||||
note=request.POST.get("note"),
|
||||
)
|
||||
note = request.POST.get("note")
|
||||
report.comment(request.user, note)
|
||||
return redirect("settings-report", report.id)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("bookwyrm.moderate_user")
|
||||
def suspend_user(request, user_id):
|
||||
def suspend_user(request, user_id, report_id=None):
|
||||
"""mark an account as inactive"""
|
||||
user = get_object_or_404(models.User, id=user_id)
|
||||
user.is_active = False
|
||||
user.deactivation_reason = "moderator_suspension"
|
||||
# this isn't a full deletion, so we don't want to tell the world
|
||||
user.save(broadcast=False)
|
||||
|
||||
models.Report.record_action(report_id, USER_SUSPENSION, request.user)
|
||||
return redirect_to_referer(request, "settings-user", user.id)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("bookwyrm.moderate_user")
|
||||
def unsuspend_user(request, user_id):
|
||||
def unsuspend_user(request, user_id, report_id=None):
|
||||
"""mark an account as inactive"""
|
||||
user = get_object_or_404(models.User, id=user_id)
|
||||
user.is_active = True
|
||||
user.deactivation_reason = None
|
||||
# this isn't a full deletion, so we don't want to tell the world
|
||||
user.save(broadcast=False)
|
||||
|
||||
models.Report.record_action(report_id, USER_UNSUSPENSION, request.user)
|
||||
return redirect_to_referer(request, "settings-user", user.id)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("bookwyrm.moderate_user")
|
||||
def moderator_delete_user(request, user_id):
|
||||
def moderator_delete_user(request, user_id, report_id=None):
|
||||
"""permanently delete a user"""
|
||||
user = get_object_or_404(models.User, id=user_id)
|
||||
|
||||
|
@ -130,6 +132,9 @@ def moderator_delete_user(request, user_id):
|
|||
if form.is_valid() and moderator.check_password(form.cleaned_data["password"]):
|
||||
user.deactivation_reason = "moderator_deletion"
|
||||
user.delete()
|
||||
|
||||
# make a note of the fact that we did this
|
||||
models.Report.record_action(report_id, USER_DELETION, request.user)
|
||||
return redirect_to_referer(request, "settings-user", user.id)
|
||||
|
||||
form.errors["password"] = ["Invalid password"]
|
||||
|
@ -140,11 +145,12 @@ def moderator_delete_user(request, user_id):
|
|||
|
||||
@login_required
|
||||
@permission_required("bookwyrm.moderate_post")
|
||||
def resolve_report(_, report_id):
|
||||
def resolve_report(request, report_id):
|
||||
"""mark a report as (un)resolved"""
|
||||
report = get_object_or_404(models.Report, id=report_id)
|
||||
report.resolved = not report.resolved
|
||||
report.save()
|
||||
if not report.resolved:
|
||||
if report.resolved:
|
||||
report.reopen(request.user)
|
||||
return redirect("settings-report", report.id)
|
||||
|
||||
report.resolve(request.user)
|
||||
return redirect("settings-reports")
|
||||
|
|
|
@ -7,6 +7,7 @@ from django.utils.decorators import method_decorator
|
|||
from django.views import View
|
||||
|
||||
from bookwyrm import forms, models
|
||||
from bookwyrm.models.report import USER_PERMS
|
||||
from bookwyrm.settings import PAGE_LENGTH
|
||||
|
||||
|
||||
|
@ -76,15 +77,16 @@ class UserAdminList(View):
|
|||
class UserAdmin(View):
|
||||
"""moderate an individual user"""
|
||||
|
||||
def get(self, request, user):
|
||||
# pylint: disable=unused-argument
|
||||
def get(self, request, user_id, report_id=None):
|
||||
"""user view"""
|
||||
user = get_object_or_404(models.User, id=user)
|
||||
user = get_object_or_404(models.User, id=user_id)
|
||||
data = {"user": user, "group_form": forms.UserGroupForm()}
|
||||
return TemplateResponse(request, "settings/users/user.html", data)
|
||||
|
||||
def post(self, request, user):
|
||||
def post(self, request, user_id, report_id=None):
|
||||
"""update user group"""
|
||||
user = get_object_or_404(models.User, id=user)
|
||||
user = get_object_or_404(models.User, id=user_id)
|
||||
|
||||
if request.POST.get("groups") == "":
|
||||
user.groups.set([])
|
||||
|
@ -93,6 +95,10 @@ class UserAdmin(View):
|
|||
form = forms.UserGroupForm(request.POST, instance=user)
|
||||
if form.is_valid():
|
||||
form.save(request)
|
||||
|
||||
if report_id:
|
||||
models.Report.record_action(report_id, USER_PERMS, request.user)
|
||||
|
||||
data = {"user": user, "group_form": form}
|
||||
return TemplateResponse(request, "settings/users/user.html", data)
|
||||
|
||||
|
@ -106,8 +112,8 @@ class ActivateUserAdmin(View):
|
|||
"""activate a user manually"""
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def post(self, request, user):
|
||||
def post(self, request, user_id):
|
||||
"""activate user"""
|
||||
user = get_object_or_404(models.User, id=user)
|
||||
user = get_object_or_404(models.User, id=user_id)
|
||||
user.reactivate()
|
||||
return redirect("settings-user", user.id)
|
||||
|
|
|
@ -222,7 +222,7 @@ def maybe_redirect_local_path(request, model):
|
|||
return redirect(new_path, permanent=True)
|
||||
|
||||
|
||||
def redirect_to_referer(request, *args):
|
||||
def redirect_to_referer(request, *args, **kwargs):
|
||||
"""Redirect to the referrer, if it's in our domain, with get params"""
|
||||
# make sure the refer is part of this instance
|
||||
validated = validate_url_domain(request.META.get("HTTP_REFERER"))
|
||||
|
@ -231,4 +231,4 @@ def redirect_to_referer(request, *args):
|
|||
return redirect(validated)
|
||||
|
||||
# if not, use the args passed you'd normally pass to redirect()
|
||||
return redirect(*args or "/")
|
||||
return redirect(*args or "/", **kwargs)
|
||||
|
|
|
@ -9,7 +9,7 @@ from django.core.exceptions import ValidationError
|
|||
from django.db import transaction
|
||||
from django.db.models import Q
|
||||
from django.http import HttpResponse, HttpResponseBadRequest, Http404
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils import timezone
|
||||
from django.utils.decorators import method_decorator
|
||||
|
@ -18,6 +18,7 @@ from django.views.decorators.http import require_POST
|
|||
|
||||
from markdown import markdown
|
||||
from bookwyrm import forms, models
|
||||
from bookwyrm.models.report import DELETE_ITEM
|
||||
from bookwyrm.utils import regex, sanitizer
|
||||
from .helpers import handle_remote_webfinger, is_api_request
|
||||
from .helpers import load_date_in_user_tz_as_utc, redirect_to_referer
|
||||
|
@ -167,7 +168,7 @@ def format_hashtags(content, hashtags):
|
|||
class DeleteStatus(View):
|
||||
"""tombstone that bad boy"""
|
||||
|
||||
def post(self, request, status_id):
|
||||
def post(self, request, status_id, report_id=None):
|
||||
"""delete and tombstone a status"""
|
||||
status = get_object_or_404(models.Status, id=status_id)
|
||||
|
||||
|
@ -176,7 +177,11 @@ class DeleteStatus(View):
|
|||
|
||||
# perform deletion
|
||||
status.delete()
|
||||
return redirect("/")
|
||||
# record deletion if it's related to a report
|
||||
if report_id:
|
||||
models.Report.record_action(report_id, DELETE_ITEM, request.user)
|
||||
|
||||
return redirect_to_referer(request, "/")
|
||||
|
||||
|
||||
@login_required
|
||||
|
|
Loading…
Reference in a new issue