diff --git a/bookwyrm/forms.py b/bookwyrm/forms.py index 865bc02fe..f2e3f9bbe 100644 --- a/bookwyrm/forms.py +++ b/bookwyrm/forms.py @@ -550,3 +550,9 @@ class ReadThroughForm(CustomForm): class Meta: model = models.ReadThrough fields = ["user", "book", "start_date", "finish_date"] + + +class AutoModRuleForm(CustomForm): + class Meta: + model = models.AutoMod + fields = ["string_match", "flag_users", "flag_statuses", "created_by"] diff --git a/bookwyrm/migrations/0138_autoflag.py b/bookwyrm/migrations/0138_automod.py similarity index 71% rename from bookwyrm/migrations/0138_autoflag.py rename to bookwyrm/migrations/0138_automod.py index b1e803d43..8b0369efa 100644 --- a/bookwyrm/migrations/0138_autoflag.py +++ b/bookwyrm/migrations/0138_automod.py @@ -1,7 +1,6 @@ -# Generated by Django 3.2.12 on 2022-02-18 04:53 +# Generated by Django 3.2.12 on 2022-02-24 18:59 from django.conf import settings -import django.contrib.postgres.fields from django.db import migrations, models import django.db.models.deletion @@ -14,12 +13,12 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='AutoFlag', + name='AutoMod', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('flag_users', models.BooleanField(default=False)), - ('flag_statuses', models.BooleanField(default=False)), - ('string_match', models.CharField(max_length=200)), + ('string_match', models.CharField(max_length=200, unique=True)), + ('flag_users', models.BooleanField(default=True)), + ('flag_statuses', models.BooleanField(default=True)), ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), ], ), diff --git a/bookwyrm/models/__init__.py b/bookwyrm/models/__init__.py index 4c6305f99..1dc6825e4 100644 --- a/bookwyrm/models/__init__.py +++ b/bookwyrm/models/__init__.py @@ -29,7 +29,7 @@ from .import_job import ImportJob, ImportItem from .site import SiteSettings, SiteInvite from .site import PasswordReset, InviteRequest from .announcement import Announcement -from .antispam import EmailBlocklist, IPBlocklist +from .antispam import EmailBlocklist, IPBlocklist, AutoMod from .notification import Notification diff --git a/bookwyrm/models/antispam.py b/bookwyrm/models/antispam.py index 98587a47d..be1050b3a 100644 --- a/bookwyrm/models/antispam.py +++ b/bookwyrm/models/antispam.py @@ -35,9 +35,9 @@ class IPBlocklist(models.Model): ordering = ("-created_date",) -class AutoFlag(models.Model): +class AutoMod(models.Model): """rules to automatically flag suspicious activity""" + string_match = models.CharField(max_length=200, unique=True) + flag_users = models.BooleanField(default=True) + flag_statuses = models.BooleanField(default=True) created_by = models.ForeignKey("User", on_delete=models.PROTECT) - flag_users = models.BooleanField(default=False) - flag_statuses = models.BooleanField(default=False) - string_match = models.CharField(max_length=200) diff --git a/bookwyrm/templates/settings/automod/rules.html b/bookwyrm/templates/settings/automod/rules.html new file mode 100644 index 000000000..7555df2d6 --- /dev/null +++ b/bookwyrm/templates/settings/automod/rules.html @@ -0,0 +1,91 @@ +{% extends 'settings/layout.html' %} +{% load i18n %} +{% load utilities %} + +{% block title %} +{% trans "Auto-moderation rules" %} +{% endblock %} + +{% block header %} +{% trans "Auto-moderation rules" %} +{% endblock %} + +{% block panel %} + +
+

+ {% trans "Auto-moderation rules will create reports for any user or status with fields matching the provided string." %} + {% trans "At this time, reports are not being generated automatically, and you must manually trigger a scan." %} +

+ +
+ +{% if success %} +
+ + + {% trans "Successfully added rule" %} + +
+{% endif %} + +
+ + + {% csrf_token %} + + + + + + + + + + + + + + + {% for rule in rules %} + + + + + + + {% endfor %} +
+ + + + + + +
+ {{ form.string_match }} + {% include 'snippets/form_errors.html' with errors_list=form.string_match.errors id="desc_string_match" %} + + {{ form.flag_users }} + + {{ form.flag_statuses }} + + +
+ {{ rule.string_match }} + + {{ rule.flag_users|yesno }} + + {{ rule.flag_statuses|yesno }} + +
+ {% csrf_token %} + +
+
+
+{% endblock %} + diff --git a/bookwyrm/templates/settings/layout.html b/bookwyrm/templates/settings/layout.html index a74e111ca..c5a3c5af2 100644 --- a/bookwyrm/templates/settings/layout.html +++ b/bookwyrm/templates/settings/layout.html @@ -56,6 +56,10 @@ {% url 'settings-reports' as url %} {% trans "Reports" %} +
  • + {% url 'settings-automod' as url %} + {% trans "Auto-moderation rules" %} +
  • {% url 'settings-email-blocks' as url %} {% trans "Email Blocklist" %} diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index 7572fe637..3c37eef55 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -214,6 +214,9 @@ urlpatterns = [ views.IPBlocklist.as_view(), name="settings-ip-blocks-delete", ), + # auto-moderation rules + re_path(r"^settings/automod/?$", views.AutoMod.as_view(), name="settings-automod"), + re_path(r"^settings/automod/(?P\d+)/delete?$", views.automod_delete, name="settings-automod-delete"), # moderation re_path( r"^settings/reports/?$", views.ReportsAdmin.as_view(), name="settings-reports" diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index 7474421b9..f80b24507 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -2,6 +2,7 @@ # site admin from .admin.announcements import Announcements, Announcement from .admin.announcements import EditAnnouncement, delete_announcement +from .admin.automod import AutoMod, automod_delete from .admin.dashboard import Dashboard from .admin.federation import Federation, FederatedServer from .admin.federation import AddFederatedServer, ImportServerBlocklist diff --git a/bookwyrm/views/admin/automod.py b/bookwyrm/views/admin/automod.py new file mode 100644 index 000000000..a62ed0ca2 --- /dev/null +++ b/bookwyrm/views/admin/automod.py @@ -0,0 +1,53 @@ +""" moderation via flagged posts and users """ +from django.contrib.auth.decorators import login_required, permission_required +from django.shortcuts import get_object_or_404, redirect +from django.template.response import TemplateResponse +from django.utils.decorators import method_decorator +from django.views import View +from django.views.decorators.http import require_POST + +from bookwyrm import forms, models + + +@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", +) +# pylint: disable=no-self-use +class AutoMod(View): + """Manage automated flagging""" + + def get(self, request): + """view rules""" + data = {"rules": models.AutoMod.objects.all(), "form": forms.AutoModRuleForm()} + return TemplateResponse(request, "settings/automod/rules.html", data) + + def post(self, request): + """add rule""" + form = forms.AutoModRuleForm(request.POST) + success = form.is_valid() + if success: + form.save() + form = forms.AutoModRuleForm() + + data = { + "rules": models.AutoMod.objects.all(), + "form": form, + "success": success, + } + return TemplateResponse(request, "settings/automod/rules.html", data) + +@require_POST +@permission_required("bookwyrm.moderate_user", raise_exception=True) +@permission_required("bookwyrm.moderate_post", raise_exception=True) +# pylint: disable=unused-argument +def automod_delete(request, rule_id): + """ Remove a rule """ + rule = get_object_or_404(models.AutoMod, id=rule_id) + rule.delete() + return redirect("settings-automod")