Make reports
This commit is contained in:
parent
e59c127686
commit
21f199c548
|
@ -231,3 +231,9 @@ class ListForm(CustomForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.List
|
model = models.List
|
||||||
fields = ["user", "name", "description", "curation", "privacy"]
|
fields = ["user", "name", "description", "curation", "privacy"]
|
||||||
|
|
||||||
|
|
||||||
|
class ReportForm(CustomForm):
|
||||||
|
class Meta:
|
||||||
|
model = models.Report
|
||||||
|
fields = ["user", "reporter", "statuses", "note"]
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
# Generated by Django 3.0.7 on 2021-03-09 00:55
|
# Generated by Django 3.0.7 on 2021-03-09 01:56
|
||||||
|
|
||||||
import bookwyrm.models.fields
|
import bookwyrm.models.fields
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
import django.db.models.expressions
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
@ -23,12 +24,9 @@ class Migration(migrations.Migration):
|
||||||
('note', models.TextField(blank=True, null=True)),
|
('note', models.TextField(blank=True, null=True)),
|
||||||
('resolved', models.BooleanField(default=False)),
|
('resolved', models.BooleanField(default=False)),
|
||||||
('reporter', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='reporter', to=settings.AUTH_USER_MODEL)),
|
('reporter', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='reporter', to=settings.AUTH_USER_MODEL)),
|
||||||
('statuses', models.ManyToManyField(to='bookwyrm.Status')),
|
('statuses', models.ManyToManyField(blank=True, null=True, to='bookwyrm.Status')),
|
||||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
||||||
],
|
],
|
||||||
options={
|
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='ReportComment',
|
name='ReportComment',
|
||||||
|
@ -45,4 +43,8 @@ class Migration(migrations.Migration):
|
||||||
'abstract': False,
|
'abstract': False,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
migrations.AddConstraint(
|
||||||
|
model_name='report',
|
||||||
|
constraint=models.CheckConstraint(check=models.Q(_negated=True, reporter=django.db.models.expressions.F('user')), name='self_report'),
|
||||||
|
),
|
||||||
]
|
]
|
|
@ -1,5 +1,6 @@
|
||||||
""" flagged for moderation """
|
""" flagged for moderation """
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models import F, Q
|
||||||
from .base_model import BookWyrmModel
|
from .base_model import BookWyrmModel
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,9 +12,17 @@ class Report(BookWyrmModel):
|
||||||
)
|
)
|
||||||
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)
|
||||||
statuses = models.ManyToManyField("Status")
|
statuses = models.ManyToManyField("Status", null=True, blank=True)
|
||||||
resolved = models.BooleanField(default=False)
|
resolved = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
""" don't let users report themselves """
|
||||||
|
constraints = [
|
||||||
|
models.CheckConstraint(
|
||||||
|
check=~Q(reporter=F('user')),
|
||||||
|
name='self_report'
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
class ReportComment(BookWyrmModel):
|
class ReportComment(BookWyrmModel):
|
||||||
""" updates on a report """
|
""" updates on a report """
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
{% extends 'settings/admin_layout.html' %}
|
||||||
|
{% load i18n %}
|
|
@ -2,7 +2,7 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block card-header %}
|
{% block card-header %}
|
||||||
<h2 class="title is-4">
|
<h2 class="title is-4">
|
||||||
<a href="{% url settings-report report.id %}">report title</a></h2>
|
<a href="{% url 'settings-report' report.id %}">report title</a></h2>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block card-content %}
|
{% block card-content %}
|
||||||
|
@ -11,4 +11,4 @@ about this report
|
||||||
|
|
||||||
{% block card-footer %}
|
{% block card-footer %}
|
||||||
footer
|
footer
|
||||||
{% endblock
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
{% load i18n %}
|
||||||
|
<form name="report" method="post" action="{% url 'report' %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="reporter" value="{{ request.user.id }}">
|
||||||
|
<input type="hidden" name="user" value="{{ user.id }}">
|
||||||
|
<button class="button is-danger is-light is-small {{ class }}" type="submit">{% trans "Report" %}</button>
|
||||||
|
</form>
|
|
@ -25,7 +25,7 @@
|
||||||
<a href="/direct-messages/{{ status.user|username }}" class="button is-fullwidth is-small">{% trans "Send direct message" %}</a>
|
<a href="/direct-messages/{{ status.user|username }}" class="button is-fullwidth is-small">{% trans "Send direct message" %}</a>
|
||||||
</li>
|
</li>
|
||||||
<li role="menuitem">
|
<li role="menuitem">
|
||||||
<a href="{{ status.local_path }}/report" class="button is-fullwidth is-small">{% trans "Report status" %}</a>
|
{% include 'snippets/report_button.html' with user=status.user class="is-fullwidth" %}
|
||||||
</li>
|
</li>
|
||||||
<li role="menuitem">
|
<li role="menuitem">
|
||||||
{% include 'snippets/block_button.html' with user=status.user class="is-fullwidth" %}
|
{% include 'snippets/block_button.html' with user=status.user class="is-fullwidth" %}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<a href="/direct-messages/{{ user|username }}" class="button is-fullwidth is-small">{% trans "Send direct message" %}</a>
|
<a href="/direct-messages/{{ user|username }}" class="button is-fullwidth is-small">{% trans "Send direct message" %}</a>
|
||||||
</li>
|
</li>
|
||||||
<li role="menuitem">
|
<li role="menuitem">
|
||||||
<a href="{{ user.local_path }}/report" class="button is-fullwidth is-small">{% trans "Report user" %}</a>
|
{% include 'snippets/report_button.html' with user=status.user class="is-fullwidth" %}
|
||||||
</li>
|
</li>
|
||||||
<li role="menuitem">
|
<li role="menuitem">
|
||||||
{% include 'snippets/block_button.html' with user=user class="is-fullwidth" %}
|
{% include 'snippets/block_button.html' with user=user class="is-fullwidth" %}
|
||||||
|
|
|
@ -3,8 +3,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 models
|
from bookwyrm import forms, models, views
|
||||||
from bookwyrm import views
|
|
||||||
|
|
||||||
|
|
||||||
class ReportViews(TestCase):
|
class ReportViews(TestCase):
|
||||||
|
@ -20,7 +19,7 @@ class ReportViews(TestCase):
|
||||||
local=True,
|
local=True,
|
||||||
localname="mouse",
|
localname="mouse",
|
||||||
)
|
)
|
||||||
self.local_user = models.User.objects.create_user(
|
self.rat = models.User.objects.create_user(
|
||||||
"rat@local.com",
|
"rat@local.com",
|
||||||
"rat@mouse.mouse",
|
"rat@mouse.mouse",
|
||||||
"password",
|
"password",
|
||||||
|
@ -35,6 +34,20 @@ class ReportViews(TestCase):
|
||||||
request = self.factory.get("")
|
request = self.factory.get("")
|
||||||
request.user = self.local_user
|
request.user = self.local_user
|
||||||
request.user.is_superuser = True
|
request.user.is_superuser = True
|
||||||
|
|
||||||
|
result = view(request)
|
||||||
|
self.assertIsInstance(result, TemplateResponse)
|
||||||
|
result.render()
|
||||||
|
self.assertEqual(result.status_code, 200)
|
||||||
|
|
||||||
|
def test_reports_page_with_data(self):
|
||||||
|
""" there are so many views, this just makes sure it LOADS """
|
||||||
|
view = views.Reports.as_view()
|
||||||
|
request = self.factory.get("")
|
||||||
|
request.user = self.local_user
|
||||||
|
request.user.is_superuser = True
|
||||||
|
report = models.Report.objects.create(reporter=self.local_user, user=self.rat)
|
||||||
|
|
||||||
result = view(request)
|
result = view(request)
|
||||||
self.assertIsInstance(result, TemplateResponse)
|
self.assertIsInstance(result, TemplateResponse)
|
||||||
result.render()
|
result.render()
|
||||||
|
@ -53,3 +66,17 @@ class ReportViews(TestCase):
|
||||||
self.assertIsInstance(result, TemplateResponse)
|
self.assertIsInstance(result, TemplateResponse)
|
||||||
result.render()
|
result.render()
|
||||||
self.assertEqual(result.status_code, 200)
|
self.assertEqual(result.status_code, 200)
|
||||||
|
|
||||||
|
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.make_report(request)
|
||||||
|
|
||||||
|
report = models.Report.objects.get()
|
||||||
|
self.assertEqual(report.reporter, self.local_user)
|
||||||
|
self.assertEqual(report.user, self.rat)
|
||||||
|
|
|
@ -54,14 +54,15 @@ urlpatterns = [
|
||||||
re_path(
|
re_path(
|
||||||
r"^settings/invites/?$", views.ManageInvites.as_view(), name="settings-invites"
|
r"^settings/invites/?$", views.ManageInvites.as_view(), name="settings-invites"
|
||||||
),
|
),
|
||||||
|
re_path(r"^invite/(?P<code>[A-Za-z0-9]+)/?$", views.Invite.as_view()),
|
||||||
# moderation
|
# moderation
|
||||||
re_path(r"^settings/reports/?$", views.Reports.as_view(), name="settings-reports"),
|
re_path(r"^settings/reports/?$", views.Reports.as_view(), name="settings-reports"),
|
||||||
re_path(
|
re_path(
|
||||||
r"^settings/report/(?P<report_id>\d+)/?$",
|
r"^settings/reports/(?P<report_id>\d+)/?$",
|
||||||
views.Report.as_view(),
|
views.Report.as_view(),
|
||||||
name="settings-report",
|
name="settings-report",
|
||||||
),
|
),
|
||||||
re_path(r"^invite/(?P<code>[A-Za-z0-9]+)/?$", views.Invite.as_view()),
|
re_path(r"^report/?$", views.make_report, name="report"),
|
||||||
# landing pages
|
# landing pages
|
||||||
re_path(r"^about/?$", views.About.as_view()),
|
re_path(r"^about/?$", views.About.as_view()),
|
||||||
path("", views.Home.as_view()),
|
path("", views.Home.as_view()),
|
||||||
|
|
|
@ -20,7 +20,7 @@ from .notifications import Notifications
|
||||||
from .outbox import Outbox
|
from .outbox import Outbox
|
||||||
from .reading import edit_readthrough, create_readthrough, delete_readthrough
|
from .reading import edit_readthrough, create_readthrough, delete_readthrough
|
||||||
from .reading import start_reading, finish_reading, delete_progressupdate
|
from .reading import start_reading, finish_reading, delete_progressupdate
|
||||||
from .reports import Report, Reports
|
from .reports import Report, Reports, make_report
|
||||||
from .rss_feed import RssFeed
|
from .rss_feed import RssFeed
|
||||||
from .password import PasswordResetRequest, PasswordReset, ChangePassword
|
from .password import PasswordResetRequest, PasswordReset, ChangePassword
|
||||||
from .search import Search
|
from .search import Search
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
""" moderation via flagged posts and users """
|
""" moderation via flagged posts and users """
|
||||||
from django.contrib.auth.decorators import login_required, permission_required
|
from django.contrib.auth.decorators import login_required, permission_required
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views import View
|
from django.views import View
|
||||||
|
from django.views.decorators.http import require_POST
|
||||||
|
|
||||||
from bookwyrm import models
|
from bookwyrm import forms, models
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
|
@ -23,7 +24,7 @@ class Reports(View):
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
""" view current reports """
|
""" view current reports """
|
||||||
resolved = request.GET.get("resolved")
|
resolved = request.GET.get("resolved", False)
|
||||||
data = {
|
data = {
|
||||||
"resolved": resolved,
|
"resolved": resolved,
|
||||||
"reports": models.Report.objects.filter(resolved=resolved),
|
"reports": models.Report.objects.filter(resolved=resolved),
|
||||||
|
@ -31,6 +32,15 @@ class Reports(View):
|
||||||
return TemplateResponse(request, "settings/reports.html", data)
|
return TemplateResponse(request, "settings/reports.html", data)
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(login_required, name="dispatch")
|
||||||
|
@method_decorator(
|
||||||
|
permission_required("bookwyrm.moderate_user", raise_exception=True),
|
||||||
|
name="dispatch",
|
||||||
|
)
|
||||||
|
@method_decorator(
|
||||||
|
permission_required("bookwyrm.moderate_post", raise_exception=True),
|
||||||
|
name="dispatch",
|
||||||
|
)
|
||||||
class Report(View):
|
class Report(View):
|
||||||
""" view a specific report """
|
""" view a specific report """
|
||||||
|
|
||||||
|
@ -38,3 +48,16 @@ class Report(View):
|
||||||
""" load a report """
|
""" load a report """
|
||||||
data = {"report": get_object_or_404(models.Report, id=report_id)}
|
data = {"report": get_object_or_404(models.Report, id=report_id)}
|
||||||
return TemplateResponse(request, "settings/report.html", data)
|
return TemplateResponse(request, "settings/report.html", data)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@require_POST
|
||||||
|
def make_report(request):
|
||||||
|
""" a user reports something """
|
||||||
|
form = forms.ReportForm(request.POST)
|
||||||
|
if not form.is_valid():
|
||||||
|
print(form.errors)
|
||||||
|
return redirect(request.headers.get("Referer", "/"))
|
||||||
|
|
||||||
|
form.save()
|
||||||
|
return redirect(request.headers.get("Referer", "/"))
|
||||||
|
|
Loading…
Reference in New Issue