diff --git a/core/migrations/0030_auto_20190704_1500.py b/core/migrations/0030_auto_20190704_1500.py new file mode 100644 index 00000000..72121e9e --- /dev/null +++ b/core/migrations/0030_auto_20190704_1500.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-07-04 13:00 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [("core", "0029_auto_20180426_2013")] + + operations = [ + migrations.AlterField( + model_name="notification", + name="type", + field=models.CharField( + choices=[ + ("POSTER_MODERATION", "A new poster needs to be moderated"), + ("MAILING_MODERATION", "A new mailing list needs to be moderated"), + ( + "PEDAGOGY_MODERATION", + "A new pedagogy comment has been signaled for moderation", + ), + ("NEWS_MODERATION", "There are %s fresh news to be moderated"), + ("FILE_MODERATION", "New files to be moderated"), + ( + "SAS_MODERATION", + "There are %s pictures to be moderated in the SAS", + ), + ("NEW_PICTURES", "You've been identified on some pictures"), + ("REFILLING", "You just refilled of %s €"), + ("SELLING", "You just bought %s"), + ("GENERIC", "You have a notification"), + ], + default="GENERIC", + max_length=32, + verbose_name="type", + ), + ) + ] diff --git a/pedagogy/forms.py b/pedagogy/forms.py index eed140b7..9180516d 100644 --- a/pedagogy/forms.py +++ b/pedagogy/forms.py @@ -23,11 +23,12 @@ # from django import forms +from django.utils.translation import ugettext_lazy as _ from core.views.forms import MarkdownInput from core.models import User -from pedagogy.models import UV, UVComment +from pedagogy.models import UV, UVComment, UVCommentReport class UVForm(forms.ModelForm): @@ -100,3 +101,45 @@ class UVCommentForm(forms.ModelForm): self.fields["author"].initial = author_id self.fields["uv"].queryset = UV.objects.filter(id=uv_id).all() self.fields["uv"].initial = uv_id + + +class UVCommentReportForm(forms.ModelForm): + """ + Form handeling creation and edit of an UVReport + """ + + class Meta: + model = UVCommentReport + fields = ("comment", "reporter", "reason") + widgets = { + "comment": forms.HiddenInput, + "reporter": forms.HiddenInput, + "reason": MarkdownInput, + } + + def __init__(self, reporter_id, comment_id, *args, **kwargs): + super(UVCommentReportForm, self).__init__(*args, **kwargs) + self.fields["reporter"].queryset = User.objects.filter(id=reporter_id).all() + self.fields["reporter"].initial = reporter_id + self.fields["comment"].queryset = UVComment.objects.filter(id=comment_id).all() + self.fields["comment"].initial = comment_id + + +class UVCommentModerationForm(forms.Form): + """ + Form handeling bulk comment deletion + """ + + accepted_reports = forms.ModelMultipleChoiceField( + UVCommentReport.objects.all(), + label=_("Accepted reports"), + widget=forms.CheckboxSelectMultiple, + required=False, + ) + + denied_reports = forms.ModelMultipleChoiceField( + UVCommentReport.objects.all(), + label=_("Denied reports"), + widget=forms.CheckboxSelectMultiple, + required=False, + ) diff --git a/pedagogy/migrations/0001_initial.py b/pedagogy/migrations/0001_initial.py index 9b4133f8..83658e8c 100644 --- a/pedagogy/migrations/0001_initial.py +++ b/pedagogy/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.20 on 2019-06-18 20:07 +# Generated by Django 1.11.20 on 2019-06-19 23:05 from __future__ import unicode_literals from django.conf import settings @@ -190,7 +190,7 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), - ("comment", models.TextField(verbose_name="comment")), + ("comment", models.TextField(blank=True, verbose_name="comment")), ( "grade_global", models.IntegerField( @@ -281,7 +281,26 @@ class Migration(migrations.Migration): serialize=False, verbose_name="ID", ), - ) + ), + ("reason", models.TextField(verbose_name="reason")), + ( + "comment", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="reports", + to="pedagogy.UVComment", + verbose_name="report", + ), + ), + ( + "reporter", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="reported_uv_comment", + to=settings.AUTH_USER_MODEL, + verbose_name="reporter", + ), + ), ], ), migrations.CreateModel( diff --git a/pedagogy/models.py b/pedagogy/models.py index 30b3076b..cf75a1ad 100644 --- a/pedagogy/models.py +++ b/pedagogy/models.py @@ -27,6 +27,7 @@ from django.utils.translation import ugettext_lazy as _ from django.utils import timezone from django.core import validators from django.conf import settings +from django.utils.functional import cached_property from core.models import User @@ -165,6 +166,13 @@ class UVComment(models.Model): """ return self.author == user or user.is_owner(self.uv) + @cached_property + def is_reported(self): + """ + Return True if someone reported this UV + """ + return self.reports.exists() + def __str__(self): return "%s - %s" % (self.uv, self.author) @@ -176,7 +184,7 @@ class UVComment(models.Model): blank=False, ) uv = models.ForeignKey(UV, related_name="comments", verbose_name=_("uv")) - comment = models.TextField(_("comment")) + comment = models.TextField(_("comment"), blank=True) grade_global = models.IntegerField( _("global grade"), validators=[validators.MinValueValidator(-1), validators.MaxValueValidator(4)], @@ -251,4 +259,19 @@ class UVCommentReport(models.Model): Report an inapropriate comment """ - pass + def is_owned_by(self, user): + """ + Can be created by a pedagogy admin, a superuser or a subscriber + """ + return user.is_subscribed or user.is_owner(self.comment.uv) + + comment = models.ForeignKey( + UVComment, + related_name="reports", + verbose_name=_("report"), + on_delete=models.CASCADE, + ) + reporter = models.ForeignKey( + User, related_name="reported_uv_comment", verbose_name=_("reporter") + ) + reason = models.TextField(_("reason")) diff --git a/pedagogy/templates/pedagogy/moderation.jinja b/pedagogy/templates/pedagogy/moderation.jinja new file mode 100644 index 00000000..f53598a7 --- /dev/null +++ b/pedagogy/templates/pedagogy/moderation.jinja @@ -0,0 +1,63 @@ +{% extends "core/base.jinja" %} +{% from 'core/macros.jinja' import select_all_checkbox %} + +{% block title %} +{% trans %}UV comment moderation{% endtrans %} +{% endblock title %} + +{% block content %} +
+ {% csrf_token %} +

