mirror of
https://github.com/ae-utbm/sith.git
synced 2025-07-10 20:09:25 +00:00
Add UserBan management views
This commit is contained in:
49
rootplace/forms.py
Normal file
49
rootplace/forms.py
Normal file
@ -0,0 +1,49 @@
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from core.models import User, UserBan
|
||||
from core.views.forms import FutureDateTimeField, SelectDateTime
|
||||
from core.views.widgets.select import AutoCompleteSelectUser
|
||||
|
||||
|
||||
class MergeForm(forms.Form):
|
||||
user1 = forms.ModelChoiceField(
|
||||
label=_("User that will be kept"),
|
||||
help_text=None,
|
||||
required=True,
|
||||
widget=AutoCompleteSelectUser,
|
||||
queryset=User.objects.all(),
|
||||
)
|
||||
user2 = forms.ModelChoiceField(
|
||||
label=_("User that will be deleted"),
|
||||
help_text=None,
|
||||
required=True,
|
||||
widget=AutoCompleteSelectUser,
|
||||
queryset=User.objects.all(),
|
||||
)
|
||||
|
||||
|
||||
class SelectUserForm(forms.Form):
|
||||
user = forms.ModelChoiceField(
|
||||
label=_("User to be selected"),
|
||||
help_text=None,
|
||||
required=True,
|
||||
widget=AutoCompleteSelectUser,
|
||||
queryset=User.objects.all(),
|
||||
)
|
||||
|
||||
|
||||
class BanForm(forms.ModelForm):
|
||||
"""Form to ban a user."""
|
||||
|
||||
required_css_class = "required"
|
||||
|
||||
class Meta:
|
||||
model = UserBan
|
||||
fields = ["user", "ban_group", "reason", "expires_at"]
|
||||
field_classes = {"expires_at": FutureDateTimeField}
|
||||
widgets = {
|
||||
"user": AutoCompleteSelectUser,
|
||||
"ban_group": forms.RadioSelect,
|
||||
"expires_at": SelectDateTime,
|
||||
}
|
62
rootplace/templates/rootplace/userban.jinja
Normal file
62
rootplace/templates/rootplace/userban.jinja
Normal file
@ -0,0 +1,62 @@
|
||||
{% extends "core/base.jinja" %}
|
||||
|
||||
|
||||
{% block additional_css %}
|
||||
<link rel="stylesheet" href="{{ static("core/components/card.scss") }}">
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
{% if user.has_perm("core:add_userban") %}
|
||||
<a href="{{ url("rootplace:ban_create") }}" class="btn btn-red margin-bottom">
|
||||
<i class="fa fa-person-circle-xmark"></i>
|
||||
{% trans %}Ban a user{% endtrans %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% for user_ban in user_bans %}
|
||||
<div class="card card-row margin-bottom">
|
||||
<img
|
||||
class="card-image"
|
||||
alt="profil de {{ user_ban.user.get_short_name() }}"
|
||||
{%- if user_ban.user.profile_pict -%}
|
||||
src="{{ user_ban.user.profile_pict.get_download_url() }}"
|
||||
{%- else -%}
|
||||
src="{{ static("core/img/unknown.jpg") }}"
|
||||
{%- endif -%}
|
||||
/>
|
||||
<div class="card-content">
|
||||
<strong>
|
||||
<a href="{{ user_ban.user.get_absolute_url() }}">
|
||||
{{ user_ban.user.get_full_name() }}
|
||||
</a>
|
||||
</strong>
|
||||
<em>{{ user_ban.ban_group.name }}</em>
|
||||
<p>{% trans %}Since{% endtrans %} : {{ user_ban.created_at|date }}</p>
|
||||
<p>
|
||||
{% trans %}Until{% endtrans %} :
|
||||
{% if user_ban.expires_at %}
|
||||
{{ user_ban.expires_at|date }} {{ user_ban.expires_at|time }}
|
||||
{% else %}
|
||||
{% trans %}not specified{% endtrans %}
|
||||
{% endif %}
|
||||
</p>
|
||||
<details>
|
||||
<summary class="clickable">{% trans %}Reason{% endtrans %}</summary>
|
||||
<p>{{ user_ban.reason }}</p>
|
||||
</details>
|
||||
{% if user.has_perm("core:delete_userban") %}
|
||||
<span>
|
||||
<a
|
||||
href="{{ url("rootplace:ban_remove", ban_id=user_ban.id) }}"
|
||||
class="btn btn-blue"
|
||||
>
|
||||
{% trans %}Remove ban{% endtrans %}
|
||||
</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>{% trans %}No active ban.{% endtrans %}</p>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
0
rootplace/tests/__init__.py
Normal file
0
rootplace/tests/__init__.py
Normal file
57
rootplace/tests/test_ban.py
Normal file
57
rootplace/tests/test_ban.py
Normal file
@ -0,0 +1,57 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import pytest
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.test import Client
|
||||
from django.urls import reverse
|
||||
from django.utils.timezone import localtime
|
||||
from model_bakery import baker
|
||||
from pytest_django.asserts import assertRedirects
|
||||
|
||||
from core.models import BanGroup, User, UserBan
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def operator(db) -> User:
|
||||
return baker.make(
|
||||
User,
|
||||
user_permissions=Permission.objects.filter(
|
||||
codename__in=["view_userban", "add_userban", "delete_userban"]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"expires_at",
|
||||
[None, localtime().replace(second=0, microsecond=0) + timedelta(days=7)],
|
||||
)
|
||||
def test_ban_user(client: Client, operator: User, expires_at: datetime):
|
||||
client.force_login(operator)
|
||||
user = baker.make(User)
|
||||
ban_group = BanGroup.objects.first()
|
||||
data = {
|
||||
"user": user.id,
|
||||
"ban_group": ban_group.id,
|
||||
"reason": "Being naughty",
|
||||
}
|
||||
if expires_at is not None:
|
||||
data["expires_at"] = expires_at.strftime("%Y-%m-%d %H:%M")
|
||||
response = client.post(reverse("rootplace:ban_create"), data)
|
||||
assertRedirects(response, expected_url=reverse("rootplace:ban_list"))
|
||||
bans = list(user.bans.all())
|
||||
assert len(bans) == 1
|
||||
assert bans[0].expires_at == expires_at
|
||||
assert bans[0].reason == "Being naughty"
|
||||
assert bans[0].ban_group == ban_group
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_remove_ban(client: Client, operator: User):
|
||||
client.force_login(operator)
|
||||
user = baker.make(User)
|
||||
ban = baker.make(UserBan, user=user)
|
||||
assert user.bans.exists()
|
||||
response = client.post(reverse("rootplace:ban_remove", kwargs={"ban_id": ban.id}))
|
||||
assertRedirects(response, expected_url=reverse("rootplace:ban_list"))
|
||||
assert not user.bans.exists()
|
@ -25,6 +25,9 @@
|
||||
from django.urls import path
|
||||
|
||||
from rootplace.views import (
|
||||
BanCreateView,
|
||||
BanDeleteView,
|
||||
BanView,
|
||||
DeleteAllForumUserMessagesView,
|
||||
MergeUsersView,
|
||||
OperationLogListView,
|
||||
@ -38,4 +41,7 @@ urlpatterns = [
|
||||
name="delete_forum_messages",
|
||||
),
|
||||
path("logs/", OperationLogListView.as_view(), name="operation_logs"),
|
||||
path("ban/", BanView.as_view(), name="ban_list"),
|
||||
path("ban/new", BanCreateView.as_view(), name="ban_create"),
|
||||
path("ban/<int:ban_id>/remove/", BanDeleteView.as_view(), name="ban_remove"),
|
||||
]
|
||||
|
@ -23,20 +23,19 @@
|
||||
#
|
||||
import logging
|
||||
|
||||
from django import forms
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.urls import reverse
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.utils.timezone import localdate
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import ListView
|
||||
from django.views.generic.edit import FormView
|
||||
from django.views.generic import DeleteView, ListView
|
||||
from django.views.generic.edit import CreateView, FormView
|
||||
|
||||
from core.models import OperationLog, SithFile, User
|
||||
from core.models import OperationLog, SithFile, User, UserBan
|
||||
from core.views import CanEditPropMixin
|
||||
from core.views.widgets.select import AutoCompleteSelectUser
|
||||
from counter.models import Customer
|
||||
from forum.models import ForumMessageMeta
|
||||
from rootplace.forms import BanForm, MergeForm, SelectUserForm
|
||||
|
||||
|
||||
def __merge_subscriptions(u1: User, u2: User):
|
||||
@ -155,33 +154,6 @@ def delete_all_forum_user_messages(
|
||||
ForumMessageMeta(message=message, user=moderator, action="DELETE").save()
|
||||
|
||||
|
||||
class MergeForm(forms.Form):
|
||||
user1 = forms.ModelChoiceField(
|
||||
label=_("User that will be kept"),
|
||||
help_text=None,
|
||||
required=True,
|
||||
widget=AutoCompleteSelectUser,
|
||||
queryset=User.objects.all(),
|
||||
)
|
||||
user2 = forms.ModelChoiceField(
|
||||
label=_("User that will be deleted"),
|
||||
help_text=None,
|
||||
required=True,
|
||||
widget=AutoCompleteSelectUser,
|
||||
queryset=User.objects.all(),
|
||||
)
|
||||
|
||||
|
||||
class SelectUserForm(forms.Form):
|
||||
user = forms.ModelChoiceField(
|
||||
label=_("User to be selected"),
|
||||
help_text=None,
|
||||
required=True,
|
||||
widget=AutoCompleteSelectUser,
|
||||
queryset=User.objects.all(),
|
||||
)
|
||||
|
||||
|
||||
class MergeUsersView(FormView):
|
||||
template_name = "rootplace/merge.jinja"
|
||||
form_class = MergeForm
|
||||
@ -233,3 +205,39 @@ class OperationLogListView(ListView, CanEditPropMixin):
|
||||
template_name = "rootplace/logs.jinja"
|
||||
ordering = ["-date"]
|
||||
paginate_by = 100
|
||||
|
||||
|
||||
class BanView(PermissionRequiredMixin, ListView):
|
||||
"""[UserBan][core.models.UserBan] management view.
|
||||
|
||||
Displays :
|
||||
|
||||
- the list of active bans with their main information,
|
||||
with a link to [BanDeleteView][rootplace.views.BanDeleteView] for each one
|
||||
- a link which redirects to [BanCreateView][rootplace.views.BanCreateView]
|
||||
"""
|
||||
|
||||
permission_required = "core.view_userban"
|
||||
template_name = "rootplace/userban.jinja"
|
||||
queryset = UserBan.objects.select_related("user", "user__profile_pict", "ban_group")
|
||||
ordering = "created_at"
|
||||
context_object_name = "user_bans"
|
||||
|
||||
|
||||
class BanCreateView(PermissionRequiredMixin, CreateView):
|
||||
"""[UserBan][core.models.UserBan] creation view."""
|
||||
|
||||
permission_required = "core.add_userban"
|
||||
form_class = BanForm
|
||||
template_name = "core/create.jinja"
|
||||
success_url = reverse_lazy("rootplace:ban_list")
|
||||
|
||||
|
||||
class BanDeleteView(PermissionRequiredMixin, DeleteView):
|
||||
"""[UserBan][core.models.UserBan] deletion view."""
|
||||
|
||||
permission_required = "core.delete_userban"
|
||||
pk_url_kwarg = "ban_id"
|
||||
model = UserBan
|
||||
template_name = "core/delete_confirm.jinja"
|
||||
success_url = reverse_lazy("rootplace:ban_list")
|
||||
|
Reference in New Issue
Block a user