Merge branch 'clubs' into 'master'

Améliore la gestion de membres de clubs

See merge request ae/Sith!196
This commit is contained in:
Antoine Bartuccio 2019-04-26 18:19:05 +02:00
commit 142299c0cd
5 changed files with 535 additions and 159 deletions

View File

@ -276,18 +276,6 @@ class Membership(models.Model):
_("description"), max_length=128, null=False, blank=True _("description"), max_length=128, null=False, blank=True
) )
def clean(self):
sub = User.objects.filter(pk=self.user.pk).first()
if sub is None or not sub.is_subscribed:
raise ValidationError(_("User must be subscriber to take part to a club"))
if (
Membership.objects.filter(user=self.user)
.filter(club=self.club)
.filter(end_date=None)
.exists()
):
raise ValidationError(_("User is already member of that club"))
def __str__(self): def __str__(self):
return ( return (
self.club.name self.club.name
@ -304,11 +292,14 @@ class Membership(models.Model):
""" """
return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP)
def can_be_edited_by(self, user): def can_be_edited_by(self, user, membership=None):
""" """
Method to see if that object can be edited by the given user Method to see if that object can be edited by the given user
""" """
if user.memberships: if user.memberships:
if membership: # This is for optimisation purpose
ms = membership
else:
ms = user.memberships.filter(club=self.club, end_date=None).first() ms = user.memberships.filter(club=self.club, end_date=None).first()
return (ms and ms.role >= self.role) or user.is_in_group( return (ms and ms.role >= self.role) or user.is_in_group(
settings.SITH_MAIN_BOARD_GROUP settings.SITH_MAIN_BOARD_GROUP

View File

@ -1,35 +1,80 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{% from 'core/macros.jinja' import user_profile_link %} {% from 'core/macros.jinja' import user_profile_link, select_all_checkbox %}
{% block content %} {% block content %}
<h2>{% trans %}Club members{% endtrans %}</h2> <h2>{% trans %}Club members{% endtrans %}</h2>
{% if members %}
<form action="{{ url('club:club_members', club_id=club.id) }}" id="users_old" method="post">
{% csrf_token %}
{% set users_old = dict(form.users_old | groupby("choice_label")) %}
{% if users_old %}
{{ select_all_checkbox("users_old") }}
<p></p>
{% endif %}
<table> <table>
<thead> <thead>
<td>{% trans %}User{% endtrans %}</td> <td>{% trans %}User{% endtrans %}</td>
<td>{% trans %}Role{% endtrans %}</td> <td>{% trans %}Role{% endtrans %}</td>
<td>{% trans %}Description{% endtrans %}</td> <td>{% trans %}Description{% endtrans %}</td>
<td>{% trans %}Since{% endtrans %}</td> <td>{% trans %}Since{% endtrans %}</td>
{% if users_old %}
<td>{% trans %}Mark as old{% endtrans %}</td>
{% endif %}
</thead> </thead>
<tbody> <tbody>
{% for m in club.members.filter(end_date=None).order_by('-role').all() %} {% for m in members %}
<tr> <tr>
<td>{{ user_profile_link(m.user) }}</td> <td>{{ user_profile_link(m.user) }}</td>
<td>{{ settings.SITH_CLUB_ROLES[m.role] }}</td> <td>{{ settings.SITH_CLUB_ROLES[m.role] }}</td>
<td>{{ m.description }}</td> <td>{{ m.description }}</td>
<td>{{ m.start_date }}</td> <td>{{ m.start_date }}</td>
{% if m.can_be_edited_by(user) %} {% if users_old %}
<td><a href="{{ url('club:membership_set_old', membership_id=m.id) }}">{% trans %}Mark as old{% endtrans %}</a></td> <td>
{% set user_old = users_old[m.user.get_display_name()] %}
{% if user_old %}
{{ user_old[0].tag() }}
{% endif %}
</td>
{% endif %} {% endif %}
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
<form action="{{ url('club:club_members', club_id=club.id) }}" method="post"> {{ form.users_old.errors }}
{% if users_old %}
<p></p>
<input type="submit" name="submit" value="{% trans %}Mark as old{% endtrans %}">
{% endif %}
</form>
{% else %}
<p>{% trans %}There are no members in this club.{% endtrans %}</p>
{% endif %}
<form action="{{ url('club:club_members', club_id=club.id) }}" id="add_users" method="post">
{% csrf_token %} {% csrf_token %}
{{ form.as_p() }} {{ form.non_field_errors() }}
<p>
{{ form.users.errors }}
<label for="{{ form.users.id_for_label }}">{{ form.users.label }} :</label>
{{ form.users }}
<span class="helptext">{{ form.users.help_text }}</span>
</p>
<p>
{{ form.role.errors }}
<label for="{{ form.role.id_for_label }}">{{ form.role.label }} :</label>
{{ form.role }}
</p>
{% if form.start_date %}
<p>
{{ form.start_date.errors }}
<label for="{{ form.start_date.id_for_label }}">{{ form.start_date.label }} :</label>
{{ form.start_date }}
</p>
{% endif %}
<p>
{{ form.description.errors }}
<label for="{{ form.description.id_for_label }}">{{ form.description.label }} :</label>
{{ form.description }}
</p>
<p><input type="submit" value="{% trans %}Add{% endtrans %}" /></p> <p><input type="submit" value="{% trans %}Add{% endtrans %}" /></p>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -44,7 +44,7 @@ class ClubTest(TestCase):
self.client.login(username="root", password="plop") self.client.login(username="root", password="plop")
self.client.post( self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}), reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"user": self.skia.id, "start_date": "12/06/2016", "role": 3}, {"users": self.skia.id, "start_date": "12/06/2016", "role": 3},
) )
response = self.client.get( response = self.client.get(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}) reverse("club:club_members", kwargs={"club_id": self.bdf.id})
@ -55,14 +55,38 @@ class ClubTest(TestCase):
in str(response.content) in str(response.content)
) )
def test_create_add_multiple_user_to_club_from_root_ok(self):
self.client.login(username="root", password="plop")
self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{
"users": "|%d|%d|" % (self.skia.id, self.rbatsbak.id),
"start_date": "12/06/2016",
"role": 3,
},
)
response = self.client.get(
reverse("club:club_members", kwargs={"club_id": self.bdf.id})
)
self.assertTrue(response.status_code == 200)
content = str(response.content)
self.assertTrue(
"S&#39; Kia</a></td>\\n <td>Responsable info</td>"
in content
)
self.assertTrue(
"Richard Batsbak</a></td>\\n <td>Responsable info</td>"
in content
)
def test_create_add_user_to_club_from_root_fail_not_subscriber(self): def test_create_add_user_to_club_from_root_fail_not_subscriber(self):
self.client.login(username="root", password="plop") self.client.login(username="root", password="plop")
response = self.client.post( response = self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}), reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"user": self.guy.id, "start_date": "12/06/2016", "role": 3}, {"users": self.guy.id, "start_date": "12/06/2016", "role": 3},
) )
self.assertTrue(response.status_code == 200) self.assertTrue(response.status_code == 200)
self.assertTrue('<ul class="errorlist nonfield"><li>' in str(response.content)) self.assertTrue('<ul class="errorlist"><li>' in str(response.content))
response = self.client.get( response = self.client.get(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}) reverse("club:club_members", kwargs={"club_id": self.bdf.id})
) )
@ -75,7 +99,7 @@ class ClubTest(TestCase):
self.client.login(username="root", password="plop") self.client.login(username="root", password="plop")
self.client.post( self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}), reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"user": self.skia.id, "start_date": "12/06/2016", "role": 3}, {"users": self.skia.id, "start_date": "12/06/2016", "role": 3},
) )
response = self.client.get( response = self.client.get(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}) reverse("club:club_members", kwargs={"club_id": self.bdf.id})
@ -86,7 +110,7 @@ class ClubTest(TestCase):
) )
response = self.client.post( response = self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}), reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"user": self.skia.id, "start_date": "12/06/2016", "role": 4}, {"users": self.skia.id, "start_date": "12/06/2016", "role": 4},
) )
self.assertTrue(response.status_code == 200) self.assertTrue(response.status_code == 200)
self.assertFalse( self.assertFalse(
@ -94,16 +118,40 @@ class ClubTest(TestCase):
in str(response.content) in str(response.content)
) )
def test_create_add_user_non_existent_to_club_from_root_fail(self):
self.client.login(username="root", password="plop")
response = self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"users": [9999], "start_date": "12/06/2016", "role": 3},
)
self.assertTrue(response.status_code == 200)
content = str(response.content)
self.assertTrue('<ul class="errorlist"><li>' in content)
self.assertFalse("<td>Responsable info</td>" in content)
self.client.login(username="root", password="plop")
response = self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{
"users": "|%d|%d|" % (self.skia.id, 9999),
"start_date": "12/06/2016",
"role": 3,
},
)
self.assertTrue(response.status_code == 200)
content = str(response.content)
self.assertTrue('<ul class="errorlist"><li>' in content)
self.assertFalse("<td>Responsable info</td>" in content)
def test_create_add_user_to_club_from_skia_ok(self): def test_create_add_user_to_club_from_skia_ok(self):
self.client.login(username="root", password="plop") self.client.login(username="root", password="plop")
self.client.post( self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}), reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"user": self.skia.id, "start_date": "12/06/2016", "role": 10}, {"users": self.skia.id, "start_date": "12/06/2016", "role": 10},
) )
self.client.login(username="skia", password="plop") self.client.login(username="skia", password="plop")
self.client.post( self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}), reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"user": self.rbatsbak.id, "start_date": "12/06/2016", "role": 9}, {"users": self.rbatsbak.id, "start_date": "12/06/2016", "role": 9},
) )
response = self.client.get( response = self.client.get(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}) reverse("club:club_members", kwargs={"club_id": self.bdf.id})
@ -118,15 +166,210 @@ class ClubTest(TestCase):
self.client.login(username="root", password="plop") self.client.login(username="root", password="plop")
self.client.post( self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}), reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"user": self.rbatsbak.id, "start_date": "12/06/2016", "role": 3}, {"users": self.rbatsbak.id, "start_date": "12/06/2016", "role": 3},
) )
self.client.login(username="rbatsbak", password="plop") self.client.login(username="rbatsbak", password="plop")
response = self.client.post( response = self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}), reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"user": self.skia.id, "start_date": "12/06/2016", "role": 10}, {"users": self.skia.id, "start_date": "12/06/2016", "role": 10},
) )
self.assertTrue(response.status_code == 200) self.assertTrue(response.status_code == 200)
self.assertTrue( self.assertTrue(
"<li>Vous n&#39;avez pas la permission de faire cela</li>" "<li>Vous n&#39;avez pas la permission de faire cela</li>"
in str(response.content) in str(response.content)
) )
def test_role_required_if_users_specified(self):
self.client.login(username="root", password="plop")
response = self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"users": self.rbatsbak.id, "start_date": "12/06/2016"},
)
self.assertTrue(
'<ul class="errorlist"><li>Vous devez choisir un r' in str(response.content)
)
def test_mark_old_user_to_club_from_skia_ok(self):
self.client.login(username="root", password="plop")
self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{
"users": "|%d|%d|" % (self.skia.id, self.rbatsbak.id),
"start_date": "12/06/2016",
"role": 3,
},
)
self.client.login(username="skia", password="plop")
response = self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"users_old": self.rbatsbak.id},
)
self.assertTrue(response.status_code == 302)
response = self.client.get(
reverse("club:club_members", kwargs={"club_id": self.bdf.id})
)
self.assertTrue(response.status_code == 200)
content = str(response.content)
self.assertFalse(
"Richard Batsbak</a></td>\\n <td>Responsable info</td>"
in content
)
self.assertTrue(
"S&#39; Kia</a></td>\\n <td>Responsable info</td>"
in content
)
# Skia is board member so he should be able to mark as old even without being in the club
response = self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"users_old": self.skia.id},
)
self.client.login(username="root", password="plop")
self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"users": self.rbatsbak.id, "start_date": "12/06/2016", "role": 3},
)
self.client.login(username="skia", password="plop")
response = self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"users_old": self.rbatsbak.id},
)
self.assertFalse(
"Richard Batsbak</a></td>\\n <td>Responsable info</td>"
in str(response.content)
)
def test_mark_old_multiple_users_from_skia_ok(self):
self.client.login(username="root", password="plop")
self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{
"users": "|%d|%d|" % (self.skia.id, self.rbatsbak.id),
"start_date": "12/06/2016",
"role": 3,
},
)
self.client.login(username="skia", password="plop")
response = self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"users_old": [self.rbatsbak.id, self.skia.id]},
)
self.assertTrue(response.status_code == 302)
response = self.client.get(
reverse("club:club_members", kwargs={"club_id": self.bdf.id})
)
self.assertTrue(response.status_code == 200)
content = str(response.content)
self.assertFalse(
"Richard Batsbak</a></td>\\n <td>Responsable info</td>"
in content
)
self.assertFalse(
"S&#39; Kia</a></td>\\n <td>Responsable info</td>"
in content
)
def test_mark_old_user_to_club_from_richard_ok(self):
self.client.login(username="root", password="plop")
self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{
"users": "|%d|%d|" % (self.skia.id, self.rbatsbak.id),
"start_date": "12/06/2016",
"role": 3,
},
)
# Test with equal rights
self.client.login(username="rbatsbak", password="plop")
response = self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"users_old": self.skia.id},
)
self.assertTrue(response.status_code == 302)
response = self.client.get(
reverse("club:club_members", kwargs={"club_id": self.bdf.id})
)
self.assertTrue(response.status_code == 200)
content = str(response.content)
self.assertTrue(
"Richard Batsbak</a></td>\\n <td>Responsable info</td>"
in content
)
self.assertFalse(
"S&#39; Kia</a></td>\\n <td>Responsable info</td>"
in content
)
# Test with lower rights
self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"users": self.skia.id, "start_date": "12/06/2016", "role": 0},
)
self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"users_old": self.skia.id},
)
response = self.client.get(
reverse("club:club_members", kwargs={"club_id": self.bdf.id})
)
self.assertTrue(response.status_code == 200)
content = str(response.content)
self.assertTrue(
"Richard Batsbak</a></td>\\n <td>Responsable info</td>"
in content
)
self.assertFalse(
"S&#39; Kia</a></td>\\n <td>Curieux</td>" in content
)
def test_mark_old_user_to_club_from_richard_fail(self):
self.client.login(username="root", password="plop")
self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"users": self.skia.id, "start_date": "12/06/2016", "role": 3},
)
# Test with richard outside of the club
self.client.login(username="rbatsbak", password="plop")
response = self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"users_old": self.skia.id},
)
self.assertTrue(response.status_code == 200)
response = self.client.get(
reverse("club:club_members", kwargs={"club_id": self.bdf.id})
)
self.assertTrue(response.status_code == 200)
self.assertTrue(
"S&#39; Kia</a></td>\\n <td>Responsable info</td>"
in str(response.content)
)
# Test with lower rights
self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"users": self.rbatsbak.id, "start_date": "12/06/2016", "role": 0},
)
self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"users_old": self.skia.id},
)
response = self.client.get(
reverse("club:club_members", kwargs={"club_id": self.bdf.id})
)
self.assertTrue(response.status_code == 200)
content = str(response.content)
self.assertTrue(
"Richard Batsbak</a></td>\\n <td>Curieux</td>" in content
)
self.assertTrue(
"S&#39; Kia</a></td>\\n <td>Responsable info</td>"
in content
)

