mirror of
https://github.com/ae-utbm/sith.git
synced 2025-10-09 00:04:41 +00:00
Split ClubMemberForm into JoinClubForm and ClubAddMemberForm
This commit is contained in:
@@ -208,15 +208,14 @@ class ClubOldMemberForm(forms.Form):
|
||||
|
||||
|
||||
class ClubMemberForm(forms.ModelForm):
|
||||
"""Form handling the members of a club."""
|
||||
"""Form to add a member to the club, as a board member."""
|
||||
|
||||
error_css_class = "error"
|
||||
required_css_class = "required"
|
||||
|
||||
class Meta:
|
||||
model = Membership
|
||||
fields = ["user", "role", "description"]
|
||||
widgets = {"user": AutoCompleteSelectUser}
|
||||
fields = ["role", "description"]
|
||||
|
||||
def __init__(self, *args, club: Club, request_user: User, **kwargs):
|
||||
self.club = club
|
||||
@@ -231,22 +230,36 @@ class ClubMemberForm(forms.ModelForm):
|
||||
]
|
||||
self.instance.club = club
|
||||
|
||||
@property
|
||||
def max_available_role(self): # pragma: no cover
|
||||
"""The greatest role that will be obtainable with this form."""
|
||||
# this is unreachable, because it will be overridden by subclasses
|
||||
return -1
|
||||
|
||||
|
||||
class ClubAddMemberForm(ClubMemberForm):
|
||||
"""Form to add a member to the club, as a board member."""
|
||||
|
||||
class Meta(ClubMemberForm.Meta):
|
||||
fields = ["user", *ClubMemberForm.Meta.fields]
|
||||
widgets = {"user": AutoCompleteSelectUser}
|
||||
|
||||
@cached_property
|
||||
def max_available_role(self):
|
||||
"""The greatest role that will be obtainable with this form.
|
||||
|
||||
Admins and the club president can attribute any role.
|
||||
Board members can attribute roles lower than their own.
|
||||
Other users can attribute curious and member roles.
|
||||
Other users cannot attribute roles with this form
|
||||
"""
|
||||
if self.request_user.has_perm("club.add_subscription"):
|
||||
return settings.SITH_CLUB_ROLES_ID["President"]
|
||||
membership = self.request_user_membership
|
||||
if membership is not None and membership.role > settings.SITH_MAXIMUM_FREE_ROLE:
|
||||
if membership.role == settings.SITH_CLUB_ROLES_ID["President"]:
|
||||
return membership.role
|
||||
return membership.role - 1
|
||||
return settings.SITH_MAXIMUM_FREE_ROLE
|
||||
if membership is None or membership.role <= settings.SITH_MAXIMUM_FREE_ROLE:
|
||||
return -1
|
||||
if membership.role == settings.SITH_CLUB_ROLES_ID["President"]:
|
||||
return membership.role
|
||||
return membership.role - 1
|
||||
|
||||
def clean_user(self):
|
||||
"""Check that the user is not trying to add a user already in the club.
|
||||
@@ -264,18 +277,26 @@ class ClubMemberForm(forms.ModelForm):
|
||||
)
|
||||
return user
|
||||
|
||||
|
||||
class JoinClubForm(ClubMemberForm):
|
||||
"""Form to join a club."""
|
||||
|
||||
def __init__(self, *args, club: Club, request_user: User, **kwargs):
|
||||
super().__init__(*args, club=club, request_user=request_user, **kwargs)
|
||||
self.instance.user = self.request_user
|
||||
|
||||
@cached_property
|
||||
def max_available_role(self):
|
||||
return settings.SITH_MAXIMUM_FREE_ROLE
|
||||
|
||||
def clean(self):
|
||||
"""Check user rights for adding a user."""
|
||||
cleaned_data = super().clean()
|
||||
if (
|
||||
self.request_user_membership is None
|
||||
or self.request_user_membership.role <= settings.SITH_MAXIMUM_FREE_ROLE
|
||||
) and not self.request_user.has_perm("club.add_membership"):
|
||||
"""Check that the user is subscribed and isn't already in the club."""
|
||||
if not self.request_user.is_subscribed:
|
||||
raise forms.ValidationError(
|
||||
_(
|
||||
"You cannot add other users to a club "
|
||||
"if you are not in the club board."
|
||||
),
|
||||
code="invalid",
|
||||
_("You must be subscribed to join a club"), code="invalid"
|
||||
)
|
||||
return cleaned_data
|
||||
if self.club.get_membership_for(self.request_user):
|
||||
raise forms.ValidationError(
|
||||
_("You are already a member of this club"), code="invalid"
|
||||
)
|
||||
return super().clean()
|
||||
|
@@ -6,23 +6,22 @@
|
||||
{% endblock %}
|
||||
{% block additional_css %}
|
||||
<link rel="stylesheet" href="{{ static("bundled/core/components/ajax-select-index.css") }}">
|
||||
<link rel="stylesheet" href="{{ static("core/components/ajax-select.scss") }}">
|
||||
<link rel="stylesheet" href="{{ static("club/members.scss") }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block notifications %}
|
||||
{# Notifications are moved inside the billing info fragment #}
|
||||
{# Notifications are moved a little bit below #}
|
||||
{% endblock %}
|
||||
|
||||
<h2>{% trans %}Club members{% endtrans %}</h2>
|
||||
|
||||
{% if add_member_fragment %}
|
||||
<br />
|
||||
<h4>{% trans %}Add a new member{% endtrans %}</h4>
|
||||
{{ add_member_fragment }}
|
||||
<br />
|
||||
{% endif %}
|
||||
{% include "core/base/notifications.jinja" %}
|
||||
{% if members %}
|
||||
<form action="{{ url('club:club_members', club_id=club.id) }}" id="members_old" method="post">
|
||||
{% csrf_token %}
|
||||
|
@@ -1,32 +1,46 @@
|
||||
<section id="member-fragment-container">
|
||||
{% if form.user %}
|
||||
<h4>{% trans %}Add a new member{% endtrans %}</h4>
|
||||
{% else %}
|
||||
<h4>{% trans %}Join club{% endtrans %}</h4>
|
||||
{% endif %}
|
||||
|
||||
{% include "core/base/notifications.jinja" %}
|
||||
|
||||
<form
|
||||
hx-post="{{ url('club:club_new_members', club_id=club.id) }}"
|
||||
hx-disabled-elt="find input[type='submit']"
|
||||
hx-swap="outerHTML"
|
||||
id="add_club_members_form"
|
||||
>
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors() }}
|
||||
<fieldset>
|
||||
<div>
|
||||
{{ form.user.label_tag()}}
|
||||
<span class="helptext">{{ form.user.help_text }}</span>
|
||||
{{ form.user }}
|
||||
{{ form.user.errors }}
|
||||
</div>
|
||||
<div>
|
||||
{{ form.role.label_tag()}}
|
||||
{{ form.role }}
|
||||
{{ form.role.errors }}
|
||||
</div>
|
||||
<div>
|
||||
{{ form.description.label_tag()}}
|
||||
{{ form.description }}
|
||||
{{ form.description.errors }}
|
||||
</div>
|
||||
</fieldset>
|
||||
<button type="submit" class="btn btn-blue">
|
||||
<i class="fa fa-user-plus"></i> {% trans %}Add{% endtrans %}</button>
|
||||
</form>
|
||||
<form
|
||||
hx-post="{{ url('club:club_new_members', club_id=club.id) }}"
|
||||
hx-disabled-elt="find input[type='submit']"
|
||||
hx-swap="outerHTML"
|
||||
hx-target="#member-fragment-container"
|
||||
id="add_club_members_form"
|
||||
>
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors() }}
|
||||
<fieldset>
|
||||
{% if form.user %}
|
||||
<div>
|
||||
{{ form.user.label_tag() }}
|
||||
<span class="helptext">{{ form.user.help_text }}</span>
|
||||
{{ form.user }}
|
||||
{{ form.user.errors }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div>
|
||||
{{ form.role.label_tag() }}
|
||||
{{ form.role }}
|
||||
{{ form.role.errors }}
|
||||
</div>
|
||||
<div>
|
||||
{{ form.description.label_tag() }}
|
||||
{{ form.description }}
|
||||
{{ form.description.errors }}
|
||||
</div>
|
||||
</fieldset>
|
||||
<button type="submit" class="btn btn-blue">
|
||||
<i class="fa fa-user-plus"></i>
|
||||
{%- if club.user -%}
|
||||
{% trans %}Add{% endtrans %}
|
||||
{%- else -%}
|
||||
{% trans %}Join{% endtrans %}
|
||||
{%- endif -%}
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
|
@@ -1,5 +1,7 @@
|
||||
from collections.abc import Callable
|
||||
from datetime import timedelta
|
||||
|
||||
import pytest
|
||||
from bs4 import BeautifulSoup
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import Permission
|
||||
@@ -11,7 +13,7 @@ from django.utils.timezone import localdate, localtime, now
|
||||
from model_bakery import baker
|
||||
from pytest_django.asserts import assertRedirects
|
||||
|
||||
from club.forms import ClubMemberForm
|
||||
from club.forms import ClubAddMemberForm, JoinClubForm
|
||||
from club.models import Club, Membership
|
||||
from club.tests.base import TestClub
|
||||
from core.baker_recipes import subscriber_user
|
||||
@@ -268,7 +270,7 @@ class TestMembership(TestClub):
|
||||
cannot be members of clubs.
|
||||
"""
|
||||
for user in self.public, self.old_subscriber:
|
||||
form = ClubMemberForm(
|
||||
form = ClubAddMemberForm(
|
||||
data={"user": user.id, "role": 1},
|
||||
request_user=self.root,
|
||||
club=self.club,
|
||||
@@ -308,7 +310,7 @@ class TestMembership(TestClub):
|
||||
nb_memberships = self.club.members.count()
|
||||
max_id = User.objects.aggregate(id=Max("id"))["id"]
|
||||
for members in [max_id + 1], [max_id + 1, self.subscriber.id]:
|
||||
form = ClubMemberForm(
|
||||
form = ClubAddMemberForm(
|
||||
data={"user": members, "role": 1},
|
||||
request_user=self.root,
|
||||
club=self.club,
|
||||
@@ -346,7 +348,7 @@ class TestMembership(TestClub):
|
||||
"""Test that a member of the club member cannot create
|
||||
a membership with a greater role than its own.
|
||||
"""
|
||||
form = ClubMemberForm(
|
||||
form = ClubAddMemberForm(
|
||||
data={"user": self.subscriber.id, "role": 10},
|
||||
request_user=self.simple_board_member,
|
||||
club=self.club,
|
||||
@@ -363,7 +365,7 @@ class TestMembership(TestClub):
|
||||
|
||||
def test_add_member_without_role(self):
|
||||
"""Test that trying to add members without specifying their role fails."""
|
||||
form = ClubMemberForm(
|
||||
form = ClubAddMemberForm(
|
||||
data={"user": self.subscriber.id}, request_user=self.root, club=self.club
|
||||
)
|
||||
|
||||
@@ -371,7 +373,7 @@ class TestMembership(TestClub):
|
||||
assert form.errors == {"role": ["Ce champ est obligatoire."]}
|
||||
|
||||
def test_add_member_already_there(self):
|
||||
form = ClubMemberForm(
|
||||
form = ClubAddMemberForm(
|
||||
data={"user": self.simple_board_member, "role": 3},
|
||||
request_user=self.root,
|
||||
club=self.club,
|
||||
@@ -385,17 +387,14 @@ class TestMembership(TestClub):
|
||||
non_member = subscriber_user.make()
|
||||
simple_member = baker.make(Membership, club=self.club, role=1).user
|
||||
for user in non_member, simple_member:
|
||||
form = ClubMemberForm(
|
||||
form = ClubAddMemberForm(
|
||||
data={"user": subscriber_user.make(), "role": 1},
|
||||
request_user=user,
|
||||
club=self.club,
|
||||
)
|
||||
assert not form.is_valid()
|
||||
assert form.errors == {
|
||||
"__all__": [
|
||||
"Vous ne pouvez pas ajouter d'autres utilisateurs "
|
||||
"dans un club si vous ne faites pas partie de son bureau."
|
||||
]
|
||||
"role": ["Sélectionnez un choix valide. 1 n\u2019en fait pas partie."]
|
||||
}
|
||||
|
||||
def test_simple_members_dont_see_form_anymore(self):
|
||||
@@ -533,6 +532,57 @@ class TestMembership(TestClub):
|
||||
assert new_board == initial_board
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestJoinClub:
|
||||
@pytest.fixture(autouse=True)
|
||||
def clear_cache(self):
|
||||
cache.clear()
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("user_factory", "role", "errors"),
|
||||
[
|
||||
(
|
||||
subscriber_user.make,
|
||||
2,
|
||||
{
|
||||
"role": [
|
||||
"Sélectionnez un choix valide. 2 n\u2019en fait pas partie."
|
||||
]
|
||||
},
|
||||
),
|
||||
(
|
||||
lambda: baker.make(User),
|
||||
1,
|
||||
{"__all__": ["Vous devez être cotisant pour faire partie d'un club"]},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_join_club_errors(
|
||||
self, user_factory: Callable[[], User], role: int, errors: dict
|
||||
):
|
||||
club = baker.make(Club)
|
||||
user = user_factory()
|
||||
form = JoinClubForm(club=club, request_user=user, data={"role": role})
|
||||
assert not form.is_valid()
|
||||
assert form.errors == errors
|
||||
|
||||
def test_user_already_in_club(self):
|
||||
club = baker.make(Club)
|
||||
user = subscriber_user.make()
|
||||
baker.make(Membership, user=user, club=club)
|
||||
form = JoinClubForm(club=club, request_user=user, data={"role": 1})
|
||||
assert not form.is_valid()
|
||||
assert form.errors == {"__all__": ["Vous êtes déjà membre de ce club."]}
|
||||
|
||||
def test_ok(self):
|
||||
club = baker.make(Club)
|
||||
user = subscriber_user.make()
|
||||
form = JoinClubForm(club=club, request_user=user, data={"role": 1})
|
||||
assert form.is_valid()
|
||||
form.save()
|
||||
assert Membership.objects.ongoing().filter(user=user, club=club).exists()
|
||||
|
||||
|
||||
class TestOldMembersView(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
@@ -47,10 +47,11 @@ from django.views.generic import DetailView, ListView, View
|
||||
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
||||
|
||||
from club.forms import (
|
||||
ClubAddMemberForm,
|
||||
ClubAdminEditForm,
|
||||
ClubEditForm,
|
||||
ClubMemberForm,
|
||||
ClubOldMemberForm,
|
||||
JoinClubForm,
|
||||
MailingForm,
|
||||
SellingsForm,
|
||||
)
|
||||
@@ -266,17 +267,21 @@ class ClubAddMembersFragment(
|
||||
FragmentMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView
|
||||
):
|
||||
template_name = "club/fragments/add_member.jinja"
|
||||
form_class = ClubMemberForm
|
||||
model = Membership
|
||||
object = None
|
||||
reload_on_redirect = True
|
||||
permission_required = "club.view_club"
|
||||
success_message = _("%(user)s has been added to club.")
|
||||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
self.club = get_object_or_404(Club, pk=kwargs.get("club_id"))
|
||||
return super().dispatch(*args, **kwargs)
|
||||
|
||||
def get_form_class(self):
|
||||
user = self.request.user
|
||||
if user.has_perm("club.add_membership") or self.club.get_membership_for(user):
|
||||
return ClubAddMemberForm
|
||||
return JoinClubForm
|
||||
|
||||
def get_form_kwargs(self):
|
||||
return super().get_form_kwargs() | {
|
||||
"request_user": self.request.user,
|
||||
@@ -293,6 +298,11 @@ class ClubAddMembersFragment(
|
||||
def get_context_data(self, **kwargs):
|
||||
return super().get_context_data(**kwargs) | {"club": self.club}
|
||||
|
||||
def get_success_message(self, cleaned_data):
|
||||
if "user" not in cleaned_data or cleaned_data["user"] == self.request.user:
|
||||
return _("You are now a member of this club.")
|
||||
return _("%(user)s has been added to club.") % cleaned_data
|
||||
|
||||
|
||||
class ClubMembersView(
|
||||
ClubTabsMixin, UseFragmentsMixin, PermissionRequiredMixin, DetailFormView
|
||||
|
@@ -6,7 +6,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-09-25 15:33+0200\n"
|
||||
"POT-Creation-Date: 2025-09-26 17:36+0200\n"
|
||||
"PO-Revision-Date: 2016-07-18\n"
|
||||
"Last-Translator: Maréchal <thomas.girod@utbm.fr\n"
|
||||
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
||||
@@ -174,14 +174,12 @@ msgid "You can not add the same user twice"
|
||||
msgstr "Vous ne pouvez pas ajouter deux fois le même utilisateur"
|
||||
|
||||
#: club/forms.py
|
||||
msgid "You cannot add other users to a club if you are not in the club board."
|
||||
msgstr ""
|
||||
"Vous ne pouvez pas ajouter d'autres utilisateurs dans un club si vous ne "
|
||||
"faites pas partie de son bureau."
|
||||
msgid "You must be subscribed to join a club"
|
||||
msgstr "Vous devez être cotisant pour faire partie d'un club"
|
||||
|
||||
#: club/forms.py sas/forms.py
|
||||
msgid "You do not have the permission to do that"
|
||||
msgstr "Vous n'avez pas la permission de faire cela"
|
||||
#: club/forms.py
|
||||
msgid "You are already a member of this club"
|
||||
msgstr "Vous êtes déjà membre de ce club."
|
||||
|
||||
#: club/models.py
|
||||
msgid "slug name"
|
||||
@@ -328,10 +326,6 @@ msgstr "Il n'y a pas de club dans ce site web."
|
||||
msgid "Club members"
|
||||
msgstr "Membres du club"
|
||||
|
||||
#: club/templates/club/club_members.jinja
|
||||
msgid "Add a new member"
|
||||
msgstr "Ajouter un nouveau membre"
|
||||
|
||||
#: club/templates/club/club_members.jinja
|
||||
#: club/templates/club/club_old_members.jinja
|
||||
#: core/templates/core/user_clubs.jinja
|
||||
@@ -570,12 +564,24 @@ msgstr ""
|
||||
msgid "Save"
|
||||
msgstr "Sauver"
|
||||
|
||||
#: club/templates/club/fragments/add_member.jinja
|
||||
msgid "Add a new member"
|
||||
msgstr "Ajouter un nouveau membre"
|
||||
|
||||
#: club/templates/club/fragments/add_member.jinja
|
||||
msgid "Join club"
|
||||
msgstr "Rejoindre le club"
|
||||
|
||||
#: club/templates/club/fragments/add_member.jinja
|
||||
#: core/templates/core/file_detail.jinja core/views/forms.py
|
||||
#: trombi/templates/trombi/detail.jinja
|
||||
msgid "Add"
|
||||
msgstr "Ajouter"
|
||||
|
||||
#: club/templates/club/fragments/add_member.jinja
|
||||
msgid "Join"
|
||||
msgstr "Rejoindre"
|
||||
|
||||
#: club/templates/club/mailing.jinja
|
||||
msgid "Mailing lists"
|
||||
msgstr "Mailing listes"
|
||||
@@ -687,6 +693,10 @@ msgstr "Listes de diffusion"
|
||||
msgid "%(user)s has been added to club."
|
||||
msgstr "%(user)s a été ajouté au club."
|
||||
|
||||
#: club/views.py
|
||||
msgid "You are now a member of this club."
|
||||
msgstr "Vous êtes maintenant membre de ce club."
|
||||
|
||||
#: com/forms.py
|
||||
msgid "Format: 16:9 | Resolution: 1920x1080"
|
||||
msgstr "Format : 16:9 | Résolution : 1920x1080"
|
||||
@@ -4657,6 +4667,10 @@ msgstr "Pas de ban actif"
|
||||
msgid "Add a new album"
|
||||
msgstr "Ajouter un nouvel album"
|
||||
|
||||
#: sas/forms.py
|
||||
msgid "You do not have the permission to do that"
|
||||
msgstr "Vous n'avez pas la permission de faire cela"
|
||||
|
||||
#: sas/forms.py
|
||||
msgid "Upload images"
|
||||
msgstr "Envoyer les images"
|
||||
@@ -5558,4 +5572,4 @@ msgstr "Vous ne pouvez plus écrire de commentaires, la date est passée."
|
||||
#: trombi/views.py
|
||||
#, python-format
|
||||
msgid "Maximum characters: %(max_length)s"
|
||||
msgstr "Nombre de caractères max: %(max_length)s"
|
||||
msgstr "Nombre de caractères max: %(max_length)s"
|
||||
|
Reference in New Issue
Block a user