{{ select_all_checkbox("moderation_delete_form") }}

+ + + + + + + + + + + {% set queryset = form.accepted_reports.field.queryset %} + {% for widget in form.accepted_reports.subwidgets %} + {% set report = queryset.get(id=widget.data.value) %} + + + + + + + {% endfor %} + +
{% trans %}UV{% endtrans %}{% trans %}Comment{% endtrans %}{% trans %}Reason{% endtrans %}{% trans %}Delete{% endtrans %}
{{ report.comment.uv }}{{ report.comment.comment|markdown }}{{ report.reason|markdown }}{{ widget.tag() }}
+

+
+
+ {% csrf_token %} +

{{ select_all_checkbox("moderation_keep_form") }}

+ + + + + + + + + + + {% set queryset = form.denied_reports.field.queryset %} + {% for widget in form.denied_reports.subwidgets %} + {% set report = queryset.get(id=widget.data.value) %} + + + + + + + {% endfor %} + +
{% trans %}UV{% endtrans %}{% trans %}Comment{% endtrans %}{% trans %}Reason{% endtrans %}{% trans %}Delete{% endtrans %}
{{ report.comment.uv }}{{ report.comment.comment|markdown }}{{ report.reason|markdown }}{{ widget.tag() }}
+

+
+{% endblock content %} diff --git a/pedagogy/templates/pedagogy/uv_detail.jinja b/pedagogy/templates/pedagogy/uv_detail.jinja index e18f1b1d..6ddc7c2e 100644 --- a/pedagogy/templates/pedagogy/uv_detail.jinja +++ b/pedagogy/templates/pedagogy/uv_detail.jinja @@ -29,6 +29,10 @@

{% trans %}Edit{% endtrans %}

{% trans %}Delete{% endtrans %}

{% endif %} +

{% trans %}Report{% endtrans %}

+ {% if comment.is_reported %} +

{% trans %}This comment has been reported{% endtrans %}