View File

@ -33,7 +33,7 @@ from django.core.urlresolvers import reverse, reverse_lazy
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext as _t from django.utils.translation import ugettext as _t
from ajax_select.fields import AutoCompleteSelectField from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
@ -44,6 +44,7 @@ from core.views import (
CanEditPropMixin, CanEditPropMixin,
TabedViewMixin, TabedViewMixin,
PageEditViewBase, PageEditViewBase,
DetailFormView,
) )
from core.views.forms import SelectDate, SelectDateTime from core.views.forms import SelectDate, SelectDateTime
from club.models import Club, Membership, Mailing, MailingSubscription from club.models import Club, Membership, Mailing, MailingSubscription
@ -305,7 +306,7 @@ class ClubToolsView(ClubTabsMixin, CanEditMixin, DetailView):
current_tab = "tools" current_tab = "tools"
class ClubMemberForm(forms.ModelForm): class ClubMemberForm(forms.Form):
""" """
Form handling the members of a club Form handling the members of a club
""" """
@ -313,24 +314,115 @@ class ClubMemberForm(forms.ModelForm):
error_css_class = "error" error_css_class = "error"
required_css_class = "required" required_css_class = "required"
class Meta: users = AutoCompleteSelectMultipleField(
model = Membership "users",
fields = ["user", "role", "start_date", "description"] label=_("Users to add"),
widgets = {"start_date": SelectDate} help_text=_("Search users to add (one or more)."),
required=False,
user = AutoCompleteSelectField(
"users", required=True, label=_("Select user"), help_text=None
) )
def save(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.club = kwargs.pop("club")
self.request_user = kwargs.pop("request_user")
self.club_members = kwargs.pop("club_members", None)
if not self.club_members:
self.club_members = (
self.club.members.filter(end_date=None).order_by("-role").all()
)
self.request_user_membership = self.club.get_membership_for(self.request_user)
super(ClubMemberForm, self).__init__(*args, **kwargs)
# Using a ModelForm binds too much the form with the model and we don't want that
# We want the view to process the model creation since they are multiple users
# We also want the form to handle bulk deletion
self.fields.update(
forms.fields_for_model(
Membership,
fields=("role", "start_date", "description"),
widgets={"start_date": SelectDate},
)
)
# Role is required only if users is specified
self.fields["role"].required = False
# Start date and description are never really required
self.fields["start_date"].required = False
self.fields["description"].required = False
self.fields["users_old"] = forms.ModelMultipleChoiceField(
User.objects.filter(
id__in=[
ms.user.id
for ms in self.club_members
if ms.can_be_edited_by(
self.request_user, self.request_user_membership
)
]
).all(),
label=_("Mark as old"),
required=False,
widget=forms.CheckboxSelectMultiple,
)
if not self.request_user.is_root:
self.fields.pop("start_date")
def clean_users(self):
""" """
Overloaded to return the club, and not to a Membership object that has no view Check that the user is not trying to add an user already in the club
Also check that the user is valid and has a valid subscription
""" """
super(ClubMemberForm, self).save(*args, **kwargs) cleaned_data = super(ClubMemberForm, self).clean()
return self.instance.club users = []
for user_id in cleaned_data["users"]:
user = User.objects.filter(id=user_id).first()
if not user:
raise forms.ValidationError(
_("One of the selected users doesn't exist"), code="invalid"
)
if not user.is_subscribed:
raise forms.ValidationError(
_("User must be subscriber to take part to a club"), code="invalid"
)
if self.club.get_membership_for(user):
raise forms.ValidationError(
_("You can not add the same user twice"), code="invalid"
)
users.append(user)
return users
def clean(self):
"""
Check user rights for adding an user
"""
cleaned_data = super(ClubMemberForm, self).clean()
if "start_date" in cleaned_data and not cleaned_data["start_date"]:
# Drop start_date if allowed to edition but not specified
cleaned_data.pop("start_date")
if not cleaned_data.get("users"):
# No user to add equals no check needed
return cleaned_data
if cleaned_data.get("role", "") == "":
# Role is required if users exists
self.add_error("role", _("You should specify a role"))
return cleaned_data
request_user = self.request_user
membership = self.request_user_membership
if not (
cleaned_data["role"] <= SITH_MAXIMUM_FREE_ROLE
or (membership is not None and membership.role >= cleaned_data["role"])
or request_user.is_board_member
or request_user.is_root
):
raise forms.ValidationError(_("You do not have the permission to do that"))
return cleaned_data
class ClubMembersView(ClubTabsMixin, CanViewMixin, UpdateView): class ClubMembersView(ClubTabsMixin, CanViewMixin, DetailFormView):
""" """
View of a club's members View of a club's members
""" """
@ -341,52 +433,45 @@ class ClubMembersView(ClubTabsMixin, CanViewMixin, UpdateView):
template_name = "club/club_members.jinja" template_name = "club/club_members.jinja"
current_tab = "members" current_tab = "members"
def get_form(self): def get_form_kwargs(self):
""" kwargs = super(ClubMembersView, self).get_form_kwargs()
Here we get a Membership object, but the view handles Club object. kwargs["request_user"] = self.request.user
That's why the save method of ClubMemberForm is overridden. kwargs["club"] = self.get_object()
""" kwargs["club_members"] = self.members
form = super(ClubMembersView, self).get_form() return kwargs
if (
"user" in form.data and form.data.get("user") != "" def get_context_data(self, *args, **kwargs):
): # Load an existing membership if possible kwargs = super(ClubMembersView, self).get_context_data(*args, **kwargs)
form.instance = ( kwargs["members"] = self.members
Membership.objects.filter(club=self.object) return kwargs
.filter(user=form.data.get("user"))
.filter(end_date=None)
.first()
)
if form.instance is None: # Instanciate a new membership
form.instance = Membership(club=self.object, user=self.request.user)
if not self.request.user.is_root:
form.fields.pop("start_date", None)
return form
def form_valid(self, form): def form_valid(self, form):
""" """
Check user rights Check user rights
""" """
user = self.request.user resp = super(ClubMembersView, self).form_valid(form)
ms = self.object.get_membership_for(user)
if ( data = form.clean()
form.cleaned_data["role"] <= SITH_MAXIMUM_FREE_ROLE users = data.pop("users", [])
or (ms is not None and ms.role >= form.cleaned_data["role"]) users_old = data.pop("users_old", [])
or user.is_board_member for user in users:
or user.is_root Membership(club=self.get_object(), user=user, **data).save()
): for user in users_old:
form.save() membership = self.get_object().get_membership_for(user)
form = self.form_class() membership.end_date = timezone.now()
return super(ModelFormMixin, self).form_valid(form) membership.save()
else: return resp
form.add_error(None, _("You do not have the permission to do that"))
return self.form_invalid(form)
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
self.request = request self.members = (
self.get_object().members.filter(end_date=None).order_by("-role").all()
)
return super(ClubMembersView, self).dispatch(request, *args, **kwargs) return super(ClubMembersView, self).dispatch(request, *args, **kwargs)
def get_success_url(self, **kwargs): def get_success_url(self, **kwargs):
return reverse_lazy("club:club_members", kwargs={"club_id": self.club.id}) return reverse_lazy(
"club:club_members", kwargs={"club_id": self.get_object().id}
)
class ClubOldMembersView(ClubTabsMixin, CanViewMixin, DetailView): class ClubOldMembersView(ClubTabsMixin, CanViewMixin, DetailView):

View File

@ -6,7 +6,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-04-22 14:57+0200\n" "POT-Creation-Date: 2019-04-25 17:35+0200\n"
"PO-Revision-Date: 2016-07-18\n" "PO-Revision-Date: 2016-07-18\n"
"Last-Translator: Skia <skia@libskia.so>\n" "Last-Translator: Skia <skia@libskia.so>\n"
"Language-Team: AE info <ae.info@utbm.fr>\n" "Language-Team: AE info <ae.info@utbm.fr>\n"
@ -174,10 +174,10 @@ msgstr "étiquette"
msgid "target type" msgid "target type"
msgstr "type de cible" msgstr "type de cible"
#: accounting/models.py:313 club/models.py:422 #: accounting/models.py:313 club/models.py:413
#: club/templates/club/club_members.jinja:8 #: club/templates/club/club_members.jinja:16
#: club/templates/club/club_old_members.jinja:8 #: club/templates/club/club_old_members.jinja:8
#: club/templates/club/mailing.jinja:28 club/views.py:111 #: club/templates/club/mailing.jinja:28 club/views.py:112
#: counter/templates/counter/cash_summary_list.jinja:32 #: counter/templates/counter/cash_summary_list.jinja:32
#: counter/templates/counter/stats.jinja:15 #: counter/templates/counter/stats.jinja:15
#: counter/templates/counter/stats.jinja:52 #: counter/templates/counter/stats.jinja:52
@ -186,7 +186,7 @@ msgstr "type de cible"
msgid "User" msgid "User"
msgstr "Utilisateur" msgstr "Utilisateur"
#: accounting/models.py:314 club/models.py:329 #: accounting/models.py:314 club/models.py:320
#: club/templates/club/club_detail.jinja:12 #: club/templates/club/club_detail.jinja:12
#: com/templates/com/mailing_admin.jinja:11 #: com/templates/com/mailing_admin.jinja:11
#: com/templates/com/news_admin_list.jinja:23 #: com/templates/com/news_admin_list.jinja:23
@ -386,7 +386,7 @@ msgid "Delete"
msgstr "Supprimer" msgstr "Supprimer"
#: accounting/templates/accounting/bank_account_details.jinja:18 #: accounting/templates/accounting/bank_account_details.jinja:18
#: club/views.py:128 core/views/user.py:205 sas/templates/sas/picture.jinja:86 #: club/views.py:129 core/views/user.py:205 sas/templates/sas/picture.jinja:86
msgid "Infos" msgid "Infos"
msgstr "Infos" msgstr "Infos"
@ -405,7 +405,7 @@ msgstr "Nouveau compte club"
#: accounting/templates/accounting/bank_account_details.jinja:27 #: accounting/templates/accounting/bank_account_details.jinja:27
#: accounting/templates/accounting/bank_account_list.jinja:22 #: accounting/templates/accounting/bank_account_list.jinja:22
#: accounting/templates/accounting/club_account_details.jinja:58 #: accounting/templates/accounting/club_account_details.jinja:58
#: accounting/templates/accounting/journal_details.jinja:89 club/views.py:174 #: accounting/templates/accounting/journal_details.jinja:89 club/views.py:175
#: com/templates/com/news_admin_list.jinja:39 #: com/templates/com/news_admin_list.jinja:39
#: com/templates/com/news_admin_list.jinja:68 #: com/templates/com/news_admin_list.jinja:68
#: com/templates/com/news_admin_list.jinja:115 #: com/templates/com/news_admin_list.jinja:115
@ -955,48 +955,40 @@ msgstr "rôle"
msgid "description" msgid "description"
msgstr "description" msgstr "description"
#: club/models.py:282 #: club/models.py:286
msgid "User must be subscriber to take part to a club"
msgstr "L'utilisateur doit être cotisant pour faire partie d'un club"
#: club/models.py:289
msgid "User is already member of that club"
msgstr "L'utilisateur est déjà membre de ce club"
#: club/models.py:298
msgid "past member" msgid "past member"
msgstr "Anciens membres" msgstr "Anciens membres"
#: club/models.py:332 club/models.py:427 #: club/models.py:323 club/models.py:418
msgid "Email address" msgid "Email address"
msgstr "Adresse email" msgstr "Adresse email"
#: club/models.py:340 #: club/models.py:331
msgid "Enter a valid address. Only the root of the address is needed." msgid "Enter a valid address. Only the root of the address is needed."
msgstr "" msgstr ""
"Entrez une adresse valide. Seule la racine de l'adresse est nécessaire." "Entrez une adresse valide. Seule la racine de l'adresse est nécessaire."
#: club/models.py:344 com/models.py:79 com/models.py:260 core/models.py:810 #: club/models.py:335 com/models.py:79 com/models.py:260 core/models.py:810
msgid "is moderated" msgid "is moderated"
msgstr "est modéré" msgstr "est modéré"
#: club/models.py:346 com/models.py:81 com/models.py:264 #: club/models.py:337 com/models.py:81 com/models.py:264
msgid "moderator" msgid "moderator"
msgstr "modérateur" msgstr "modérateur"
#: club/models.py:415 club/templates/club/mailing.jinja:14 #: club/models.py:406 club/templates/club/mailing.jinja:14
msgid "Mailing" msgid "Mailing"
msgstr "Liste de diffusion" msgstr "Liste de diffusion"
#: club/models.py:434 #: club/models.py:425
msgid "At least user or email is required" msgid "At least user or email is required"
msgstr "Au moins un utilisateur ou un email est nécessaire" msgstr "Au moins un utilisateur ou un email est nécessaire"
#: club/models.py:442 #: club/models.py:433
msgid "This email is already suscribed in this mailing" msgid "This email is already suscribed in this mailing"
msgstr "Cet email est déjà abonné à cette mailing" msgstr "Cet email est déjà abonné à cette mailing"
#: club/models.py:471 club/templates/club/mailing.jinja:36 #: club/models.py:462 club/templates/club/mailing.jinja:36
msgid "Unregistered user" msgid "Unregistered user"
msgstr "Désabonner un utilisateur" msgstr "Désabonner un utilisateur"
@ -1021,7 +1013,7 @@ msgstr "Il n'y a pas de club dans ce site web."
msgid "Club members" msgid "Club members"
msgstr "Membres du club" msgstr "Membres du club"
#: club/templates/club/club_members.jinja:9 #: club/templates/club/club_members.jinja:17
#: club/templates/club/club_old_members.jinja:9 #: club/templates/club/club_old_members.jinja:9
#: core/templates/core/user_clubs.jinja:16 #: core/templates/core/user_clubs.jinja:16
#: core/templates/core/user_clubs.jinja:42 #: core/templates/core/user_clubs.jinja:42
@ -1031,7 +1023,7 @@ msgstr "Membres du club"
msgid "Role" msgid "Role"
msgstr "Rôle" msgstr "Rôle"
#: club/templates/club/club_members.jinja:10 #: club/templates/club/club_members.jinja:18
#: club/templates/club/club_old_members.jinja:10 #: club/templates/club/club_old_members.jinja:10
#: core/templates/core/group_list.jinja:15 #: core/templates/core/group_list.jinja:15
#: core/templates/core/user_clubs.jinja:17 #: core/templates/core/user_clubs.jinja:17
@ -1039,18 +1031,23 @@ msgstr "Rôle"
msgid "Description" msgid "Description"
msgstr "Description" msgstr "Description"
#: club/templates/club/club_members.jinja:11 #: club/templates/club/club_members.jinja:19
#: core/templates/core/user_clubs.jinja:18 #: core/templates/core/user_clubs.jinja:18
#: launderette/templates/launderette/launderette_admin.jinja:45 #: launderette/templates/launderette/launderette_admin.jinja:45
msgid "Since" msgid "Since"
msgstr "Depuis" msgstr "Depuis"
#: club/templates/club/club_members.jinja:21 #: club/templates/club/club_members.jinja:21
#: club/templates/club/club_members.jinja:46 club/views.py:363
#: core/templates/core/user_clubs.jinja:29 #: core/templates/core/user_clubs.jinja:29
msgid "Mark as old" msgid "Mark as old"
msgstr "Marquer comme ancien" msgstr "Marquer comme ancien"
#: club/templates/club/club_members.jinja:30 #: club/templates/club/club_members.jinja:50
msgid "There are no members in this club."
msgstr "Il n'y a pas de membres dans ce club."
#: club/templates/club/club_members.jinja:78
#: core/templates/core/file_detail.jinja:19 core/views/forms.py:355 #: core/templates/core/file_detail.jinja:19 core/views/forms.py:355
#: launderette/views.py:226 trombi/templates/trombi/detail.jinja:19 #: launderette/views.py:226 trombi/templates/trombi/detail.jinja:19
msgid "Add" msgid "Add"
@ -1070,8 +1067,8 @@ msgstr "Du"
msgid "To" msgid "To"
msgstr "Au" msgstr "Au"
#: club/templates/club/club_sellings.jinja:5 club/views.py:194 #: club/templates/club/club_sellings.jinja:5 club/views.py:195
#: club/views.py:478 counter/templates/counter/counter_main.jinja:19 #: club/views.py:564 counter/templates/counter/counter_main.jinja:19
#: counter/templates/counter/last_ops.jinja:35 #: counter/templates/counter/last_ops.jinja:35
msgid "Sellings" msgid "Sellings"
msgstr "Ventes" msgstr "Ventes"
@ -1097,7 +1094,7 @@ msgstr "unités"
msgid "Benefit: " msgid "Benefit: "
msgstr "Bénéfice : " msgstr "Bénéfice : "
#: club/templates/club/club_sellings.jinja:21 club/views.py:417 #: club/templates/club/club_sellings.jinja:21 club/views.py:503
#: core/templates/core/user_account_detail.jinja:18 #: core/templates/core/user_account_detail.jinja:18
#: core/templates/core/user_account_detail.jinja:51 #: core/templates/core/user_account_detail.jinja:51
#: counter/templates/counter/cash_summary_list.jinja:33 counter/views.py:168 #: counter/templates/counter/cash_summary_list.jinja:33 counter/views.py:168
@ -1246,60 +1243,79 @@ msgstr "Aucune page n'existe pour ce club"
msgid "Club stats" msgid "Club stats"
msgstr "Statistiques du club" msgstr "Statistiques du club"
#: club/views.py:138 #: club/views.py:139
msgid "Members" msgid "Members"
msgstr "Membres" msgstr "Membres"
#: club/views.py:147 #: club/views.py:148
msgid "Old members" msgid "Old members"
msgstr "Anciens membres" msgstr "Anciens membres"
#: club/views.py:157 core/templates/core/page.jinja:33 #: club/views.py:158 core/templates/core/page.jinja:33
msgid "History" msgid "History"
msgstr "Historique" msgstr "Historique"
#: club/views.py:165 core/templates/core/base.jinja:121 core/views/user.py:228 #: club/views.py:166 core/templates/core/base.jinja:121 core/views/user.py:228
#: sas/templates/sas/picture.jinja:95 trombi/views.py:60 #: sas/templates/sas/picture.jinja:95 trombi/views.py:60
msgid "Tools" msgid "Tools"
msgstr "Outils" msgstr "Outils"
#: club/views.py:185 #: club/views.py:186
msgid "Edit club page" msgid "Edit club page"
msgstr "Éditer la page de club" msgstr "Éditer la page de club"
#: club/views.py:201 #: club/views.py:202
msgid "Mailing list" msgid "Mailing list"
msgstr "Listes de diffusion" msgstr "Listes de diffusion"
#: club/views.py:210 com/views.py:141 #: club/views.py:211 com/views.py:141
msgid "Posters list" msgid "Posters list"
msgstr "Liste d'affiches" msgstr "Liste d'affiches"
#: club/views.py:220 counter/templates/counter/counter_list.jinja:21 #: club/views.py:221 counter/templates/counter/counter_list.jinja:21
#: counter/templates/counter/counter_list.jinja:43 #: counter/templates/counter/counter_list.jinja:43
#: counter/templates/counter/counter_list.jinja:59 #: counter/templates/counter/counter_list.jinja:59
msgid "Props" msgid "Props"
msgstr "Propriétés" msgstr "Propriétés"
#: club/views.py:322 core/views/forms.py:358 counter/views.py:113 #: club/views.py:319
#: trombi/views.py:141 msgid "Users to add"
msgid "Select user" msgstr "Utilisateurs à ajouter"
msgstr "Choisir un utilisateur"
#: club/views.py:381 sas/views.py:129 sas/views.py:195 sas/views.py:286 #: club/views.py:320 core/views/group.py:63
msgid "Search users to add (one or more)."
msgstr "Recherche les utilisateurs à ajouter (un ou plus)."
#: club/views.py:381
msgid "One of the selected users doesn't exist"
msgstr "Un des utilisateurs sélectionné n'existe pas"
#: club/views.py:385
msgid "User must be subscriber to take part to a club"
msgstr "L'utilisateur doit être cotisant pour faire partie d'un club"
#: club/views.py:389 core/views/group.py:82
msgid "You can not add the same user twice"
msgstr "Vous ne pouvez pas ajouter deux fois le même utilisateur"
#: club/views.py:410
msgid "You should specify a role"
msgstr "Vous devez choisir un rôle"
#: club/views.py:421 sas/views.py:129 sas/views.py:195 sas/views.py:286
msgid "You do not have the permission to do that" msgid "You do not have the permission to do that"
msgstr "Vous n'avez pas la permission de faire cela" msgstr "Vous n'avez pas la permission de faire cela"
#: club/views.py:406 counter/views.py:1481 #: club/views.py:492 counter/views.py:1481
msgid "Begin date" msgid "Begin date"
msgstr "Date de début" msgstr "Date de début"
#: club/views.py:412 com/views.py:85 com/views.py:221 counter/views.py:1487 #: club/views.py:498 com/views.py:85 com/views.py:221 counter/views.py:1487
#: election/views.py:190 subscription/views.py:52 #: election/views.py:190 subscription/views.py:52
msgid "End date" msgid "End date"
msgstr "Date de fin" msgstr "Date de fin"
#: club/views.py:435 core/templates/core/user_stats.jinja:27 #: club/views.py:521 core/templates/core/user_stats.jinja:27
#: counter/views.py:1635 #: counter/views.py:1635
msgid "Product" msgid "Product"
msgstr "Produit" msgstr "Produit"
@ -3507,6 +3523,10 @@ msgstr "Parrain"
msgid "Godchild" msgid "Godchild"
msgstr "Fillot" msgstr "Fillot"
#: core/views/forms.py:358 counter/views.py:113 trombi/views.py:141
msgid "Select user"
msgstr "Choisir un utilisateur"
#: core/views/forms.py:371 core/views/forms.py:389 election/models.py:24 #: core/views/forms.py:371 core/views/forms.py:389 election/models.py:24
#: election/views.py:167 #: election/views.py:167
msgid "edit groups" msgid "edit groups"
@ -3525,14 +3545,6 @@ msgstr "Utilisateurs à retirer du groupe"
msgid "Users to add to group" msgid "Users to add to group"
msgstr "Utilisateurs à ajouter au groupe" msgstr "Utilisateurs à ajouter au groupe"
#: core/views/group.py:63
msgid "Search users to add (one or more)."
msgstr "Recherche les utilisateurs à ajouter (un ou plus)."
#: core/views/group.py:82
msgid "You can not add the same user twice"
msgstr "Vous ne pouvez pas ajouter deux fois le même utilisateur"
#: core/views/user.py:223 trombi/templates/trombi/export.jinja:25 #: core/views/user.py:223 trombi/templates/trombi/export.jinja:25
#: trombi/templates/trombi/user_profile.jinja:11 #: trombi/templates/trombi/user_profile.jinja:11
msgid "Pictures" msgid "Pictures"