diff --git a/.gitignore b/.gitignore index 1384056f..4b5b7fef 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /venv *.pyc *.swp +**/__pycache__ # VSCode /.vscode @@ -15,4 +16,4 @@ /images/ # Testing -.coverage \ No newline at end of file +.coverage diff --git a/bookwyrm/forms.py b/bookwyrm/forms.py index be6c7df8..c6dfa9fe 100644 --- a/bookwyrm/forms.py +++ b/bookwyrm/forms.py @@ -231,3 +231,9 @@ class ListForm(CustomForm): class Meta: model = models.List fields = ["user", "name", "description", "curation", "privacy"] + + +class ReportForm(CustomForm): + class Meta: + model = models.Report + fields = ["user", "reporter", "statuses", "note"] diff --git a/bookwyrm/migrations/0049_auto_20210309_0156.py b/bookwyrm/migrations/0049_auto_20210309_0156.py new file mode 100644 index 00000000..ae9d77a8 --- /dev/null +++ b/bookwyrm/migrations/0049_auto_20210309_0156.py @@ -0,0 +1,113 @@ +# Generated by Django 3.0.7 on 2021-03-09 01:56 + +import bookwyrm.models.fields +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.db.models.expressions + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0048_merge_20210308_1754"), + ] + + operations = [ + migrations.CreateModel( + name="Report", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_date", models.DateTimeField(auto_now_add=True)), + ("updated_date", models.DateTimeField(auto_now=True)), + ( + "remote_id", + bookwyrm.models.fields.RemoteIdField( + max_length=255, + null=True, + validators=[bookwyrm.models.fields.validate_remote_id], + ), + ), + ("note", models.TextField(blank=True, null=True)), + ("resolved", models.BooleanField(default=False)), + ( + "reporter", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="reporter", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "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, + ), + ), + ], + ), + migrations.CreateModel( + name="ReportComment", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_date", models.DateTimeField(auto_now_add=True)), + ("updated_date", models.DateTimeField(auto_now=True)), + ( + "remote_id", + bookwyrm.models.fields.RemoteIdField( + max_length=255, + null=True, + validators=[bookwyrm.models.fields.validate_remote_id], + ), + ), + ("note", models.TextField()), + ( + "report", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to="bookwyrm.Report", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "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", + ), + ), + ] diff --git a/bookwyrm/models/__init__.py b/bookwyrm/models/__init__.py index 67ee16d3..326a673e 100644 --- a/bookwyrm/models/__init__.py +++ b/bookwyrm/models/__init__.py @@ -21,6 +21,7 @@ from .tag import Tag, UserTag from .user import User, KeyPair, AnnualGoal from .relationship import UserFollows, UserFollowRequest, UserBlocks +from .report import Report, ReportComment from .federated_server import FederatedServer from .import_job import ImportJob, ImportItem diff --git a/bookwyrm/models/federated_server.py b/bookwyrm/models/federated_server.py index ce804310..8f7d903e 100644 --- a/bookwyrm/models/federated_server.py +++ b/bookwyrm/models/federated_server.py @@ -4,7 +4,7 @@ from .base_model import BookWyrmModel class FederatedServer(BookWyrmModel): - """ store which server's we federate with """ + """ store which servers we federate with """ server_name = models.CharField(max_length=255, unique=True) # federated, blocked, whatever else diff --git a/bookwyrm/models/report.py b/bookwyrm/models/report.py new file mode 100644 index 00000000..a1e17eb2 --- /dev/null +++ b/bookwyrm/models/report.py @@ -0,0 +1,37 @@ +""" flagged for moderation """ +from django.db import models +from django.db.models import F, Q +from .base_model import BookWyrmModel + + +class Report(BookWyrmModel): + """ reported status or user """ + + reporter = models.ForeignKey( + "User", related_name="reporter", on_delete=models.PROTECT + ) + note = models.TextField(null=True, blank=True) + user = models.ForeignKey("User", on_delete=models.PROTECT) + statuses = models.ManyToManyField("Status", null=True, blank=True) + resolved = models.BooleanField(default=False) + + class Meta: + """ don't let users report themselves """ + + constraints = [ + models.CheckConstraint(check=~Q(reporter=F("user")), name="self_report") + ] + ordering = ("-created_date",) + + +class ReportComment(BookWyrmModel): + """ updates on a report """ + + user = models.ForeignKey("User", on_delete=models.PROTECT) + note = models.TextField() + report = models.ForeignKey(Report, on_delete=models.PROTECT) + + class Meta: + """ sort comments """ + + ordering = ("-created_date",) diff --git a/bookwyrm/templates/layout.html b/bookwyrm/templates/layout.html index 901a12ff..fc2ebdb7 100644 --- a/bookwyrm/templates/layout.html +++ b/bookwyrm/templates/layout.html @@ -114,8 +114,8 @@ {% endif %} {% if perms.bookwyrm.edit_instance_settings %}
  • - - {% trans 'Site Configuration' %} + + {% trans 'Admin' %}
  • {% endif %} diff --git a/bookwyrm/templates/moderation/report.html b/bookwyrm/templates/moderation/report.html new file mode 100644 index 00000000..a231c41c --- /dev/null +++ b/bookwyrm/templates/moderation/report.html @@ -0,0 +1,76 @@ +{% extends 'settings/admin_layout.html' %} +{% load i18n %} +{% load humanize %} + +{% block title %}{% blocktrans with report_id=report.id username=report.user.username %}Report #{{ report_id }}: {{ username }}{% endblocktrans %}{% endblock %} +{% block header %}{% blocktrans with report_id=report.id username=report.user.username %}Report #{{ report_id }}: {{ username }}{% endblocktrans %}{% endblock %} + +{% block panel %} +
    + {% trans "Back to reports" %} +
    + +
    + {% include 'moderation/report_preview.html' with report=report %} +
    + +
    +

    {% trans "Actions" %}

    +

    {% trans "View user profile" %}

    +
    +

    + {% trans "Send direct message" %} +

    +
    + {% csrf_token %} + {% if report.user.is_active %} + + {% else %} + + {% endif %} +
    +
    +
    + +
    +

    {% trans "Moderator Comments" %}

    + {% for comment in report.reportcomment_set.all %} +
    +

    {{ comment.note }}

    + +
    + {% endfor %} +
    + {% csrf_token %} + + + +
    +
    + +
    +

    {% trans "Reported statuses" %}

    + {% if not report.statuses.exists %} + {% trans "No statuses reported" %} + {% else %} + + {% endif %} +
    +{% endblock %} diff --git a/bookwyrm/templates/moderation/report_modal.html b/bookwyrm/templates/moderation/report_modal.html new file mode 100644 index 00000000..ce8408ee --- /dev/null +++ b/bookwyrm/templates/moderation/report_modal.html @@ -0,0 +1,37 @@ +{% extends 'components/modal.html' %} +{% load i18n %} +{% load humanize %} + +{% block modal-title %} +{% blocktrans with username=user.username %}Report @{{ username }}{% endblocktrans %} +{% endblock %} + +{% block modal-form-open %} +
    +{% endblock %} + +{% block modal-body %} + +{% csrf_token %} + + + + +
    +

    {% blocktrans with site_name=site.name %}This report will be sent to {{ site_name }}'s moderators for review.{% endblocktrans %}

    + + +
    + +{% endblock %} + + +{% block modal-footer %} + + +{% trans "Cancel" as button_text %} +{% include 'snippets/toggle/toggle_button.html' with text=button_text controls_text="report" controls_uid=report_uuid class="" %} + +{% endblock %} +{% block modal-form-close %}
    {% endblock %} + diff --git a/bookwyrm/templates/moderation/report_preview.html b/bookwyrm/templates/moderation/report_preview.html new file mode 100644 index 00000000..363783d5 --- /dev/null +++ b/bookwyrm/templates/moderation/report_preview.html @@ -0,0 +1,37 @@ +{% extends 'components/card.html' %} +{% load i18n %} +{% load humanize %} +{% block card-header %} +

    + {% blocktrans with report_id=report.id username=report.user.username %}Report #{{ report_id }}: {{ username }}{% endblocktrans %} +

    +{% endblock %} + +{% block card-content %} +
    +

    + {% if report.note %}{{ report.note }}{% else %}{% trans "No notes provided" %}{% endif %} +

    +
    +{% endblock %} + +{% block card-footer %} + + + +{% endblock %} diff --git a/bookwyrm/templates/moderation/reports.html b/bookwyrm/templates/moderation/reports.html new file mode 100644 index 00000000..ebf29a7a --- /dev/null +++ b/bookwyrm/templates/moderation/reports.html @@ -0,0 +1,28 @@ +{% extends 'settings/admin_layout.html' %} +{% load i18n %} + +{% block title %}{% trans "Reports" %}{% endblock %} +{% block header %}{% trans "Reports" %}{% endblock %} + +{% block panel %} +
    + +
    + +
    + {% for report in reports %} +
    + {% include 'moderation/report_preview.html' with report=report %} +
    + {% endfor %} +
    + +{% endblock %} + diff --git a/bookwyrm/templates/notifications.html b/bookwyrm/templates/notifications.html index 80ee2250..3f0300bd 100644 --- a/bookwyrm/templates/notifications.html +++ b/bookwyrm/templates/notifications.html @@ -115,15 +115,7 @@
    - {% if related_status.content %} - - {{ related_status.content | safe | truncatewords_html:10 }}{% if related_status.mention_books %} {{ related_status.mention_books.first.title }}{% endif %} - - {% elif related_status.quote %} - {{ related_status.quote | safe | truncatewords_html:10 }} - {% elif related_status.rating %} - {% include 'snippets/stars.html' with rating=related_status.rating %} - {% endif %} + {% include 'snippets/status_preview.html' with status=related_status %}
    {{ related_status.published_date | post_date }} diff --git a/bookwyrm/templates/settings/admin_layout.html b/bookwyrm/templates/settings/admin_layout.html index 16741436..a34fe638 100644 --- a/bookwyrm/templates/settings/admin_layout.html +++ b/bookwyrm/templates/settings/admin_layout.html @@ -18,6 +18,10 @@ {% url 'settings-invites' as url %} {% trans "Invites" %} +
  • + {% url 'settings-reports' as url %} + {% trans "Reports" %} +
  • {% url 'settings-federation' as url %} {% trans "Federated Servers" %} @@ -42,7 +46,7 @@ {% endif %} -
    +
    {% block panel %}{% endblock %}
    diff --git a/bookwyrm/templates/snippets/report_button.html b/bookwyrm/templates/snippets/report_button.html new file mode 100644 index 00000000..2fa0a3f3 --- /dev/null +++ b/bookwyrm/templates/snippets/report_button.html @@ -0,0 +1,10 @@ +{% load i18n %} +{% load bookwyrm_tags %} +{% with 0|uuid as report_uuid %} + +{% trans "Report" as button_text %} +{% include 'snippets/toggle/toggle_button.html' with class="is-danger is-light is-small is-fullwidth" text=button_text controls_text="report" controls_uid=report_uuid focus="modal-title-report" disabled=is_current %} + +{% include 'moderation/report_modal.html' with user=user reporter=request.user controls_text="report" controls_uid=report_uuid %} + +{% endwith %} diff --git a/bookwyrm/templates/snippets/status/status_body.html b/bookwyrm/templates/snippets/status/status_body.html index 8d6c21ed..a7e8e884 100644 --- a/bookwyrm/templates/snippets/status/status_body.html +++ b/bookwyrm/templates/snippets/status/status_body.html @@ -18,7 +18,17 @@ {% block card-footer %}