mirror of
https://github.com/ae-utbm/sith.git
synced 2025-01-22 06:51:09 +00:00
Improve pedagogy permissions
This commit is contained in:
parent
61170c0918
commit
0d95c3b9c9
@ -256,7 +256,7 @@ class ClubMembersView(ClubTabsMixin, CanViewMixin, DetailFormView):
|
|||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
kwargs["request_user"] = self.request.user
|
kwargs["request_user"] = self.request.user
|
||||||
kwargs["club"] = self.get_object()
|
kwargs["club"] = self.object
|
||||||
kwargs["club_members"] = self.members
|
kwargs["club_members"] = self.members
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
@ -273,9 +273,9 @@ class ClubMembersView(ClubTabsMixin, CanViewMixin, DetailFormView):
|
|||||||
users = data.pop("users", [])
|
users = data.pop("users", [])
|
||||||
users_old = data.pop("users_old", [])
|
users_old = data.pop("users_old", [])
|
||||||
for user in users:
|
for user in users:
|
||||||
Membership(club=self.get_object(), user=user, **data).save()
|
Membership(club=self.object, user=user, **data).save()
|
||||||
for user in users_old:
|
for user in users_old:
|
||||||
membership = self.get_object().get_membership_for(user)
|
membership = self.object.get_membership_for(user)
|
||||||
membership.end_date = timezone.now()
|
membership.end_date = timezone.now()
|
||||||
membership.save()
|
membership.save()
|
||||||
return resp
|
return resp
|
||||||
@ -285,9 +285,7 @@ class ClubMembersView(ClubTabsMixin, CanViewMixin, DetailFormView):
|
|||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_success_url(self, **kwargs):
|
def get_success_url(self, **kwargs):
|
||||||
return reverse_lazy(
|
return reverse_lazy("club:club_members", kwargs={"club_id": self.object.id})
|
||||||
"club:club_members", kwargs={"club_id": self.get_object().id}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ClubOldMembersView(ClubTabsMixin, CanViewMixin, DetailView):
|
class ClubOldMembersView(ClubTabsMixin, CanViewMixin, DetailView):
|
||||||
|
@ -895,13 +895,16 @@ Welcome to the wiki page!
|
|||||||
|
|
||||||
subscribers = Group.objects.create(name="Subscribers")
|
subscribers = Group.objects.create(name="Subscribers")
|
||||||
subscribers.permissions.add(
|
subscribers.permissions.add(
|
||||||
*list(perms.filter(codename__in=["add_news", "add_uvcommentreport"]))
|
*list(perms.filter(codename__in=["add_news", "add_uvcomment"]))
|
||||||
)
|
)
|
||||||
old_subscribers = Group.objects.create(name="Old subscribers")
|
old_subscribers = Group.objects.create(name="Old subscribers")
|
||||||
old_subscribers.permissions.add(
|
old_subscribers.permissions.add(
|
||||||
*list(
|
*list(
|
||||||
perms.filter(
|
perms.filter(
|
||||||
codename__in=[
|
codename__in=[
|
||||||
|
"view_uv",
|
||||||
|
"view_uvcomment",
|
||||||
|
"add_uvcommentreport",
|
||||||
"view_user",
|
"view_user",
|
||||||
"view_picture",
|
"view_picture",
|
||||||
"view_album",
|
"view_album",
|
||||||
@ -973,9 +976,9 @@ Welcome to the wiki page!
|
|||||||
)
|
)
|
||||||
pedagogy_admin.permissions.add(
|
pedagogy_admin.permissions.add(
|
||||||
*list(
|
*list(
|
||||||
perms.filter(content_type__app_label="pedagogy").values_list(
|
perms.filter(content_type__app_label="pedagogy")
|
||||||
"pk", flat=True
|
.exclude(codename__in=["change_uvcomment"])
|
||||||
)
|
.values_list("pk", flat=True)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.reset_index("core", "auth")
|
self.reset_index("core", "auth")
|
||||||
|
@ -28,8 +28,7 @@ from django.http import (
|
|||||||
HttpResponseServerError,
|
HttpResponseServerError,
|
||||||
)
|
)
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.utils.functional import cached_property
|
from django.views.generic.detail import BaseDetailView
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
|
||||||
from django.views.generic.edit import FormView
|
from django.views.generic.edit import FormView
|
||||||
from sentry_sdk import last_event_id
|
from sentry_sdk import last_event_id
|
||||||
|
|
||||||
@ -54,17 +53,12 @@ def internal_servor_error(request):
|
|||||||
return HttpResponseServerError(render(request, "core/500.jinja"))
|
return HttpResponseServerError(render(request, "core/500.jinja"))
|
||||||
|
|
||||||
|
|
||||||
class DetailFormView(SingleObjectMixin, FormView):
|
class DetailFormView(FormView, BaseDetailView):
|
||||||
"""Class that allow both a detail view and a form view."""
|
"""Class that allow both a detail view and a form view."""
|
||||||
|
|
||||||
def get_object(self):
|
def post(self, request, *args, **kwargs):
|
||||||
"""Get current group from id in url."""
|
self.object = self.get_object()
|
||||||
return self.cached_object
|
return super().post(request, *args, **kwargs)
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def cached_object(self):
|
|
||||||
"""Optimisation on group retrieval."""
|
|
||||||
return super().get_object()
|
|
||||||
|
|
||||||
|
|
||||||
# F403: those star-imports would be hellish to refactor
|
# F403: those star-imports would be hellish to refactor
|
||||||
|
@ -66,7 +66,6 @@ from core.views.forms import (
|
|||||||
)
|
)
|
||||||
from core.views.mixins import QuickNotifMixin, TabedViewMixin
|
from core.views.mixins import QuickNotifMixin, TabedViewMixin
|
||||||
from counter.models import Refilling, Selling
|
from counter.models import Refilling, Selling
|
||||||
from counter.views.student_card import StudentCardFormView
|
|
||||||
from eboutic.models import Invoice
|
from eboutic.models import Invoice
|
||||||
from subscription.models import Subscription
|
from subscription.models import Subscription
|
||||||
from trombi.views import UserTrombiForm
|
from trombi.views import UserTrombiForm
|
||||||
@ -566,6 +565,8 @@ class UserPreferencesView(UserTabsMixin, CanEditMixin, UpdateView):
|
|||||||
if not hasattr(self.object, "trombi_user"):
|
if not hasattr(self.object, "trombi_user"):
|
||||||
kwargs["trombi_form"] = UserTrombiForm()
|
kwargs["trombi_form"] = UserTrombiForm()
|
||||||
if hasattr(self.object, "customer"):
|
if hasattr(self.object, "customer"):
|
||||||
|
from counter.views.student_card import StudentCardFormView
|
||||||
|
|
||||||
kwargs["student_card_fragment"] = StudentCardFormView.get_template_data(
|
kwargs["student_card_fragment"] = StudentCardFormView.get_template_data(
|
||||||
self.object.customer
|
self.object.customer
|
||||||
).render(self.request)
|
).render(self.request)
|
||||||
|
@ -20,10 +20,12 @@
|
|||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
from typing import Self
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models import Exists, OuterRef
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
@ -145,14 +147,6 @@ class UV(models.Model):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("pedagogy:uv_detail", kwargs={"uv_id": self.id})
|
return reverse("pedagogy:uv_detail", kwargs={"uv_id": self.id})
|
||||||
|
|
||||||
def is_owned_by(self, user):
|
|
||||||
"""Can be created by superuser, root or pedagogy admin user."""
|
|
||||||
return user.is_in_group(pk=settings.SITH_GROUP_PEDAGOGY_ADMIN_ID)
|
|
||||||
|
|
||||||
def can_be_viewed_by(self, user):
|
|
||||||
"""Only visible by subscribers."""
|
|
||||||
return user.is_subscribed
|
|
||||||
|
|
||||||
def __grade_average_generic(self, field):
|
def __grade_average_generic(self, field):
|
||||||
comments = self.comments.filter(**{field + "__gte": 0})
|
comments = self.comments.filter(**{field + "__gte": 0})
|
||||||
if not comments.exists():
|
if not comments.exists():
|
||||||
@ -191,6 +185,22 @@ class UV(models.Model):
|
|||||||
return self.__grade_average_generic("grade_work_load")
|
return self.__grade_average_generic("grade_work_load")
|
||||||
|
|
||||||
|
|
||||||
|
class UVCommentQuerySet(models.QuerySet):
|
||||||
|
def viewable_by(self, user: User) -> Self:
|
||||||
|
if user.has_perms(["pedagogy.view_uvcomment", "pedagogy.view_uvcommentreport"]):
|
||||||
|
# the user can view uv comment reports,
|
||||||
|
# so he can view non-moderated comments
|
||||||
|
return self
|
||||||
|
if user.has_perm("pedagogy.view_uvcomment"):
|
||||||
|
return self.filter(reports=None)
|
||||||
|
return self.filter(author=user)
|
||||||
|
|
||||||
|
def annotate_is_reported(self) -> Self:
|
||||||
|
return self.annotate(
|
||||||
|
is_reported=Exists(UVCommentReport.objects.filter(comment=OuterRef("pk")))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class UVComment(models.Model):
|
class UVComment(models.Model):
|
||||||
"""A comment about an UV."""
|
"""A comment about an UV."""
|
||||||
|
|
||||||
@ -243,6 +253,8 @@ class UVComment(models.Model):
|
|||||||
)
|
)
|
||||||
publish_date = models.DateTimeField(_("publish date"), blank=True)
|
publish_date = models.DateTimeField(_("publish date"), blank=True)
|
||||||
|
|
||||||
|
objects = UVCommentQuerySet.as_manager()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.uv} - {self.author}"
|
return f"{self.uv} - {self.author}"
|
||||||
|
|
||||||
@ -251,15 +263,6 @@ class UVComment(models.Model):
|
|||||||
self.publish_date = timezone.now()
|
self.publish_date = timezone.now()
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def is_owned_by(self, user):
|
|
||||||
"""Is owned by a pedagogy admin, a superuser or the author himself."""
|
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
||||||
# TODO : it seems that some views were meant to be implemented
|
# TODO : it seems that some views were meant to be implemented
|
||||||
# to use this model.
|
# to use this model.
|
||||||
@ -323,7 +326,3 @@ class UVCommentReport(models.Model):
|
|||||||
@cached_property
|
@cached_property
|
||||||
def uv(self):
|
def uv(self):
|
||||||
return self.comment.uv
|
return self.comment.uv
|
||||||
|
|
||||||
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)
|
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
{% endblock head %}
|
{% endblock head %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% if can_create_uv %}
|
{% if user.has_perm("pedagogy.add_uv") %}
|
||||||
<div class="action-bar">
|
<div class="action-bar">
|
||||||
<p>
|
<p>
|
||||||
<a href="{{ url('pedagogy:uv_create') }}">{% trans %}Create UV{% endtrans %}</a>
|
<a href="{{ url('pedagogy:uv_create') }}">{% trans %}Create UV{% endtrans %}</a>
|
||||||
@ -94,8 +94,10 @@
|
|||||||
<td>{% trans %}Credit type{% endtrans %}</td>
|
<td>{% trans %}Credit type{% endtrans %}</td>
|
||||||
<td><i class="fa fa-leaf"></i></td>
|
<td><i class="fa fa-leaf"></i></td>
|
||||||
<td><i class="fa-regular fa-sun"></i></td>
|
<td><i class="fa-regular fa-sun"></i></td>
|
||||||
{% if can_create_uv %}
|
{%- if user.has_perm("pedagogy.change_uv") -%}
|
||||||
<td>{% trans %}Edit{% endtrans %}</td>
|
<td>{% trans %}Edit{% endtrans %}</td>
|
||||||
|
{%- endif -%}
|
||||||
|
{%- if user.has_perm("pedagogy.delete_uv") -%}
|
||||||
<td>{% trans %}Delete{% endtrans %}</td>
|
<td>{% trans %}Delete{% endtrans %}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
@ -109,8 +111,10 @@
|
|||||||
<td x-text="uv.credit_type"></td>
|
<td x-text="uv.credit_type"></td>
|
||||||
<td><i :class="uv.semester.includes('AUTUMN') && 'fa fa-leaf'"></i></td>
|
<td><i :class="uv.semester.includes('AUTUMN') && 'fa fa-leaf'"></i></td>
|
||||||
<td><i :class="uv.semester.includes('SPRING') && 'fa-regular fa-sun'"></i></td>
|
<td><i :class="uv.semester.includes('SPRING') && 'fa-regular fa-sun'"></i></td>
|
||||||
{% if can_create_uv -%}
|
{%- if user.has_perm("pedagogy.change_uv") -%}
|
||||||
<td><a :href="`/pedagogy/uv/${uv.id}/edit`">{% trans %}Edit{% endtrans %}</a></td>
|
<td><a :href="`/pedagogy/uv/${uv.id}/edit`">{% trans %}Edit{% endtrans %}</a></td>
|
||||||
|
{%- endif -%}
|
||||||
|
{%- if user.has_perm("pedagogy.delete_uv") -%}
|
||||||
<td><a :href="`/pedagogy/uv/${uv.id}/delete`">{% trans %}Delete{% endtrans %}</a></td>
|
<td><a :href="`/pedagogy/uv/${uv.id}/delete`">{% trans %}Delete{% endtrans %}</a></td>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -89,7 +89,7 @@
|
|||||||
<div id="leave_comment_not_allowed">
|
<div id="leave_comment_not_allowed">
|
||||||
<p>{% trans %}You already posted a comment on this UV. If you want to comment again, please modify or delete your previous comment.{% endtrans %}</p>
|
<p>{% trans %}You already posted a comment on this UV. If you want to comment again, please modify or delete your previous comment.{% endtrans %}</p>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% elif user.has_perm("pedagogy.add_uvcomment") %}
|
||||||
<div id="leave_comment">
|
<div id="leave_comment">
|
||||||
<h2>{% trans %}Leave comment{% endtrans %}</h2>
|
<h2>{% trans %}Leave comment{% endtrans %}</h2>
|
||||||
<div>
|
<div>
|
||||||
@ -146,9 +146,9 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
{% if object.comments.exists() %}
|
{% if comments %}
|
||||||
<h2>{% trans %}Comments{% endtrans %}</h2>
|
<h2>{% trans %}Comments{% endtrans %}</h2>
|
||||||
{% for comment in object.comments.order_by("-publish_date").all() %}
|
{% for comment in comments %}
|
||||||
<div id="{{ comment.id }}" class="comment-container">
|
<div id="{{ comment.id }}" class="comment-container">
|
||||||
|
|
||||||
<div class="grade-block">
|
<div class="grade-block">
|
||||||
@ -183,16 +183,28 @@
|
|||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if user.is_owner(comment) %}
|
{% if comment.author_id == user.id or user.has_perm("pedagogy.change_comment") %}
|
||||||
<p class="actions">
|
<p class="actions">
|
||||||
<a href="{{ url('pedagogy:comment_update', comment_id=comment.id) }}">{% trans %}Edit{% endtrans %}</a>
|
<a href="{{ url('pedagogy:comment_update', comment_id=comment.id) }}">
|
||||||
<a href="{{ url('pedagogy:comment_delete', comment_id=comment.id) }}">{% trans %}Delete{% endtrans %}</a>
|
{% trans %}Edit{% endtrans %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if comment.author_id == user.id or user.has_perm("pedagogy.delete_comment") %}
|
||||||
|
<a href="{{ url('pedagogy:comment_delete', comment_id=comment.id) }}">
|
||||||
|
{% trans %}Delete{% endtrans %}
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="comment-end-bar">
|
<div class="comment-end-bar">
|
||||||
<div class="report"><p><a href="{{ url('pedagogy:comment_report', comment_id=comment.id) }}">{% trans %}Report this comment{% endtrans %}</a></p></div>
|
<div class="report">
|
||||||
|
<p>
|
||||||
|
<a href="{{ url('pedagogy:comment_report', comment_id=comment.id) }}">
|
||||||
|
{% trans %}Report this comment{% endtrans %}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="date"><p>{{ comment.publish_date.strftime('%d/%m/%Y') }}</p></div>
|
<div class="date"><p>{{ comment.publish_date.strftime('%d/%m/%Y') }}</p></div>
|
||||||
|
|
||||||
@ -209,7 +221,7 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$("#return_noscript").hide();
|
$("#return_noscript").hide();
|
||||||
$("#return_js").show();
|
$("#return_js").show();
|
||||||
var icons = {
|
const icons = {
|
||||||
header: "fa fa-toggle-right",
|
header: "fa fa-toggle-right",
|
||||||
activeHeader: "fa fa-toggle-down"
|
activeHeader: "fa fa-toggle-down"
|
||||||
};
|
};
|
||||||
|
@ -20,14 +20,18 @@
|
|||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib.auth.models import Permission
|
||||||
from django.test import Client, TestCase
|
from django.test import Client, TestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from model_bakery import baker
|
||||||
from pytest_django.asserts import assertRedirects
|
from pytest_django.asserts import assertRedirects
|
||||||
|
|
||||||
|
from core.baker_recipes import old_subscriber_user, subscriber_user
|
||||||
from core.models import Notification, User
|
from core.models import Notification, User
|
||||||
from pedagogy.models import UV, UVComment, UVCommentReport
|
from pedagogy.models import UV, UVComment, UVCommentReport
|
||||||
|
|
||||||
@ -144,17 +148,17 @@ class TestUVCreation(TestCase):
|
|||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("username", "expected_code"),
|
("user_factory", "expected_code"),
|
||||||
[
|
[
|
||||||
("root", 200),
|
(subscriber_user.make, 200),
|
||||||
("tutu", 200),
|
(old_subscriber_user.make, 200),
|
||||||
("sli", 200),
|
(lambda: baker.make(User), 403),
|
||||||
("old_subscriber", 200),
|
|
||||||
("public", 403),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_guide_permissions(client: Client, username: str, expected_code: int):
|
def test_guide_permissions(
|
||||||
client.force_login(User.objects.get(username=username))
|
client: Client, user_factory: Callable[[], User], expected_code: int
|
||||||
|
):
|
||||||
|
client.force_login(user_factory())
|
||||||
res = client.get(reverse("pedagogy:guide"))
|
res = client.get(reverse("pedagogy:guide"))
|
||||||
assert res.status_code == expected_code
|
assert res.status_code == expected_code
|
||||||
|
|
||||||
@ -190,17 +194,12 @@ class TestUVDelete(TestCase):
|
|||||||
def test_uv_delete_pedagogy_unauthorized_fail(self):
|
def test_uv_delete_pedagogy_unauthorized_fail(self):
|
||||||
# Anonymous user
|
# Anonymous user
|
||||||
response = self.client.post(self.delete_uv_url)
|
response = self.client.post(self.delete_uv_url)
|
||||||
assert response.status_code == 403
|
assertRedirects(response, reverse("core:login") + f"?next={self.delete_uv_url}")
|
||||||
assert UV.objects.filter(pk=self.uv.pk).exists()
|
assert UV.objects.filter(pk=self.uv.pk).exists()
|
||||||
|
|
||||||
# Not subscribed user
|
for user in baker.make(User), subscriber_user.make():
|
||||||
self.client.force_login(self.guy)
|
with self.subTest():
|
||||||
response = self.client.post(self.delete_uv_url)
|
self.client.force_login(user)
|
||||||
assert response.status_code == 403
|
|
||||||
assert UV.objects.filter(pk=self.uv.pk).exists()
|
|
||||||
|
|
||||||
# Simply subscribed user
|
|
||||||
self.client.force_login(self.sli)
|
|
||||||
response = self.client.post(self.delete_uv_url)
|
response = self.client.post(self.delete_uv_url)
|
||||||
assert response.status_code == 403
|
assert response.status_code == 403
|
||||||
assert UV.objects.filter(pk=self.uv.pk).exists()
|
assert UV.objects.filter(pk=self.uv.pk).exists()
|
||||||
@ -249,7 +248,7 @@ class TestUVUpdate(TestCase):
|
|||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
self.update_uv_url, create_uv_template(self.bibou.id, code="PA00")
|
self.update_uv_url, create_uv_template(self.bibou.id, code="PA00")
|
||||||
)
|
)
|
||||||
assert response.status_code == 403
|
assertRedirects(response, reverse("core:login") + f"?next={self.update_uv_url}")
|
||||||
|
|
||||||
# Not subscribed user
|
# Not subscribed user
|
||||||
self.client.force_login(self.guy)
|
self.client.force_login(self.guy)
|
||||||
@ -312,7 +311,7 @@ class TestUVCommentCreationAndDisplay(TestCase):
|
|||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
self.uv_url, create_uv_comment_template(self.bibou.id)
|
self.uv_url, create_uv_comment_template(self.bibou.id)
|
||||||
)
|
)
|
||||||
self.assertRedirects(response, self.uv_url)
|
assertRedirects(response, self.uv_url)
|
||||||
response = self.client.get(self.uv_url)
|
response = self.client.get(self.uv_url)
|
||||||
self.assertContains(response, text="Superbe UV")
|
self.assertContains(response, text="Superbe UV")
|
||||||
|
|
||||||
@ -338,7 +337,7 @@ class TestUVCommentCreationAndDisplay(TestCase):
|
|||||||
nb_comments = self.uv.comments.count()
|
nb_comments = self.uv.comments.count()
|
||||||
# Test with anonymous user
|
# Test with anonymous user
|
||||||
response = self.client.post(self.uv_url, create_uv_comment_template(0))
|
response = self.client.post(self.uv_url, create_uv_comment_template(0))
|
||||||
assert response.status_code == 403
|
assertRedirects(response, reverse("core:login") + f"?next={self.uv_url}")
|
||||||
|
|
||||||
# Test with non subscribed user
|
# Test with non subscribed user
|
||||||
self.client.force_login(self.guy)
|
self.client.force_login(self.guy)
|
||||||
@ -405,61 +404,34 @@ class TestUVCommentDelete(TestCase):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
cls.bibou = User.objects.get(username="root")
|
cls.comment = baker.make(UVComment)
|
||||||
cls.tutu = User.objects.get(username="tutu")
|
|
||||||
cls.sli = User.objects.get(username="sli")
|
|
||||||
cls.guy = User.objects.get(username="guy")
|
|
||||||
cls.krophil = User.objects.get(username="krophil")
|
|
||||||
|
|
||||||
def setUp(self):
|
def test_uv_comment_delete_success(self):
|
||||||
comment_kwargs = create_uv_comment_template(
|
url = reverse("pedagogy:comment_delete", kwargs={"comment_id": self.comment.id})
|
||||||
User.objects.get(username="krophil").id
|
for user in (
|
||||||
)
|
baker.make(User, is_superuser=True),
|
||||||
comment_kwargs["author"] = User.objects.get(id=comment_kwargs["author"])
|
baker.make(
|
||||||
comment_kwargs["uv"] = UV.objects.get(id=comment_kwargs["uv"])
|
User, user_permissions=[Permission.objects.get(codename="view_uv")]
|
||||||
self.comment = UVComment(**comment_kwargs)
|
),
|
||||||
self.comment.save()
|
self.comment.author,
|
||||||
|
):
|
||||||
def test_uv_comment_delete_root_success(self):
|
with self.subTest():
|
||||||
self.client.force_login(self.bibou)
|
self.client.force_login(user)
|
||||||
self.client.post(
|
self.client.post(url)
|
||||||
reverse("pedagogy:comment_delete", kwargs={"comment_id": self.comment.id})
|
|
||||||
)
|
|
||||||
assert not UVComment.objects.filter(id=self.comment.id).exists()
|
|
||||||
|
|
||||||
def test_uv_comment_delete_pedagogy_admin_success(self):
|
|
||||||
self.client.force_login(self.tutu)
|
|
||||||
self.client.post(
|
|
||||||
reverse("pedagogy:comment_delete", kwargs={"comment_id": self.comment.id})
|
|
||||||
)
|
|
||||||
assert not UVComment.objects.filter(id=self.comment.id).exists()
|
|
||||||
|
|
||||||
def test_uv_comment_delete_author_success(self):
|
|
||||||
self.client.force_login(self.krophil)
|
|
||||||
self.client.post(
|
|
||||||
reverse("pedagogy:comment_delete", kwargs={"comment_id": self.comment.id})
|
|
||||||
)
|
|
||||||
assert not UVComment.objects.filter(id=self.comment.id).exists()
|
assert not UVComment.objects.filter(id=self.comment.id).exists()
|
||||||
|
|
||||||
def test_uv_comment_delete_unauthorized_fail(self):
|
def test_uv_comment_delete_unauthorized_fail(self):
|
||||||
|
url = reverse("pedagogy:comment_delete", kwargs={"comment_id": self.comment.id})
|
||||||
|
|
||||||
# Anonymous user
|
# Anonymous user
|
||||||
response = self.client.post(
|
response = self.client.post(url)
|
||||||
reverse("pedagogy:comment_delete", kwargs={"comment_id": self.comment.id})
|
assertRedirects(response, reverse("core:login") + f"?next={url}")
|
||||||
)
|
|
||||||
assert response.status_code == 403
|
|
||||||
|
|
||||||
# Unsbscribed user
|
# Unsbscribed user
|
||||||
self.client.force_login(self.guy)
|
for user in baker.make(User), subscriber_user.make():
|
||||||
response = self.client.post(
|
with self.subTest():
|
||||||
reverse("pedagogy:comment_delete", kwargs={"comment_id": self.comment.id})
|
self.client.force_login(user)
|
||||||
)
|
response = self.client.post(url)
|
||||||
assert response.status_code == 403
|
|
||||||
|
|
||||||
# Subscribed user (not author of the comment)
|
|
||||||
self.client.force_login(self.sli)
|
|
||||||
response = self.client.post(
|
|
||||||
reverse("pedagogy:comment_delete", kwargs={"comment_id": self.comment.id})
|
|
||||||
)
|
|
||||||
assert response.status_code == 403
|
assert response.status_code == 403
|
||||||
|
|
||||||
# Check that the comment still exists
|
# Check that the comment still exists
|
||||||
@ -499,16 +471,6 @@ class TestUVCommentUpdate(TestCase):
|
|||||||
self.comment.refresh_from_db()
|
self.comment.refresh_from_db()
|
||||||
self.assertEqual(self.comment.comment, self.comment_edit["comment"])
|
self.assertEqual(self.comment.comment, self.comment_edit["comment"])
|
||||||
|
|
||||||
def test_uv_comment_update_pedagogy_admin_success(self):
|
|
||||||
self.client.force_login(self.tutu)
|
|
||||||
response = self.client.post(
|
|
||||||
reverse("pedagogy:comment_update", kwargs={"comment_id": self.comment.id}),
|
|
||||||
self.comment_edit,
|
|
||||||
)
|
|
||||||
assert response.status_code == 302
|
|
||||||
self.comment.refresh_from_db()
|
|
||||||
self.assertEqual(self.comment.comment, self.comment_edit["comment"])
|
|
||||||
|
|
||||||
def test_uv_comment_update_author_success(self):
|
def test_uv_comment_update_author_success(self):
|
||||||
self.client.force_login(self.krophil)
|
self.client.force_login(self.krophil)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
@ -520,25 +482,18 @@ class TestUVCommentUpdate(TestCase):
|
|||||||
self.assertEqual(self.comment.comment, self.comment_edit["comment"])
|
self.assertEqual(self.comment.comment, self.comment_edit["comment"])
|
||||||
|
|
||||||
def test_uv_comment_update_unauthorized_fail(self):
|
def test_uv_comment_update_unauthorized_fail(self):
|
||||||
|
url = reverse("pedagogy:comment_update", kwargs={"comment_id": self.comment.id})
|
||||||
# Anonymous user
|
# Anonymous user
|
||||||
response = self.client.post(
|
response = self.client.post(url, self.comment_edit)
|
||||||
reverse("pedagogy:comment_update", kwargs={"comment_id": self.comment.id}),
|
assertRedirects(response, reverse("core:login") + f"?next={url}")
|
||||||
self.comment_edit,
|
|
||||||
)
|
|
||||||
assert response.status_code == 403
|
|
||||||
|
|
||||||
# Unsbscribed user
|
# Unsbscribed user
|
||||||
response = self.client.post(
|
self.client.force_login(baker.make(User))
|
||||||
reverse("pedagogy:comment_update", kwargs={"comment_id": self.comment.id}),
|
response = self.client.post(url, self.comment_edit)
|
||||||
self.comment_edit,
|
|
||||||
)
|
|
||||||
assert response.status_code == 403
|
assert response.status_code == 403
|
||||||
|
|
||||||
# Subscribed user (not author of the comment)
|
# Subscribed user (not author of the comment)
|
||||||
response = self.client.post(
|
response = self.client.post(url, self.comment_edit)
|
||||||
reverse("pedagogy:comment_update", kwargs={"comment_id": self.comment.id}),
|
|
||||||
self.comment_edit,
|
|
||||||
)
|
|
||||||
assert response.status_code == 403
|
assert response.status_code == 403
|
||||||
|
|
||||||
# Check that the comment hasn't change
|
# Check that the comment hasn't change
|
||||||
@ -611,18 +566,19 @@ class TestUVModerationForm(TestCase):
|
|||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
def test_access_unauthorized_fail(self):
|
def test_access_unauthorized_fail(self):
|
||||||
|
url = reverse("pedagogy:moderation")
|
||||||
# Test with anonymous user
|
# Test with anonymous user
|
||||||
response = self.client.get(reverse("pedagogy:moderation"))
|
response = self.client.get(url)
|
||||||
assert response.status_code == 403
|
assertRedirects(response, reverse("core:login") + f"?next={url}")
|
||||||
|
|
||||||
# Test with unsubscribed user
|
# Test with unsubscribed user
|
||||||
self.client.force_login(self.guy)
|
self.client.force_login(self.guy)
|
||||||
response = self.client.get(reverse("pedagogy:moderation"))
|
response = self.client.get(url)
|
||||||
assert response.status_code == 403
|
assert response.status_code == 403
|
||||||
|
|
||||||
# Test with subscribed user
|
# Test with subscribed user
|
||||||
self.client.force_login(self.sli)
|
self.client.force_login(self.sli)
|
||||||
response = self.client.get(reverse("pedagogy:moderation"))
|
response = self.client.get(url)
|
||||||
assert response.status_code == 403
|
assert response.status_code == 403
|
||||||
|
|
||||||
def test_do_nothing(self):
|
def test_do_nothing(self):
|
||||||
|
@ -22,8 +22,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||||
from django.core.exceptions import PermissionDenied
|
|
||||||
from django.db.models import Exists, OuterRef
|
from django.db.models import Exists, OuterRef
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
@ -35,7 +34,7 @@ from django.views.generic import (
|
|||||||
UpdateView,
|
UpdateView,
|
||||||
)
|
)
|
||||||
|
|
||||||
from core.auth.mixins import CanEditPropMixin, CanViewMixin, FormerSubscriberMixin
|
from core.auth.mixins import PermissionOrAuthorRequiredMixin
|
||||||
from core.models import Notification, User
|
from core.models import Notification, User
|
||||||
from core.views import DetailFormView
|
from core.views import DetailFormView
|
||||||
from pedagogy.forms import (
|
from pedagogy.forms import (
|
||||||
@ -47,7 +46,7 @@ from pedagogy.forms import (
|
|||||||
from pedagogy.models import UV, UVComment, UVCommentReport
|
from pedagogy.models import UV, UVComment, UVCommentReport
|
||||||
|
|
||||||
|
|
||||||
class UVDetailFormView(CanViewMixin, DetailFormView):
|
class UVDetailFormView(PermissionRequiredMixin, DetailFormView):
|
||||||
"""Display every comment of an UV and detailed infos about it.
|
"""Display every comment of an UV and detailed infos about it.
|
||||||
|
|
||||||
Allow to comment the UV.
|
Allow to comment the UV.
|
||||||
@ -57,11 +56,21 @@ class UVDetailFormView(CanViewMixin, DetailFormView):
|
|||||||
pk_url_kwarg = "uv_id"
|
pk_url_kwarg = "uv_id"
|
||||||
template_name = "pedagogy/uv_detail.jinja"
|
template_name = "pedagogy/uv_detail.jinja"
|
||||||
form_class = UVCommentForm
|
form_class = UVCommentForm
|
||||||
|
permission_required = "pedagogy.view_uv"
|
||||||
|
|
||||||
|
def has_permission(self):
|
||||||
|
if self.request.method == "POST" and not self.request.user.has_perm(
|
||||||
|
"pedagogy.add_uvcomment"
|
||||||
|
):
|
||||||
|
# if it's a POST request, the user is trying to add a new UVComment
|
||||||
|
# thus he also needs the "add_uvcomment" permission
|
||||||
|
return False
|
||||||
|
return super().has_permission()
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
kwargs["author_id"] = self.request.user.id
|
kwargs["author_id"] = self.request.user.id
|
||||||
kwargs["uv_id"] = self.get_object().id
|
kwargs["uv_id"] = self.object.id
|
||||||
kwargs["is_creation"] = True
|
kwargs["is_creation"] = True
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
@ -69,66 +78,61 @@ class UVDetailFormView(CanViewMixin, DetailFormView):
|
|||||||
form.save()
|
form.save()
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse_lazy(
|
|
||||||
"pedagogy:uv_detail", kwargs={"uv_id": self.get_object().id}
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
user = self.request.user
|
|
||||||
return super().get_context_data(**kwargs) | {
|
return super().get_context_data(**kwargs) | {
|
||||||
"can_create_uv": (
|
"comments": list(
|
||||||
user.is_root
|
self.object.comments.viewable_by(self.request.user)
|
||||||
or user.is_in_group(pk=settings.SITH_GROUP_PEDAGOGY_ADMIN_ID)
|
.annotate_is_reported()
|
||||||
|
.select_related("author")
|
||||||
|
.order_by("-publish_date")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
# once the new uv comment has been saved
|
||||||
|
# redirect to the same page we are currently
|
||||||
|
return self.request.path
|
||||||
|
|
||||||
class UVCommentUpdateView(CanEditPropMixin, UpdateView):
|
|
||||||
|
class UVCommentUpdateView(PermissionOrAuthorRequiredMixin, UpdateView):
|
||||||
"""Allow edit of a given comment."""
|
"""Allow edit of a given comment."""
|
||||||
|
|
||||||
model = UVComment
|
model = UVComment
|
||||||
form_class = UVCommentForm
|
form_class = UVCommentForm
|
||||||
pk_url_kwarg = "comment_id"
|
pk_url_kwarg = "comment_id"
|
||||||
template_name = "core/edit.jinja"
|
template_name = "core/edit.jinja"
|
||||||
|
permission_required = "pedagogy.change_uvcomment"
|
||||||
|
author_field = "author"
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
obj = self.get_object()
|
kwargs["author_id"] = self.object.author_id
|
||||||
kwargs["author_id"] = obj.author.id
|
kwargs["uv_id"] = self.object.uv_id
|
||||||
kwargs["uv_id"] = obj.uv.id
|
|
||||||
kwargs["is_creation"] = False
|
kwargs["is_creation"] = False
|
||||||
|
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse_lazy("pedagogy:uv_detail", kwargs={"uv_id": self.object.uv.id})
|
return reverse("pedagogy:uv_detail", kwargs={"uv_id": self.object.uv_id})
|
||||||
|
|
||||||
|
|
||||||
class UVCommentDeleteView(CanEditPropMixin, DeleteView):
|
class UVCommentDeleteView(PermissionOrAuthorRequiredMixin, DeleteView):
|
||||||
"""Allow delete of a given comment."""
|
"""Allow delete of a given comment."""
|
||||||
|
|
||||||
model = UVComment
|
model = UVComment
|
||||||
pk_url_kwarg = "comment_id"
|
pk_url_kwarg = "comment_id"
|
||||||
template_name = "core/delete_confirm.jinja"
|
template_name = "core/delete_confirm.jinja"
|
||||||
|
permission_required = "pedagogy.delete_uvcomment"
|
||||||
|
author_field = "author"
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse_lazy("pedagogy:uv_detail", kwargs={"uv_id": self.object.uv.id})
|
return reverse("pedagogy:uv_detail", kwargs={"uv_id": self.object.uv_id})
|
||||||
|
|
||||||
|
|
||||||
class UVGuideView(LoginRequiredMixin, FormerSubscriberMixin, TemplateView):
|
class UVGuideView(PermissionRequiredMixin, TemplateView):
|
||||||
"""UV guide main page."""
|
"""UV guide main page."""
|
||||||
|
|
||||||
template_name = "pedagogy/guide.jinja"
|
template_name = "pedagogy/guide.jinja"
|
||||||
|
permission_required = "pedagogy.view_uv"
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
user = self.request.user
|
|
||||||
return super().get_context_data(**kwargs) | {
|
|
||||||
"can_create_uv": (
|
|
||||||
user.is_root
|
|
||||||
or user.is_in_group(pk=settings.SITH_GROUP_PEDAGOGY_ADMIN_ID)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class UVCommentReportCreateView(PermissionRequiredMixin, CreateView):
|
class UVCommentReportCreateView(PermissionRequiredMixin, CreateView):
|
||||||
@ -168,21 +172,16 @@ class UVCommentReportCreateView(PermissionRequiredMixin, CreateView):
|
|||||||
return resp
|
return resp
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse_lazy(
|
return reverse("pedagogy:uv_detail", kwargs={"uv_id": self.uv_comment.uv_id})
|
||||||
"pedagogy:uv_detail", kwargs={"uv_id": self.uv_comment.uv.id}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class UVModerationFormView(FormView):
|
class UVModerationFormView(PermissionRequiredMixin, FormView):
|
||||||
"""Moderation interface (Privileged)."""
|
"""Moderation interface (Privileged)."""
|
||||||
|
|
||||||
form_class = UVCommentModerationForm
|
form_class = UVCommentModerationForm
|
||||||
template_name = "pedagogy/moderation.jinja"
|
template_name = "pedagogy/moderation.jinja"
|
||||||
|
permission_required = "pedagogy.delete_uvcomment"
|
||||||
def dispatch(self, request, *args, **kwargs):
|
success_url = reverse_lazy("pedagogy:moderation")
|
||||||
if not request.user.is_owner(UV()):
|
|
||||||
raise PermissionDenied
|
|
||||||
return super().dispatch(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form_clean = form.clean()
|
form_clean = form.clean()
|
||||||
@ -194,9 +193,6 @@ class UVModerationFormView(FormView):
|
|||||||
UVCommentReport.objects.filter(id__in={d.id for d in denied}).delete()
|
UVCommentReport.objects.filter(id__in={d.id for d in denied}).delete()
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse_lazy("pedagogy:moderation")
|
|
||||||
|
|
||||||
|
|
||||||
class UVCreateView(PermissionRequiredMixin, CreateView):
|
class UVCreateView(PermissionRequiredMixin, CreateView):
|
||||||
"""Add a new UV (Privileged)."""
|
"""Add a new UV (Privileged)."""
|
||||||
@ -211,34 +207,28 @@ class UVCreateView(PermissionRequiredMixin, CreateView):
|
|||||||
kwargs["author_id"] = self.request.user.id
|
kwargs["author_id"] = self.request.user.id
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse_lazy("pedagogy:uv_detail", kwargs={"uv_id": self.object.id})
|
|
||||||
|
|
||||||
|
class UVDeleteView(PermissionRequiredMixin, DeleteView):
|
||||||
class UVDeleteView(CanEditPropMixin, DeleteView):
|
|
||||||
"""Allow to delete an UV (Privileged)."""
|
"""Allow to delete an UV (Privileged)."""
|
||||||
|
|
||||||
model = UV
|
model = UV
|
||||||
pk_url_kwarg = "uv_id"
|
pk_url_kwarg = "uv_id"
|
||||||
template_name = "core/delete_confirm.jinja"
|
template_name = "core/delete_confirm.jinja"
|
||||||
|
permission_required = "pedagogy.delete_uv"
|
||||||
def get_success_url(self):
|
success_url = reverse_lazy("pedagogy:guide")
|
||||||
return reverse_lazy("pedagogy:guide")
|
|
||||||
|
|
||||||
|
|
||||||
class UVUpdateView(CanEditPropMixin, UpdateView):
|
class UVUpdateView(PermissionRequiredMixin, UpdateView):
|
||||||
"""Allow to edit an UV (Privilegied)."""
|
"""Allow to edit an UV (Privilegied)."""
|
||||||
|
|
||||||
model = UV
|
model = UV
|
||||||
form_class = UVForm
|
form_class = UVForm
|
||||||
pk_url_kwarg = "uv_id"
|
pk_url_kwarg = "uv_id"
|
||||||
template_name = "pedagogy/uv_edit.jinja"
|
template_name = "pedagogy/uv_edit.jinja"
|
||||||
|
permission_required = "pedagogy.change_uv"
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
kwargs["author_id"] = obj.author.id
|
kwargs["author_id"] = obj.author_id
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse_lazy("pedagogy:uv_detail", kwargs={"uv_id": self.object.id})
|
|
||||||
|
Loading…
Reference in New Issue
Block a user