mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-11-04 02:53:06 +00:00 
			
		
		
		
	Merge branch 'clubs' into 'master'
Améliore la gestion de membres de clubs See merge request ae/Sith!196
This commit is contained in:
		@@ -276,18 +276,6 @@ class Membership(models.Model):
 | 
			
		||||
        _("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):
 | 
			
		||||
        return (
 | 
			
		||||
            self.club.name
 | 
			
		||||
@@ -304,12 +292,15 @@ class Membership(models.Model):
 | 
			
		||||
        """
 | 
			
		||||
        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
 | 
			
		||||
        """
 | 
			
		||||
        if user.memberships:
 | 
			
		||||
            ms = user.memberships.filter(club=self.club, end_date=None).first()
 | 
			
		||||
            if membership:  # This is for optimisation purpose
 | 
			
		||||
                ms = membership
 | 
			
		||||
            else:
 | 
			
		||||
                ms = user.memberships.filter(club=self.club, end_date=None).first()
 | 
			
		||||
            return (ms and ms.role >= self.role) or user.is_in_group(
 | 
			
		||||
                settings.SITH_MAIN_BOARD_GROUP
 | 
			
		||||
            )
 | 
			
		||||
 
 | 
			
		||||
@@ -1,35 +1,80 @@
 | 
			
		||||
{% 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 %}
 | 
			
		||||
    <h2>{% trans %}Club members{% endtrans %}</h2>
 | 
			
		||||
    <table>
 | 
			
		||||
        <thead>
 | 
			
		||||
            <td>{% trans %}User{% endtrans %}</td>
 | 
			
		||||
            <td>{% trans %}Role{% endtrans %}</td>
 | 
			
		||||
            <td>{% trans %}Description{% endtrans %}</td>
 | 
			
		||||
            <td>{% trans %}Since{% endtrans %}</td>
 | 
			
		||||
        </thead>
 | 
			
		||||
        <tbody>
 | 
			
		||||
        {% for m in club.members.filter(end_date=None).order_by('-role').all() %}
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td>{{ user_profile_link(m.user) }}</td>
 | 
			
		||||
                <td>{{ settings.SITH_CLUB_ROLES[m.role] }}</td>
 | 
			
		||||
                <td>{{ m.description }}</td>
 | 
			
		||||
                <td>{{ m.start_date }}</td>
 | 
			
		||||
                {% if m.can_be_edited_by(user) %}
 | 
			
		||||
                <td><a href="{{ url('club:membership_set_old', membership_id=m.id) }}">{% trans %}Mark as old{% endtrans %}</a></td>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
            </tr>
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
        </tbody>
 | 
			
		||||
    </table>
 | 
			
		||||
    <form action="{{ url('club:club_members', club_id=club.id) }}" method="post">
 | 
			
		||||
    {% if members %}
 | 
			
		||||
    <form action="{{ url('club:club_members', club_id=club.id) }}" id="users_old" method="post">
 | 
			
		||||
        {% csrf_token %}
 | 
			
		||||
        {{ form.as_p() }}
 | 
			
		||||
        {% set users_old = dict(form.users_old | groupby("choice_label")) %}
 | 
			
		||||
        {% if users_old %}
 | 
			
		||||
            {{ select_all_checkbox("users_old") }}
 | 
			
		||||
            <p></p>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        <table>
 | 
			
		||||
            <thead>
 | 
			
		||||
                <td>{% trans %}User{% endtrans %}</td>
 | 
			
		||||
                <td>{% trans %}Role{% endtrans %}</td>
 | 
			
		||||
                <td>{% trans %}Description{% endtrans %}</td>
 | 
			
		||||
                <td>{% trans %}Since{% endtrans %}</td>
 | 
			
		||||
                {% if users_old %}
 | 
			
		||||
                    <td>{% trans %}Mark as old{% endtrans %}</td>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
            </thead>
 | 
			
		||||
            <tbody>
 | 
			
		||||
            {% for m in members %}
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td>{{ user_profile_link(m.user) }}</td>
 | 
			
		||||
                    <td>{{ settings.SITH_CLUB_ROLES[m.role] }}</td>
 | 
			
		||||
                    <td>{{ m.description }}</td>
 | 
			
		||||
                    <td>{{ m.start_date }}</td>
 | 
			
		||||
                    {% if users_old %}
 | 
			
		||||
                        <td>
 | 
			
		||||
                        {% set user_old = users_old[m.user.get_display_name()] %}
 | 
			
		||||
                        {% if user_old %}
 | 
			
		||||
                            {{ user_old[0].tag() }}
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                        </td>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                </tr>
 | 
			
		||||
            {% endfor %}
 | 
			
		||||
            </tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
        {{ 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 %}
 | 
			
		||||
        {{ 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>
 | 
			
		||||
    </form>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										269
									
								
								club/tests.py
									
									
									
									
									
								
							
							
						
						
									
										269
									
								
								club/tests.py
									
									
									
									
									
								
							@@ -44,30 +44,54 @@ class ClubTest(TestCase):
 | 
			
		||||
        self.client.login(username="root", password="plop")
 | 
			
		||||
        self.client.post(
 | 
			
		||||
            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(
 | 
			
		||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id})
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(response.status_code == 200)
 | 
			
		||||
        self.assertTrue(
 | 
			
		||||
            "S' Kia</a></td>\\n                <td>Responsable info</td>"
 | 
			
		||||
            "S' Kia</a></td>\\n                    <td>Responsable info</td>"
 | 
			
		||||
            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' 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):
 | 
			
		||||
        self.client.login(username="root", password="plop")
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            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('<ul class="errorlist nonfield"><li>' in str(response.content))
 | 
			
		||||
        self.assertTrue('<ul class="errorlist"><li>' in str(response.content))
 | 
			
		||||
        response = self.client.get(
 | 
			
		||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id})
 | 
			
		||||
        )
 | 
			
		||||
        self.assertFalse(
 | 
			
		||||
            "Guy Carlier</a></td>\\n                <td>Responsable info</td>"
 | 
			
		||||
            "Guy Carlier</a></td>\\n                    <td>Responsable info</td>"
 | 
			
		||||
            in str(response.content)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
@@ -75,18 +99,18 @@ class ClubTest(TestCase):
 | 
			
		||||
        self.client.login(username="root", password="plop")
 | 
			
		||||
        self.client.post(
 | 
			
		||||
            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(
 | 
			
		||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id})
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(
 | 
			
		||||
            "S' Kia</a></td>\\n                <td>Responsable info</td>"
 | 
			
		||||
            "S' Kia</a></td>\\n                    <td>Responsable info</td>"
 | 
			
		||||
            in str(response.content)
 | 
			
		||||
        )
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            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.assertFalse(
 | 
			
		||||
@@ -94,23 +118,47 @@ class ClubTest(TestCase):
 | 
			
		||||
            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):
 | 
			
		||||
        self.client.login(username="root", password="plop")
 | 
			
		||||
        self.client.post(
 | 
			
		||||
            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.post(
 | 
			
		||||
            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(
 | 
			
		||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id})
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(response.status_code == 200)
 | 
			
		||||
        self.assertTrue(
 | 
			
		||||
            """Richard Batsbak</a></td>\\n                <td>Vice-Pr\\xc3\\xa9sident</td>"""
 | 
			
		||||
            """Richard Batsbak</a></td>\\n                    <td>Vice-Pr\\xc3\\xa9sident</td>"""
 | 
			
		||||
            in str(response.content)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
@@ -118,15 +166,210 @@ class ClubTest(TestCase):
 | 
			
		||||
        self.client.login(username="root", password="plop")
 | 
			
		||||
        self.client.post(
 | 
			
		||||
            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")
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            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(
 | 
			
		||||
            "<li>Vous n'avez pas la permission de faire cela</li>"
 | 
			
		||||
            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' 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' 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' 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' 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' 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' Kia</a></td>\\n                    <td>Responsable info</td>"
 | 
			
		||||
            in content
 | 
			
		||||
        )
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										185
									
								
								club/views.py
									
									
									
									
									
								
							
							
						
						
									
										185
									
								
								club/views.py
									
									
									
									
									
								
							@@ -33,7 +33,7 @@ from django.core.urlresolvers import reverse, reverse_lazy
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
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.shortcuts import get_object_or_404, redirect
 | 
			
		||||
 | 
			
		||||
@@ -44,6 +44,7 @@ from core.views import (
 | 
			
		||||
    CanEditPropMixin,
 | 
			
		||||
    TabedViewMixin,
 | 
			
		||||
    PageEditViewBase,
 | 
			
		||||
    DetailFormView,
 | 
			
		||||
)
 | 
			
		||||
from core.views.forms import SelectDate, SelectDateTime
 | 
			
		||||
from club.models import Club, Membership, Mailing, MailingSubscription
 | 
			
		||||
@@ -305,7 +306,7 @@ class ClubToolsView(ClubTabsMixin, CanEditMixin, DetailView):
 | 
			
		||||
    current_tab = "tools"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ClubMemberForm(forms.ModelForm):
 | 
			
		||||
class ClubMemberForm(forms.Form):
 | 
			
		||||
    """
 | 
			
		||||
    Form handling the members of a club
 | 
			
		||||
    """
 | 
			
		||||
@@ -313,24 +314,115 @@ class ClubMemberForm(forms.ModelForm):
 | 
			
		||||
    error_css_class = "error"
 | 
			
		||||
    required_css_class = "required"
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Membership
 | 
			
		||||
        fields = ["user", "role", "start_date", "description"]
 | 
			
		||||
        widgets = {"start_date": SelectDate}
 | 
			
		||||
 | 
			
		||||
    user = AutoCompleteSelectField(
 | 
			
		||||
        "users", required=True, label=_("Select user"), help_text=None
 | 
			
		||||
    users = AutoCompleteSelectMultipleField(
 | 
			
		||||
        "users",
 | 
			
		||||
        label=_("Users to add"),
 | 
			
		||||
        help_text=_("Search users to add (one or more)."),
 | 
			
		||||
        required=False,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
        return self.instance.club
 | 
			
		||||
        cleaned_data = super(ClubMemberForm, self).clean()
 | 
			
		||||
        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
 | 
			
		||||
    """
 | 
			
		||||
@@ -341,52 +433,45 @@ class ClubMembersView(ClubTabsMixin, CanViewMixin, UpdateView):
 | 
			
		||||
    template_name = "club/club_members.jinja"
 | 
			
		||||
    current_tab = "members"
 | 
			
		||||
 | 
			
		||||
    def get_form(self):
 | 
			
		||||
        """
 | 
			
		||||
        Here we get a Membership object, but the view handles Club object.
 | 
			
		||||
        That's why the save method of ClubMemberForm is overridden.
 | 
			
		||||
        """
 | 
			
		||||
        form = super(ClubMembersView, self).get_form()
 | 
			
		||||
        if (
 | 
			
		||||
            "user" in form.data and form.data.get("user") != ""
 | 
			
		||||
        ):  # Load an existing membership if possible
 | 
			
		||||
            form.instance = (
 | 
			
		||||
                Membership.objects.filter(club=self.object)
 | 
			
		||||
                .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 get_form_kwargs(self):
 | 
			
		||||
        kwargs = super(ClubMembersView, self).get_form_kwargs()
 | 
			
		||||
        kwargs["request_user"] = self.request.user
 | 
			
		||||
        kwargs["club"] = self.get_object()
 | 
			
		||||
        kwargs["club_members"] = self.members
 | 
			
		||||
        return kwargs
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, *args, **kwargs):
 | 
			
		||||
        kwargs = super(ClubMembersView, self).get_context_data(*args, **kwargs)
 | 
			
		||||
        kwargs["members"] = self.members
 | 
			
		||||
        return kwargs
 | 
			
		||||
 | 
			
		||||
    def form_valid(self, form):
 | 
			
		||||
        """
 | 
			
		||||
            Check user rights
 | 
			
		||||
        """
 | 
			
		||||
        user = self.request.user
 | 
			
		||||
        ms = self.object.get_membership_for(user)
 | 
			
		||||
        if (
 | 
			
		||||
            form.cleaned_data["role"] <= SITH_MAXIMUM_FREE_ROLE
 | 
			
		||||
            or (ms is not None and ms.role >= form.cleaned_data["role"])
 | 
			
		||||
            or user.is_board_member
 | 
			
		||||
            or user.is_root
 | 
			
		||||
        ):
 | 
			
		||||
            form.save()
 | 
			
		||||
            form = self.form_class()
 | 
			
		||||
            return super(ModelFormMixin, self).form_valid(form)
 | 
			
		||||
        else:
 | 
			
		||||
            form.add_error(None, _("You do not have the permission to do that"))
 | 
			
		||||
            return self.form_invalid(form)
 | 
			
		||||
        resp = super(ClubMembersView, self).form_valid(form)
 | 
			
		||||
 | 
			
		||||
        data = form.clean()
 | 
			
		||||
        users = data.pop("users", [])
 | 
			
		||||
        users_old = data.pop("users_old", [])
 | 
			
		||||
        for user in users:
 | 
			
		||||
            Membership(club=self.get_object(), user=user, **data).save()
 | 
			
		||||
        for user in users_old:
 | 
			
		||||
            membership = self.get_object().get_membership_for(user)
 | 
			
		||||
            membership.end_date = timezone.now()
 | 
			
		||||
            membership.save()
 | 
			
		||||
        return resp
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
 | 
			
		||||
    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):
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
msgid ""
 | 
			
		||||
msgstr ""
 | 
			
		||||
"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"
 | 
			
		||||
"Last-Translator: Skia <skia@libskia.so>\n"
 | 
			
		||||
"Language-Team: AE info <ae.info@utbm.fr>\n"
 | 
			
		||||
@@ -174,10 +174,10 @@ msgstr "étiquette"
 | 
			
		||||
msgid "target type"
 | 
			
		||||
msgstr "type de cible"
 | 
			
		||||
 | 
			
		||||
#: accounting/models.py:313 club/models.py:422
 | 
			
		||||
#: club/templates/club/club_members.jinja:8
 | 
			
		||||
#: accounting/models.py:313 club/models.py:413
 | 
			
		||||
#: club/templates/club/club_members.jinja:16
 | 
			
		||||
#: 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/stats.jinja:15
 | 
			
		||||
#: counter/templates/counter/stats.jinja:52
 | 
			
		||||
@@ -186,7 +186,7 @@ msgstr "type de cible"
 | 
			
		||||
msgid "User"
 | 
			
		||||
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
 | 
			
		||||
#: com/templates/com/mailing_admin.jinja:11
 | 
			
		||||
#: com/templates/com/news_admin_list.jinja:23
 | 
			
		||||
@@ -386,7 +386,7 @@ msgid "Delete"
 | 
			
		||||
msgstr "Supprimer"
 | 
			
		||||
 | 
			
		||||
#: 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"
 | 
			
		||||
msgstr "Infos"
 | 
			
		||||
 | 
			
		||||
@@ -405,7 +405,7 @@ msgstr "Nouveau compte club"
 | 
			
		||||
#: accounting/templates/accounting/bank_account_details.jinja:27
 | 
			
		||||
#: accounting/templates/accounting/bank_account_list.jinja:22
 | 
			
		||||
#: 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:68
 | 
			
		||||
#: com/templates/com/news_admin_list.jinja:115
 | 
			
		||||
@@ -955,48 +955,40 @@ msgstr "rôle"
 | 
			
		||||
msgid "description"
 | 
			
		||||
msgstr "description"
 | 
			
		||||
 | 
			
		||||
#: club/models.py:282
 | 
			
		||||
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
 | 
			
		||||
#: club/models.py:286
 | 
			
		||||
msgid "past member"
 | 
			
		||||
msgstr "Anciens membres"
 | 
			
		||||
 | 
			
		||||
#: club/models.py:332 club/models.py:427
 | 
			
		||||
#: club/models.py:323 club/models.py:418
 | 
			
		||||
msgid "Email address"
 | 
			
		||||
msgstr "Adresse email"
 | 
			
		||||
 | 
			
		||||
#: club/models.py:340
 | 
			
		||||
#: club/models.py:331
 | 
			
		||||
msgid "Enter a valid address. Only the root of the address is needed."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"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"
 | 
			
		||||
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"
 | 
			
		||||
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"
 | 
			
		||||
msgstr "Liste de diffusion"
 | 
			
		||||
 | 
			
		||||
#: club/models.py:434
 | 
			
		||||
#: club/models.py:425
 | 
			
		||||
msgid "At least user or email is required"
 | 
			
		||||
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"
 | 
			
		||||
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"
 | 
			
		||||
msgstr "Désabonner un utilisateur"
 | 
			
		||||
 | 
			
		||||
@@ -1021,7 +1013,7 @@ 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:9
 | 
			
		||||
#: club/templates/club/club_members.jinja:17
 | 
			
		||||
#: club/templates/club/club_old_members.jinja:9
 | 
			
		||||
#: core/templates/core/user_clubs.jinja:16
 | 
			
		||||
#: core/templates/core/user_clubs.jinja:42
 | 
			
		||||
@@ -1031,7 +1023,7 @@ msgstr "Membres du club"
 | 
			
		||||
msgid "Role"
 | 
			
		||||
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
 | 
			
		||||
#: core/templates/core/group_list.jinja:15
 | 
			
		||||
#: core/templates/core/user_clubs.jinja:17
 | 
			
		||||
@@ -1039,18 +1031,23 @@ msgstr "Rôle"
 | 
			
		||||
msgid "Description"
 | 
			
		||||
msgstr "Description"
 | 
			
		||||
 | 
			
		||||
#: club/templates/club/club_members.jinja:11
 | 
			
		||||
#: club/templates/club/club_members.jinja:19
 | 
			
		||||
#: core/templates/core/user_clubs.jinja:18
 | 
			
		||||
#: launderette/templates/launderette/launderette_admin.jinja:45
 | 
			
		||||
msgid "Since"
 | 
			
		||||
msgstr "Depuis"
 | 
			
		||||
 | 
			
		||||
#: 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
 | 
			
		||||
msgid "Mark as old"
 | 
			
		||||
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
 | 
			
		||||
#: launderette/views.py:226 trombi/templates/trombi/detail.jinja:19
 | 
			
		||||
msgid "Add"
 | 
			
		||||
@@ -1070,8 +1067,8 @@ msgstr "Du"
 | 
			
		||||
msgid "To"
 | 
			
		||||
msgstr "Au"
 | 
			
		||||
 | 
			
		||||
#: club/templates/club/club_sellings.jinja:5 club/views.py:194
 | 
			
		||||
#: club/views.py:478 counter/templates/counter/counter_main.jinja:19
 | 
			
		||||
#: club/templates/club/club_sellings.jinja:5 club/views.py:195
 | 
			
		||||
#: club/views.py:564 counter/templates/counter/counter_main.jinja:19
 | 
			
		||||
#: counter/templates/counter/last_ops.jinja:35
 | 
			
		||||
msgid "Sellings"
 | 
			
		||||
msgstr "Ventes"
 | 
			
		||||
@@ -1097,7 +1094,7 @@ msgstr "unités"
 | 
			
		||||
msgid "Benefit: "
 | 
			
		||||
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:51
 | 
			
		||||
#: 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"
 | 
			
		||||
msgstr "Statistiques du club"
 | 
			
		||||
 | 
			
		||||
#: club/views.py:138
 | 
			
		||||
#: club/views.py:139
 | 
			
		||||
msgid "Members"
 | 
			
		||||
msgstr "Membres"
 | 
			
		||||
 | 
			
		||||
#: club/views.py:147
 | 
			
		||||
#: club/views.py:148
 | 
			
		||||
msgid "Old members"
 | 
			
		||||
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"
 | 
			
		||||
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
 | 
			
		||||
msgid "Tools"
 | 
			
		||||
msgstr "Outils"
 | 
			
		||||
 | 
			
		||||
#: club/views.py:185
 | 
			
		||||
#: club/views.py:186
 | 
			
		||||
msgid "Edit club page"
 | 
			
		||||
msgstr "Éditer la page de club"
 | 
			
		||||
 | 
			
		||||
#: club/views.py:201
 | 
			
		||||
#: club/views.py:202
 | 
			
		||||
msgid "Mailing list"
 | 
			
		||||
msgstr "Listes de diffusion"
 | 
			
		||||
 | 
			
		||||
#: club/views.py:210 com/views.py:141
 | 
			
		||||
#: club/views.py:211 com/views.py:141
 | 
			
		||||
msgid "Posters list"
 | 
			
		||||
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:59
 | 
			
		||||
msgid "Props"
 | 
			
		||||
msgstr "Propriétés"
 | 
			
		||||
 | 
			
		||||
#: club/views.py:322 core/views/forms.py:358 counter/views.py:113
 | 
			
		||||
#: trombi/views.py:141
 | 
			
		||||
msgid "Select user"
 | 
			
		||||
msgstr "Choisir un utilisateur"
 | 
			
		||||
#: club/views.py:319
 | 
			
		||||
msgid "Users to add"
 | 
			
		||||
msgstr "Utilisateurs à ajouter"
 | 
			
		||||
 | 
			
		||||
#: 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"
 | 
			
		||||
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"
 | 
			
		||||
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
 | 
			
		||||
msgid "End date"
 | 
			
		||||
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
 | 
			
		||||
msgid "Product"
 | 
			
		||||
msgstr "Produit"
 | 
			
		||||
@@ -3507,6 +3523,10 @@ msgstr "Parrain"
 | 
			
		||||
msgid "Godchild"
 | 
			
		||||
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
 | 
			
		||||
#: election/views.py:167
 | 
			
		||||
msgid "edit groups"
 | 
			
		||||
@@ -3525,14 +3545,6 @@ msgstr "Utilisateurs à retirer du groupe"
 | 
			
		||||
msgid "Users to add to group"
 | 
			
		||||
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
 | 
			
		||||
#: trombi/templates/trombi/user_profile.jinja:11
 | 
			
		||||
msgid "Pictures"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user