mirror of
https://github.com/ae-utbm/sith.git
synced 2024-11-25 18:44:23 +00:00
Merge branch 'pedagogy_v2_moderation' into 'pedagogy_v2'
Pedagogy comments moderation See merge request ae/Sith!215
This commit is contained in:
commit
4f7a8661ba
40
core/migrations/0030_auto_20190704_1500.py
Normal file
40
core/migrations/0030_auto_20190704_1500.py
Normal file
@ -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",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]
|
@ -23,11 +23,12 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from core.views.forms import MarkdownInput
|
from core.views.forms import MarkdownInput
|
||||||
from core.models import User
|
from core.models import User
|
||||||
|
|
||||||
from pedagogy.models import UV, UVComment
|
from pedagogy.models import UV, UVComment, UVCommentReport
|
||||||
|
|
||||||
|
|
||||||
class UVForm(forms.ModelForm):
|
class UVForm(forms.ModelForm):
|
||||||
@ -100,3 +101,45 @@ class UVCommentForm(forms.ModelForm):
|
|||||||
self.fields["author"].initial = author_id
|
self.fields["author"].initial = author_id
|
||||||
self.fields["uv"].queryset = UV.objects.filter(id=uv_id).all()
|
self.fields["uv"].queryset = UV.objects.filter(id=uv_id).all()
|
||||||
self.fields["uv"].initial = uv_id
|
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,
|
||||||
|
)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- 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 __future__ import unicode_literals
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -190,7 +190,7 @@ class Migration(migrations.Migration):
|
|||||||
verbose_name="ID",
|
verbose_name="ID",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
("comment", models.TextField(verbose_name="comment")),
|
("comment", models.TextField(blank=True, verbose_name="comment")),
|
||||||
(
|
(
|
||||||
"grade_global",
|
"grade_global",
|
||||||
models.IntegerField(
|
models.IntegerField(
|
||||||
@ -281,7 +281,26 @@ class Migration(migrations.Migration):
|
|||||||
serialize=False,
|
serialize=False,
|
||||||
verbose_name="ID",
|
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(
|
migrations.CreateModel(
|
||||||
|
@ -27,6 +27,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
from core.models import User
|
from core.models import User
|
||||||
|
|
||||||
@ -165,6 +166,13 @@ class UVComment(models.Model):
|
|||||||
"""
|
"""
|
||||||
return self.author == user or user.is_owner(self.uv)
|
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):
|
def __str__(self):
|
||||||
return "%s - %s" % (self.uv, self.author)
|
return "%s - %s" % (self.uv, self.author)
|
||||||
|
|
||||||
@ -176,7 +184,7 @@ class UVComment(models.Model):
|
|||||||
blank=False,
|
blank=False,
|
||||||
)
|
)
|
||||||
uv = models.ForeignKey(UV, related_name="comments", verbose_name=_("uv"))
|
uv = models.ForeignKey(UV, related_name="comments", verbose_name=_("uv"))
|
||||||
comment = models.TextField(_("comment"))
|
comment = models.TextField(_("comment"), blank=True)
|
||||||
grade_global = models.IntegerField(
|
grade_global = models.IntegerField(
|
||||||
_("global grade"),
|
_("global grade"),
|
||||||
validators=[validators.MinValueValidator(-1), validators.MaxValueValidator(4)],
|
validators=[validators.MinValueValidator(-1), validators.MaxValueValidator(4)],
|
||||||
@ -251,4 +259,19 @@ class UVCommentReport(models.Model):
|
|||||||
Report an inapropriate comment
|
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"))
|
||||||
|
63
pedagogy/templates/pedagogy/moderation.jinja
Normal file
63
pedagogy/templates/pedagogy/moderation.jinja
Normal file
@ -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 %}
|
||||||
|
<form action="{{ url('pedagogy:moderation') }}", id="moderation_delete_form" method="post" enctype="multipart/form-data">
|
||||||
|
{% csrf_token %}
|
||||||
|
<p style="margin-bottom: 1em;">{{ select_all_checkbox("moderation_delete_form") }}</p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans %}UV{% endtrans %}</td>
|
||||||
|
<td>{% trans %}Comment{% endtrans %}</td>
|
||||||
|
<td>{% trans %}Reason{% endtrans %}</td>
|
||||||
|
<td>{% trans %}Delete{% endtrans %}</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% set queryset = form.accepted_reports.field.queryset %}
|
||||||
|
{% for widget in form.accepted_reports.subwidgets %}
|
||||||
|
{% set report = queryset.get(id=widget.data.value) %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="{{ url('pedagogy:uv_detail', uv_id=report.comment.uv.id) }}">{{ report.comment.uv }}</a></td>
|
||||||
|
<td>{{ report.comment.comment|markdown }}</td>
|
||||||
|
<td>{{ report.reason|markdown }}</td>
|
||||||
|
<td>{{ widget.tag() }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p><input type="submit" value="{% trans %}Delete comments{% endtrans %}"></p>
|
||||||
|
</form>
|
||||||
|
<form action="{{ url('pedagogy:moderation') }}", id="moderation_keep_form" method="post" enctype="multipart/form-data">
|
||||||
|
{% csrf_token %}
|
||||||
|
<p style="margin-bottom: 1em;">{{ select_all_checkbox("moderation_keep_form") }}</p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans %}UV{% endtrans %}</td>
|
||||||
|
<td>{% trans %}Comment{% endtrans %}</td>
|
||||||
|
<td>{% trans %}Reason{% endtrans %}</td>
|
||||||
|
<td>{% trans %}Delete{% endtrans %}</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% set queryset = form.denied_reports.field.queryset %}
|
||||||
|
{% for widget in form.denied_reports.subwidgets %}
|
||||||
|
{% set report = queryset.get(id=widget.data.value) %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="{{ url('pedagogy:uv_detail', uv_id=report.comment.uv.id) }}">{{ report.comment.uv }}</a></td>
|
||||||
|
<td>{{ report.comment.comment|markdown }}</td>
|
||||||
|
<td>{{ report.reason|markdown }}</td>
|
||||||
|
<td>{{ widget.tag() }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p><input type="submit" value="{% trans %}Delete report{% endtrans %}"></p>
|
||||||
|
</form>
|
||||||
|
{% endblock content %}
|
@ -29,6 +29,10 @@
|
|||||||
<p><a href="{{ url('pedagogy:comment_update', comment_id=comment.id) }}">{% trans %}Edit{% endtrans %}</a></p>
|
<p><a href="{{ url('pedagogy:comment_update', comment_id=comment.id) }}">{% trans %}Edit{% endtrans %}</a></p>
|
||||||
<p><a href="{{ url('pedagogy:comment_delete', comment_id=comment.id) }}">{% trans %}Delete{% endtrans %}</a></p>
|
<p><a href="{{ url('pedagogy:comment_delete', comment_id=comment.id) }}">{% trans %}Delete{% endtrans %}</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<p><a href="{{ url('pedagogy:comment_report', comment_id=comment.id) }}">{% trans %}Report{% endtrans %}</a></p>
|
||||||
|
{% if comment.is_reported %}
|
||||||
|
<p>{% trans %}This comment has been reported{% endtrans %}</p>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@ -22,13 +22,14 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.core.management import call_command
|
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=[]):
|
def create_uv_template(user_id, code="IFC1", exclude_list=[]):
|
||||||
@ -727,3 +728,323 @@ class UVSearchTest(TestCase):
|
|||||||
# Search with credit type
|
# Search with credit type
|
||||||
response = self.client.get(reverse("pedagogy:guide"), {"credit_type": "TM"})
|
response = self.client.get(reverse("pedagogy:guide"), {"credit_type": "TM"})
|
||||||
self.assertNotContains(response, text="PA00")
|
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
|
||||||
|
)
|
||||||
|
@ -46,7 +46,6 @@ urlpatterns = [
|
|||||||
name="comment_report",
|
name="comment_report",
|
||||||
),
|
),
|
||||||
# Moderation
|
# Moderation
|
||||||
url(r"^reported$", UVCommentReportListView.as_view(), name="comment_report_list"),
|
|
||||||
url(r"^moderation$", UVModerationFormView.as_view(), name="moderation"),
|
url(r"^moderation$", UVModerationFormView.as_view(), name="moderation"),
|
||||||
# Administration : Create Update Delete Edit
|
# Administration : Create Update Delete Edit
|
||||||
url(r"^uv/create$", UVCreateView.as_view(), name="uv_create"),
|
url(r"^uv/create$", UVCreateView.as_view(), name="uv_create"),
|
||||||
|
@ -34,7 +34,10 @@ from django.views.generic import (
|
|||||||
from django.core import serializers
|
from django.core import serializers
|
||||||
from django.utils import html
|
from django.utils import html
|
||||||
from django.http import HttpResponse
|
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 (
|
from core.views import (
|
||||||
DetailFormView,
|
DetailFormView,
|
||||||
@ -43,11 +46,17 @@ from core.views import (
|
|||||||
CanViewMixin,
|
CanViewMixin,
|
||||||
CanEditPropMixin,
|
CanEditPropMixin,
|
||||||
)
|
)
|
||||||
|
from core.models import RealGroup, Notification
|
||||||
|
|
||||||
from haystack.query import SearchQuerySet
|
from haystack.query import SearchQuerySet
|
||||||
|
|
||||||
from pedagogy.forms import UVForm, UVCommentForm
|
from pedagogy.forms import (
|
||||||
from pedagogy.models import UV, UVComment
|
UVForm,
|
||||||
|
UVCommentForm,
|
||||||
|
UVCommentReportForm,
|
||||||
|
UVCommentModerationForm,
|
||||||
|
)
|
||||||
|
from pedagogy.models import UV, UVComment, UVCommentReport
|
||||||
|
|
||||||
# Some mixins
|
# Some mixins
|
||||||
|
|
||||||
@ -200,28 +209,82 @@ class UVListView(CanViewMixin, CanCreateUVFunctionMixin, ListView):
|
|||||||
return queryset.filter(id__in=([o.object.id for o in qs]))
|
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
|
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):
|
def get_form_kwargs(self):
|
||||||
"""
|
kwargs = super(UVCommentReportCreateView, self).get_form_kwargs()
|
||||||
List all UV reports for moderation (Privileged)
|
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):
|
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):
|
class UVCreateView(CanCreateMixin, CreateView):
|
||||||
|
@ -560,6 +560,10 @@ SITH_LAUNDERETTE_PRICES = {"WASHING": 1.0, "DRYING": 0.75}
|
|||||||
SITH_NOTIFICATIONS = [
|
SITH_NOTIFICATIONS = [
|
||||||
("POSTER_MODERATION", _("A new poster needs to be moderated")),
|
("POSTER_MODERATION", _("A new poster needs to be moderated")),
|
||||||
("MAILING_MODERATION", _("A new mailing list 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")),
|
("NEWS_MODERATION", _("There are %s fresh news to be moderated")),
|
||||||
("FILE_MODERATION", _("New files to be moderated")),
|
("FILE_MODERATION", _("New files to be moderated")),
|
||||||
("SAS_MODERATION", _("There are %s pictures to be moderated in the SAS")),
|
("SAS_MODERATION", _("There are %s pictures to be moderated in the SAS")),
|
||||||
|
Loading…
Reference in New Issue
Block a user