+ {% endif %} {% endfor %} {% endif %} diff --git a/pedagogy/tests.py b/pedagogy/tests.py index 89874ca6..8780d7b4 100644 --- a/pedagogy/tests.py +++ b/pedagogy/tests.py @@ -22,13 +22,14 @@ # # +from django.conf import settings from django.test import TestCase from django.core.urlresolvers import reverse from django.core.management import call_command -from core.models import User +from core.models import User, Notification -from pedagogy.models import UV, UVComment +from pedagogy.models import UV, UVComment, UVCommentReport def create_uv_template(user_id, code="IFC1", exclude_list=[]): @@ -727,3 +728,323 @@ class UVSearchTest(TestCase): # Search with credit type response = self.client.get(reverse("pedagogy:guide"), {"credit_type": "TM"}) self.assertNotContains(response, text="PA00") + + +class UVModerationFormTest(TestCase): + """ + Test moderation view + Assert access rights and if the form works well + """ + + def setUp(self): + call_command("populate") + + self.krophil = User.objects.get(username="krophil") + + # Prepare a comment + comment_kwargs = create_uv_comment_template(self.krophil.id) + comment_kwargs["author"] = self.krophil + comment_kwargs["uv"] = UV.objects.get(id=comment_kwargs["uv"]) + self.comment_1 = UVComment(**comment_kwargs) + self.comment_1.save() + + # Prepare another comment + comment_kwargs = create_uv_comment_template(self.krophil.id) + comment_kwargs["author"] = self.krophil + comment_kwargs["uv"] = UV.objects.get(id=comment_kwargs["uv"]) + self.comment_2 = UVComment(**comment_kwargs) + self.comment_2.save() + + # Prepare a comment report for comment 1 + self.report_1 = UVCommentReport( + comment=self.comment_1, reporter=self.krophil, reason="C'est moche" + ) + self.report_1.save() + self.report_1_bis = UVCommentReport( + comment=self.comment_1, reporter=self.krophil, reason="C'est moche 2" + ) + self.report_1_bis.save() + + # Prepare a comment report for comment 2 + self.report_2 = UVCommentReport( + comment=self.comment_2, reporter=self.krophil, reason="C'est moche" + ) + self.report_2.save() + + def test_access_authorized_success(self): + # Test with root + self.client.login(username="root", password="plop") + response = self.client.get(reverse("pedagogy:moderation")) + self.assertEquals(response.status_code, 200) + + # Test with pedagogy admin + self.client.login(username="tutu", password="plop") + response = self.client.get(reverse("pedagogy:moderation")) + self.assertEquals(response.status_code, 200) + + def test_access_unauthorized_fail(self): + # Test with anonymous user + response = self.client.get(reverse("pedagogy:moderation")) + self.assertEquals(response.status_code, 403) + + # Test with unsubscribed user + self.client.login(username="guy", password="plop") + response = self.client.get(reverse("pedagogy:moderation")) + self.assertEquals(response.status_code, 403) + + # Test with subscribed user + self.client.login(username="sli", password="plop") + response = self.client.get(reverse("pedagogy:moderation")) + self.assertEquals(response.status_code, 403) + + def test_do_nothing(self): + self.client.login(username="root", password="plop") + response = self.client.post(reverse("pedagogy:moderation")) + self.assertEquals(response.status_code, 302) + + # Test that nothing has changed + self.assertTrue(UVCommentReport.objects.filter(id=self.report_1.id).exists()) + self.assertTrue(UVComment.objects.filter(id=self.comment_1.id).exists()) + self.assertTrue( + UVCommentReport.objects.filter(id=self.report_1_bis.id).exists() + ) + self.assertTrue(UVCommentReport.objects.filter(id=self.report_2.id).exists()) + self.assertTrue(UVComment.objects.filter(id=self.comment_2.id).exists()) + + def test_delete_comment(self): + self.client.login(username="root", password="plop") + response = self.client.post( + reverse("pedagogy:moderation"), {"accepted_reports": [self.report_1.id]} + ) + self.assertEquals(response.status_code, 302) + + # Test that the comment and it's associated report has been deleted + self.assertFalse(UVCommentReport.objects.filter(id=self.report_1.id).exists()) + self.assertFalse(UVComment.objects.filter(id=self.comment_1.id).exists()) + # Test that the bis report has been deleted + self.assertFalse( + UVCommentReport.objects.filter(id=self.report_1_bis.id).exists() + ) + + # Test that the other comment and report still exists + self.assertTrue(UVCommentReport.objects.filter(id=self.report_2.id).exists()) + self.assertTrue(UVComment.objects.filter(id=self.comment_2.id).exists()) + + def test_delete_comment_bulk(self): + self.client.login(username="root", password="plop") + response = self.client.post( + reverse("pedagogy:moderation"), + {"accepted_reports": [self.report_1.id, self.report_2.id]}, + ) + self.assertEquals(response.status_code, 302) + + # Test that comments and their associated reports has been deleted + self.assertFalse(UVCommentReport.objects.filter(id=self.report_1.id).exists()) + self.assertFalse(UVComment.objects.filter(id=self.comment_1.id).exists()) + self.assertFalse(UVCommentReport.objects.filter(id=self.report_2.id).exists()) + self.assertFalse(UVComment.objects.filter(id=self.comment_2.id).exists()) + # Test that the bis report has been deleted + self.assertFalse( + UVCommentReport.objects.filter(id=self.report_1_bis.id).exists() + ) + + def test_delete_comment_with_bis(self): + # Test case if two reports targets the same comment and are both deleted + self.client.login(username="root", password="plop") + response = self.client.post( + reverse("pedagogy:moderation"), + {"accepted_reports": [self.report_1.id, self.report_1_bis.id]}, + ) + self.assertEquals(response.status_code, 302) + + # Test that the comment and it's associated report has been deleted + self.assertFalse(UVCommentReport.objects.filter(id=self.report_1.id).exists()) + self.assertFalse(UVComment.objects.filter(id=self.comment_1.id).exists()) + # Test that the bis report has been deleted + self.assertFalse( + UVCommentReport.objects.filter(id=self.report_1_bis.id).exists() + ) + + def test_delete_report(self): + self.client.login(username="root", password="plop") + response = self.client.post( + reverse("pedagogy:moderation"), {"denied_reports": [self.report_1.id]} + ) + self.assertEquals(response.status_code, 302) + + # Test that the report has been deleted and that the comment still exists + self.assertFalse(UVCommentReport.objects.filter(id=self.report_1.id).exists()) + self.assertTrue(UVComment.objects.filter(id=self.comment_1.id).exists()) + # Test that the bis report is still there + self.assertTrue( + UVCommentReport.objects.filter(id=self.report_1_bis.id).exists() + ) + + # Test that the other comment and report still exists + self.assertTrue(UVCommentReport.objects.filter(id=self.report_2.id).exists()) + self.assertTrue(UVComment.objects.filter(id=self.comment_2.id).exists()) + + def test_delete_report_bulk(self): + self.client.login(username="root", password="plop") + response = self.client.post( + reverse("pedagogy:moderation"), + { + "denied_reports": [ + self.report_1.id, + self.report_1_bis.id, + self.report_2.id, + ] + }, + ) + self.assertEquals(response.status_code, 302) + + # Test that every reports has been deleted + self.assertFalse(UVCommentReport.objects.filter(id=self.report_1.id).exists()) + self.assertFalse( + UVCommentReport.objects.filter(id=self.report_1_bis.id).exists() + ) + self.assertFalse(UVCommentReport.objects.filter(id=self.report_2.id).exists()) + + # Test that comments still exists + self.assertTrue(UVComment.objects.filter(id=self.comment_1.id).exists()) + self.assertTrue(UVComment.objects.filter(id=self.comment_2.id).exists()) + + def test_delete_mixed(self): + self.client.login(username="root", password="plop") + response = self.client.post( + reverse("pedagogy:moderation"), + { + "accepted_reports": [self.report_2.id], + "denied_reports": [self.report_1.id], + }, + ) + self.assertEquals(response.status_code, 302) + + # Test that report 2 and his comment has been deleted + self.assertFalse(UVCommentReport.objects.filter(id=self.report_2.id).exists()) + self.assertFalse(UVComment.objects.filter(id=self.comment_2.id).exists()) + + # Test that report 1 has been deleted and it's comment still exists + self.assertFalse(UVCommentReport.objects.filter(id=self.report_1.id).exists()) + self.assertTrue(UVComment.objects.filter(id=self.comment_1.id).exists()) + + # Test that report 1 bis is still there + self.assertTrue( + UVCommentReport.objects.filter(id=self.report_1_bis.id).exists() + ) + + def test_delete_mixed_with_bis(self): + self.client.login(username="root", password="plop") + response = self.client.post( + reverse("pedagogy:moderation"), + { + "accepted_reports": [self.report_1.id], + "denied_reports": [self.report_1_bis.id], + }, + ) + self.assertEquals(response.status_code, 302) + + # Test that report 1 and 1 bis has been deleted + self.assertFalse( + UVCommentReport.objects.filter( + id__in=[self.report_1.id, self.report_1_bis.id] + ).exists() + ) + + # Test that comment 1 has been deleted + self.assertFalse(UVComment.objects.filter(id=self.comment_1.id).exists()) + + # Test that report and comment 2 still exists + self.assertTrue(UVCommentReport.objects.filter(id=self.report_2.id).exists()) + self.assertTrue(UVComment.objects.filter(id=self.comment_2.id).exists()) + + +class UVCommentReportCreateTest(TestCase): + """ + Test report creation view view + Assert access rights and if you can create with it + """ + + def setUp(self): + call_command("populate") + + self.krophil = User.objects.get(username="krophil") + self.tutu = User.objects.get(username="tutu") + + # Prepare a comment + comment_kwargs = create_uv_comment_template(self.krophil.id) + comment_kwargs["author"] = self.krophil + comment_kwargs["uv"] = UV.objects.get(id=comment_kwargs["uv"]) + self.comment = UVComment(**comment_kwargs) + self.comment.save() + + def create_report_test(self, username, success): + self.client.login(username=username, password="plop") + response = self.client.post( + reverse("pedagogy:comment_report", kwargs={"comment_id": self.comment.id}), + { + "comment": self.comment.id, + "reporter": User.objects.get(username=username).id, + "reason": "C'est moche", + }, + ) + if success: + self.assertEquals(response.status_code, 302) + else: + self.assertEquals(response.status_code, 403) + self.assertEquals(UVCommentReport.objects.all().exists(), success) + + def test_create_report_root_success(self): + self.create_report_test("root", True) + + def test_create_report_pedagogy_admin_success(self): + self.create_report_test("tutu", True) + + def test_create_report_subscriber_success(self): + self.create_report_test("sli", True) + + def test_create_report_unsubscribed_fail(self): + self.create_report_test("guy", False) + + def test_create_report_anonymous_fail(self): + response = self.client.post( + reverse("pedagogy:comment_report", kwargs={"comment_id": self.comment.id}), + {"comment": self.comment.id, "reporter": 0, "reason": "C'est moche"}, + ) + self.assertEquals(response.status_code, 403) + self.assertFalse(UVCommentReport.objects.all().exists()) + + def test_notifications(self): + self.assertFalse( + self.tutu.notifications.filter(type="PEDAGOGY_MODERATION").exists() + ) + # Create a comment report + self.create_report_test("tutu", True) + + # Check that a notification has been created for pedagogy admins + self.assertTrue( + self.tutu.notifications.filter(type="PEDAGOGY_MODERATION").exists() + ) + + # Check that only pedagogy admins recieves this notification + for notif in Notification.objects.filter(type="PEDAGOGY_MODERATION").all(): + self.assertTrue( + notif.user.is_in_group(settings.SITH_GROUP_PEDAGOGY_ADMIN_ID) + ) + + # Check that notifications are not duplicated if not viewed + self.create_report_test("tutu", True) + self.assertEquals( + self.tutu.notifications.filter(type="PEDAGOGY_MODERATION").count(), 1 + ) + + # Check that a new notification is created when the old one has been viewed + notif = self.tutu.notifications.filter(type="PEDAGOGY_MODERATION").first() + notif.viewed = True + notif.save() + + self.create_report_test("tutu", True) + + self.assertEquals( + self.tutu.notifications.filter(type="PEDAGOGY_MODERATION").count(), 2 + ) diff --git a/pedagogy/urls.py b/pedagogy/urls.py index 7d90c735..5527bf3b 100644 --- a/pedagogy/urls.py +++ b/pedagogy/urls.py @@ -46,7 +46,6 @@ urlpatterns = [ name="comment_report", ), # Moderation - url(r"^reported$", UVCommentReportListView.as_view(), name="comment_report_list"), url(r"^moderation$", UVModerationFormView.as_view(), name="moderation"), # Administration : Create Update Delete Edit url(r"^uv/create$", UVCreateView.as_view(), name="uv_create"), diff --git a/pedagogy/views.py b/pedagogy/views.py index bdc2fa2f..f1e21736 100644 --- a/pedagogy/views.py +++ b/pedagogy/views.py @@ -34,7 +34,10 @@ from django.views.generic import ( from django.core import serializers from django.utils import html from django.http import HttpResponse -from django.core.urlresolvers import reverse_lazy +from django.core.exceptions import PermissionDenied, ObjectDoesNotExist +from django.core.urlresolvers import reverse_lazy, reverse +from django.shortcuts import get_object_or_404 +from django.conf import settings from core.views import ( DetailFormView, @@ -43,11 +46,17 @@ from core.views import ( CanViewMixin, CanEditPropMixin, ) +from core.models import RealGroup, Notification from haystack.query import SearchQuerySet -from pedagogy.forms import UVForm, UVCommentForm -from pedagogy.models import UV, UVComment +from pedagogy.forms import ( + UVForm, + UVCommentForm, + UVCommentReportForm, + UVCommentModerationForm, +) +from pedagogy.models import UV, UVComment, UVCommentReport # Some mixins @@ -200,28 +209,82 @@ class UVListView(CanViewMixin, CanCreateUVFunctionMixin, ListView): return queryset.filter(id__in=([o.object.id for o in qs])) -class UVCommentReportCreateView(CreateView): +class UVCommentReportCreateView(CanCreateMixin, CreateView): """ Create a new report for an inapropriate comment """ - pass + model = UVCommentReport + form_class = UVCommentReportForm + template_name = "core/edit.jinja" + def dispatch(self, request, *args, **kwargs): + self.uv_comment = get_object_or_404(UVComment, pk=kwargs["comment_id"]) + return super(UVCommentReportCreateView, self).dispatch(request, *args, **kwargs) -class UVCommentReportListView(ListView): - """ - List all UV reports for moderation (Privileged) - """ + def get_form_kwargs(self): + kwargs = super(UVCommentReportCreateView, self).get_form_kwargs() + kwargs["reporter_id"] = self.request.user.id + kwargs["comment_id"] = self.uv_comment.id + return kwargs - pass + def form_valid(self, form): + resp = super(UVCommentReportCreateView, self).form_valid(form) + + # Send a message to moderation admins + for user in ( + RealGroup.objects.filter(id=settings.SITH_GROUP_PEDAGOGY_ADMIN_ID) + .first() + .users.all() + ): + if not user.notifications.filter( + type="PEDAGOGY_MODERATION", viewed=False + ).exists(): + Notification( + user=user, + url=reverse("pedagogy:moderation"), + type="PEDAGOGY_MODERATION", + ).save() + + return resp + + def get_success_url(self): + return reverse_lazy( + "pedagogy:uv_detail", kwargs={"uv_id": self.uv_comment.uv.id} + ) class UVModerationFormView(FormView): """ - List all UVs to moderate and allow to moderate them (Privileged) + Moderation interface (Privileged) """ - pass + form_class = UVCommentModerationForm + template_name = "pedagogy/moderation.jinja" + + def dispatch(self, request, *args, **kwargs): + if not request.user.is_owner(UV()): + raise PermissionDenied + return super(UVModerationFormView, self).dispatch(request, *args, **kwargs) + + def form_valid(self, form): + form_clean = form.clean() + for report in form_clean.get("accepted_reports", []): + try: + report.comment.delete() # Delete the related comment + except ObjectDoesNotExist: + # To avoid errors when two reports points the same comment + pass + for report in form_clean.get("denied_reports", []): + try: + report.delete() # Delete the report itself + except ObjectDoesNotExist: + # To avoid errors when two reports points the same comment + pass + return super(UVModerationFormView, self).form_valid(form) + + def get_success_url(self): + return reverse_lazy("pedagogy:moderation") class UVCreateView(CanCreateMixin, CreateView): diff --git a/sith/settings.py b/sith/settings.py index c02ffff0..ef3dbd17 100644 --- a/sith/settings.py +++ b/sith/settings.py @@ -560,6 +560,10 @@ SITH_LAUNDERETTE_PRICES = {"WASHING": 1.0, "DRYING": 0.75} SITH_NOTIFICATIONS = [ ("POSTER_MODERATION", _("A new poster needs to be moderated")), ("MAILING_MODERATION", _("A new mailing list needs to be moderated")), + ( + "PEDAGOGY_MODERATION", + _("A new pedagogy comment has been signaled for moderation"), + ), ("NEWS_MODERATION", _("There are %s fresh news to be moderated")), ("FILE_MODERATION", _("New files to be moderated")), ("SAS_MODERATION", _("There are %s pictures to be moderated in the SAS")),