diff --git a/src/fellchensammlung/admin.py b/src/fellchensammlung/admin.py index 9ae4a8e..1764b8b 100644 --- a/src/fellchensammlung/admin.py +++ b/src/fellchensammlung/admin.py @@ -2,7 +2,7 @@ from django.contrib import admin from django.contrib import admin -from .models import Animal, Species, RescueOrganization, AdoptionNotice, Location, Rule, Image +from .models import Animal, Species, RescueOrganization, AdoptionNotice, Location, Rule, Image, ModerationAction, Report admin.site.register(Animal) admin.site.register(Species) @@ -11,3 +11,5 @@ admin.site.register(Location) admin.site.register(AdoptionNotice) admin.site.register(Rule) admin.site.register(Image) +admin.site.register(Report) +admin.site.register(ModerationAction) diff --git a/src/fellchensammlung/forms.py b/src/fellchensammlung/forms.py index db9314d..305bc67 100644 --- a/src/fellchensammlung/forms.py +++ b/src/fellchensammlung/forms.py @@ -1,5 +1,5 @@ from django import forms -from .models import AdoptionNotice, Animal, Image +from .models import AdoptionNotice, Animal, Image, Report, ModerationAction class DateInput(forms.DateInput): @@ -29,3 +29,15 @@ class ImageForm(forms.ModelForm): class Meta: model = Image fields = ('title', 'image', 'alt_text') + + +class ReportForm(forms.ModelForm): + class Meta: + model = Report + fields = ('reported_broken_rules', 'comment') + + +class ModerationActionForm(forms.ModelForm): + class Meta: + model = ModerationAction + fields = ('action', 'public_comment', 'private_comment') diff --git a/src/fellchensammlung/management/commands/populate_db.py b/src/fellchensammlung/management/commands/populate_db.py index 746c5af..88d47e4 100644 --- a/src/fellchensammlung/management/commands/populate_db.py +++ b/src/fellchensammlung/management/commands/populate_db.py @@ -33,6 +33,18 @@ class Command(BaseCommand): rule2 = baker.make(Rule, title="Keep al least the minimum number of animals for species", rule_text="This is not markdown") + rule3 = baker.make(Rule, + title="Rule three", + rule_text="Everything needs at least three rules") + + report1 = baker.make(Report, reported_broken_rules=[rule1, rule2], comment="This seems sketchy") + + moderation_action1 = baker.make(ModerationAction, + action=ModerationAction.COMMENT, + public_comment="This has been seen by a moderator") + moderation_action1 = baker.make(ModerationAction, + action=ModerationAction.DELETE, + public_comment="A moderator has deleted the reported content") User.objects.create_user('test', password='foobar') User.objects.create_superuser(username="admin", password="admin") diff --git a/src/fellchensammlung/migrations/0005_report_moderationaction.py b/src/fellchensammlung/migrations/0005_report_moderationaction.py new file mode 100644 index 0000000..133f713 --- /dev/null +++ b/src/fellchensammlung/migrations/0005_report_moderationaction.py @@ -0,0 +1,37 @@ +# Generated by Django 5.0.3 on 2024-03-22 09:20 + +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fellchensammlung', '0004_alter_animal_sex'), + ] + + operations = [ + migrations.CreateModel( + name='Report', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, help_text='ID dieses reports', primary_key=True, serialize=False, verbose_name='ID')), + ('status', models.CharField(choices=[('action taken', 'Action was taken'), ('no action taken', 'No action was taken'), ('waiting', 'Waiting for moderator action')], max_length=30)), + ('comment', models.TextField(blank=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('adoption_notice', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fellchensammlung.adoptionnotice')), + ('reported_broken_rules', models.ManyToManyField(blank=True, to='fellchensammlung.rule')), + ], + ), + migrations.CreateModel( + name='ModerationAction', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('action', models.CharField(choices=[('user_banned', 'User was banned'), ('content_deleted', 'Content was deleted'), ('other_action_taken', 'Other action was taken'), ('no_action_taken', 'No action was taken')], max_length=30)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('public_comment', models.TextField()), + ('private_comment', models.TextField()), + ('report', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fellchensammlung.report')), + ], + ), + ] diff --git a/src/fellchensammlung/migrations/0006_alter_moderationaction_action.py b/src/fellchensammlung/migrations/0006_alter_moderationaction_action.py new file mode 100644 index 0000000..ba1e0b3 --- /dev/null +++ b/src/fellchensammlung/migrations/0006_alter_moderationaction_action.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.3 on 2024-03-22 11:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fellchensammlung', '0005_report_moderationaction'), + ] + + operations = [ + migrations.AlterField( + model_name='moderationaction', + name='action', + field=models.CharField(choices=[('user_banned', 'User was banned'), ('content_deleted', 'Content was deleted'), ('comment', 'Comment was added'), ('other_action_taken', 'Other action was taken'), ('no_action_taken', 'No action was taken')], max_length=30), + ), + ] diff --git a/src/fellchensammlung/migrations/0007_alter_moderationaction_private_comment_and_more.py b/src/fellchensammlung/migrations/0007_alter_moderationaction_private_comment_and_more.py new file mode 100644 index 0000000..5a3647a --- /dev/null +++ b/src/fellchensammlung/migrations/0007_alter_moderationaction_private_comment_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.3 on 2024-03-22 11:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fellchensammlung', '0006_alter_moderationaction_action'), + ] + + operations = [ + migrations.AlterField( + model_name='moderationaction', + name='private_comment', + field=models.TextField(blank=True), + ), + migrations.AlterField( + model_name='moderationaction', + name='public_comment', + field=models.TextField(blank=True), + ), + ] diff --git a/src/fellchensammlung/models.py b/src/fellchensammlung/models.py index d6c0a99..92a1470 100644 --- a/src/fellchensammlung/models.py +++ b/src/fellchensammlung/models.py @@ -1,3 +1,5 @@ +import uuid + from django.db import models from django.urls import reverse from django.contrib.auth.models import User @@ -180,6 +182,7 @@ class MarkdownContent(models.Model): def __str__(self): return self.title + class Rule(models.Model): """ Class to store rules @@ -192,3 +195,55 @@ class Rule(models.Model): def __str__(self): return self.title + +class Report(models.Model): + ACTION_TAKEN = "action taken" + NO_ACTION_TAKEN = "no action taken" + WAITING = "waiting" + STATES = { + ACTION_TAKEN: "Action was taken", + NO_ACTION_TAKEN: "No action was taken", + WAITING: "Waiting for moderator action", + } + id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text=_('ID dieses reports'), + verbose_name=_('ID')) + status = models.CharField(max_length=30, choices=STATES) + reported_broken_rules = models.ManyToManyField(Rule, blank=True) + adoption_notice = models.ForeignKey("AdoptionNotice", on_delete=models.CASCADE) + comment = models.TextField(blank=True) + created_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return f"[{self.status}]: {self.adoption_notice.name}" + + def get_reported_rules(self): + return self.reported_broken_rules.all() + + def get_moderation_actions(self): + return ModerationAction.objects.filter(report=self) + + +class ModerationAction(models.Model): + BAN = "user_banned" + DELETE = "content_deleted" + COMMENT = "comment" + OTHER = "other_action_taken" + NONE = "no_action_taken" + ACTIONS = { + BAN: "User was banned", + DELETE: "Content was deleted", + COMMENT: "Comment was added", + OTHER: "Other action was taken", + NONE: "No action was taken" + } + action = models.CharField(max_length=30, choices=ACTIONS.items()) + created_at = models.DateTimeField(auto_now_add=True) + public_comment = models.TextField(blank=True) + # Only visible to moderator + private_comment = models.TextField(blank=True) + report = models.ForeignKey(Report, on_delete=models.CASCADE) + + # TODO: Needs field for moderator that performed the action + + def __str__(self): + return f"[{self.action}]: {self.public_comment}" diff --git a/src/fellchensammlung/static/fellchensammlung/css/styles.css b/src/fellchensammlung/static/fellchensammlung/css/styles.css index 565166b..480e71a 100644 --- a/src/fellchensammlung/static/fellchensammlung/css/styles.css +++ b/src/fellchensammlung/static/fellchensammlung/css/styles.css @@ -277,6 +277,23 @@ h1 { box-sizing: border-box; } +.container-list-moderation-actions { + display: flex; + flex-wrap: wrap; +} + + +.card-moderation-action { + width: 25%; + margin: 10px; + margin-bottom: 20px; + + border: 1px solid #ccc; + border-radius: 5px; + padding: 2%; + box-sizing: border-box; +} + @media (max-width: 920px) { .card-rule { width: 100%; diff --git a/src/fellchensammlung/templates/fellchensammlung/detail-report.html b/src/fellchensammlung/templates/fellchensammlung/detail-report.html new file mode 100644 index 0000000..66c7fec --- /dev/null +++ b/src/fellchensammlung/templates/fellchensammlung/detail-report.html @@ -0,0 +1,17 @@ +{% extends "fellchensammlung/base_generic.html" %} +{% load i18n %} + +{% block content %} + {% if form_complete %} +
{{ moderation_action.public_comment }}
+Kommentar zur Meldung: + {{ report.comment }} +
+