From c07cf654fb6a62fc994847fdfebb56d876b3e138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Tue, 23 Aug 2016 17:40:24 +0200 Subject: [PATCH 1/5] Premier jet des clubs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On peut inscrire les utilisateurs aux différents clubs du COF. Le formulaire d'inscription est inclus dans la page “inscription d'un nouveau membre”. À réfléchir comment ajouter des infos supplémentaires : chèque des caution, commentaires, etc. --- gestioncof/forms.py | 13 +++++- gestioncof/migrations/0007_alter_club.py | 47 +++++++++++++++++++++ gestioncof/models.py | 9 ++-- gestioncof/templates/registration_form.html | 4 ++ gestioncof/views.py | 30 ++++++++++--- 5 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 gestioncof/migrations/0007_alter_club.py diff --git a/gestioncof/forms.py b/gestioncof/forms.py index 064eef8e..f6676c4f 100644 --- a/gestioncof/forms.py +++ b/gestioncof/forms.py @@ -13,7 +13,7 @@ from django.db.models import Max from django.core.validators import MinLengthValidator from gestioncof.models import CofProfile, EventCommentValue, \ - CalendarSubscription + CalendarSubscription, Club from gestioncof.widgets import TriStateCheckbox from gestioncof.shared import lock_table, unlock_table @@ -389,3 +389,14 @@ class CalendarForm(forms.ModelForm): model = CalendarSubscription fields = ['subscribe_to_events', 'subscribe_to_my_shows', 'other_shows'] + + +class ClubsForm(forms.Form): + """ + Formulaire d'inscription d'un membre à plusieurs clubs du COF. + """ + clubs = forms.ModelMultipleChoiceField( + label="Inscriptions aux clubs du COF", + queryset=Club.objects.all(), + widget=forms.CheckboxSelectMultiple, + required=False) diff --git a/gestioncof/migrations/0007_alter_club.py b/gestioncof/migrations/0007_alter_club.py new file mode 100644 index 00000000..324c59a6 --- /dev/null +++ b/gestioncof/migrations/0007_alter_club.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + ('gestioncof', '0006_add_calendar'), + ] + + operations = [ + migrations.AlterField( + model_name='club', + name='name', + field=models.CharField(unique=True, max_length=200, + verbose_name='Nom') + ), + migrations.AlterField( + model_name='club', + name='description', + field=models.TextField(verbose_name='Description', blank=True) + ), + migrations.AlterField( + model_name='club', + name='membres', + field=models.ManyToManyField(related_name='clubs', + to=settings.AUTH_USER_MODEL, + blank=True), + ), + migrations.AlterField( + model_name='club', + name='respos', + field=models.ManyToManyField(related_name='clubs_geres', + to=settings.AUTH_USER_MODEL, + blank=True), + ), + migrations.AlterField( + model_name='event', + name='start_date', + field=models.DateTimeField(null=True, + verbose_name='Date de d\xe9but', + blank=True), + ), + ] diff --git a/gestioncof/models.py b/gestioncof/models.py index 95837a3a..382a5750 100644 --- a/gestioncof/models.py +++ b/gestioncof/models.py @@ -86,10 +86,11 @@ post_save.connect(create_user_profile, sender=User) @python_2_unicode_compatible class Club(models.Model): - name = models.CharField("Nom", max_length=200) - description = models.TextField("Description") - respos = models.ManyToManyField(User, related_name="clubs_geres") - membres = models.ManyToManyField(User, related_name="clubs") + name = models.CharField("Nom", max_length=200, unique=True) + description = models.TextField("Description", blank=True) + respos = models.ManyToManyField(User, related_name="clubs_geres", + blank=True) + membres = models.ManyToManyField(User, related_name="clubs", blank=True) def __str__(self): return self.name diff --git a/gestioncof/templates/registration_form.html b/gestioncof/templates/registration_form.html index 1eb16393..8668152b 100644 --- a/gestioncof/templates/registration_form.html +++ b/gestioncof/templates/registration_form.html @@ -13,6 +13,10 @@ {{ user_form | bootstrap }} {{ profile_form | bootstrap }} +
+ + {{ clubs_form | bootstrap }} +
{{ event_formset.management_form }} {% for event_form in event_formset %}
diff --git a/gestioncof/views.py b/gestioncof/views.py index 5a665860..4e428791 100644 --- a/gestioncof/views.py +++ b/gestioncof/views.py @@ -29,7 +29,7 @@ from gestioncof.decorators import buro_required, cof_required from gestioncof.forms import UserProfileForm, EventStatusFilterForm, \ SurveyForm, SurveyStatusFilterForm, RegistrationUserForm, \ RegistrationProfileForm, EventForm, CalendarForm, EventFormset, \ - RegistrationPassUserForm + RegistrationPassUserForm, ClubsForm from bda.models import Tirage, Spectacle @@ -341,8 +341,9 @@ def registration_form2(request, login_clipper=None, username=None): profile_form = RegistrationProfileForm(initial={ 'login_clipper': login_clipper}) registration_set_ro_fields(user_form, profile_form) - # events + # events & clubs event_formset = EventFormset(events=events, prefix='events') + clubs_form = ClubsForm(initial={'clubs': member.clubs.all()}) if username: member = get_object_or_404(User, username=username) (profile, _) = CofProfile.objects.get_or_create(user=member) @@ -361,15 +362,20 @@ def registration_form2(request, login_clipper=None, username=None): event_formset = EventFormset( events=events, prefix='events', current_registrations=current_registrations) + # Clubs + clubs_form = ClubsForm(initial={'clubs': member.clubs.all()}) elif not login_clipper: # new user user_form = RegistrationPassUserForm() profile_form = RegistrationProfileForm() event_formset = EventFormset(events=events, prefix='events') + clubs_form = ClubsForm() return render(request, "registration_form.html", - {"user_form": user_form, "profile_form": profile_form, - "member": member, "login_clipper": login_clipper, - "event_formset": event_formset}) + {"member": member, "login_clipper": login_clipper, + "user_form": user_form, + "profile_form": profile_form, + "event_formset": event_formset, + "clubs_form": clubs_form}) @buro_required @@ -392,6 +398,7 @@ def registration(request): else: user_form = RegistrationUserForm profile_form = RegistrationProfileForm(request_dict) + clubs_form = ClubsForm(request_dict) events = Event.objects.filter(old=False).all() event_formset = EventFormset(events=events, data=request_dict, prefix='events') @@ -419,10 +426,13 @@ def registration(request): # Maintenant on remplit le formulaire de profil profile_form = RegistrationProfileForm(request_dict, instance=profile) - if profile_form.is_valid() and event_formset.is_valid(): + if (profile_form.is_valid() and event_formset.is_valid() + and clubs_form.is_valid()): + # Enregistrement du profil profile = profile_form.save() if profile.is_cof and not was_cof: send_custom_mail(member, "bienvenue") + # Enregistrement des inscriptions aux événements for form in event_formset: if 'status' not in form.cleaned_data: form.cleaned_data['status'] = 'no' @@ -455,6 +465,11 @@ def registration(request): comments = field.default send_custom_mail(member, "mega", {"remarques": comments}) + # Enregistrement des inscriptions aux clubs + member.clubs.clear() + for club in clubs_form.cleaned_data['clubs']: + club.membres.add(member) + club.save() success = True return render(request, "registration_post.html", {"success": success, @@ -462,7 +477,8 @@ def registration(request): "profile_form": profile_form, "member": member, "login_clipper": login_clipper, - "event_formset": event_formset}) + "event_formset": event_formset, + "clubs_form": clubs_form}) else: return render(request, "registration.html") From f25243b0824ae67f1178fea7ded76cc71b60dd64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Tue, 23 Aug 2016 18:57:59 +0200 Subject: [PATCH 2/5] Ajout d'une vue : membres d'un club MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Liste des membres inscrits à un club. - Vue accessible aux membres du burô et aux respos des clubs concernés. --- cof/urls.py | 5 ++++- gestioncof/templates/membres_clubs.html | 20 ++++++++++++++++++++ gestioncof/urls.py | 4 ++++ gestioncof/views.py | 16 ++++++++++++++-- 4 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 gestioncof/templates/membres_clubs.html diff --git a/cof/urls.py b/cof/urls.py index 5a4a49dc..750107fb 100644 --- a/cof/urls.py +++ b/cof/urls.py @@ -16,7 +16,8 @@ from django.contrib.auth import views as django_views from django_cas_ng import views as django_cas_views from gestioncof import views as gestioncof_views, csv_views from gestioncof.urls import export_patterns, petitcours_patterns, \ - surveys_patterns, events_patterns, calendar_patterns + surveys_patterns, events_patterns, calendar_patterns, \ + clubs_patterns from gestioncof.autocomplete import autocomplete @@ -38,6 +39,8 @@ urlpatterns = [ url(r'^event/', include(events_patterns)), # Calendrier url(r'^calendar/', include(calendar_patterns)), + # Clubs + url(r'^clubs/', include(clubs_patterns)), # Authentification url(r'^cof/denied$', TemplateView.as_view(template_name='cof-denied.html'), name="cof-denied"), diff --git a/gestioncof/templates/membres_clubs.html b/gestioncof/templates/membres_clubs.html new file mode 100644 index 00000000..d93e8cdb --- /dev/null +++ b/gestioncof/templates/membres_clubs.html @@ -0,0 +1,20 @@ +{% extends "base_title.html" %} + +{% block realcontent %} +

{{ club }}

+ +{% if club.membres.exists %} +

Liste des membres

+ +{% for member in club.membres.all %} + + + + +{% endfor %} + +{% else %} +Ce club ne contient actuellement aucun membre. +{% endif %} + +{% endblock %} diff --git a/gestioncof/urls.py b/gestioncof/urls.py index 89cd5aa8..367cfbe8 100644 --- a/gestioncof/urls.py +++ b/gestioncof/urls.py @@ -52,3 +52,7 @@ calendar_patterns = [ url(r'^(?P[a-z0-9-]+)/calendar.ics$', 'gestioncof.views.calendar_ics') ] + +clubs_patterns = [ + url(r'^membres/(?P\w+)', views.membres_club) +] diff --git a/gestioncof/views.py b/gestioncof/views.py index 4e428791..247d6cad 100644 --- a/gestioncof/views.py +++ b/gestioncof/views.py @@ -10,7 +10,7 @@ from datetime import timedelta from icalendar import Calendar, Event as Vevent from django.shortcuts import redirect, get_object_or_404, render -from django.http import Http404, HttpResponse +from django.http import Http404, HttpResponse, HttpResponseForbidden from django.contrib.auth.decorators import login_required from django.contrib.auth.views import login as django_login_view from django.contrib.auth.models import User @@ -24,7 +24,7 @@ from gestioncof.models import Event, EventRegistration, EventOption, \ from gestioncof.models import EventCommentField, EventCommentValue, \ CalendarSubscription from gestioncof.shared import send_custom_mail -from gestioncof.models import CofProfile, Clipper +from gestioncof.models import CofProfile, Clipper, Club from gestioncof.decorators import buro_required, cof_required from gestioncof.forms import UserProfileForm, EventStatusFilterForm, \ SurveyForm, SurveyStatusFilterForm, RegistrationUserForm, \ @@ -483,6 +483,18 @@ def registration(request): return render(request, "registration.html") +@login_required +def membres_club(request, name): + # Vérification des permissions : l'utilisateur doit être membre du burô + # ou respo du club. + user = request.user + club = get_object_or_404(Club, name=name) + if not request.user.profile.is_buro \ + and club not in user.clubs_geres.all(): + return HttpResponseForbidden('

Permission denied

') + return render(request, 'membres_clubs.html', {'club': club}) + + @buro_required def export_members(request): response = HttpResponse(content_type='text/csv') From ccc1c791010bdd867e179215e3391f450f568386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Tue, 23 Aug 2016 20:10:25 +0200 Subject: [PATCH 3/5] Ajout de la liste des clubs --- gestioncof/templates/liste_clubs.html | 25 +++++++++++++++++++++++++ gestioncof/urls.py | 3 ++- gestioncof/views.py | 16 ++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 gestioncof/templates/liste_clubs.html diff --git a/gestioncof/templates/liste_clubs.html b/gestioncof/templates/liste_clubs.html new file mode 100644 index 00000000..0551817e --- /dev/null +++ b/gestioncof/templates/liste_clubs.html @@ -0,0 +1,25 @@ +{% extends "base_title.html" %} + +{% block page_size %}col-sm-8{% endblock %} + +{% block realcontent %} +

Clubs enregistrés sur GestioCOF

+ +{% endblock %} diff --git a/gestioncof/urls.py b/gestioncof/urls.py index 367cfbe8..f0590b1e 100644 --- a/gestioncof/urls.py +++ b/gestioncof/urls.py @@ -54,5 +54,6 @@ calendar_patterns = [ ] clubs_patterns = [ - url(r'^membres/(?P\w+)', views.membres_club) + url(r'^membres/(?P\w+)', views.membres_club, name='membres-club'), + url(r'^liste', views.liste_clubs, name='liste-clubs') ] diff --git a/gestioncof/views.py b/gestioncof/views.py index 247d6cad..c74c1da6 100644 --- a/gestioncof/views.py +++ b/gestioncof/views.py @@ -483,6 +483,11 @@ def registration(request): return render(request, "registration.html") +# ----- +# Clubs +# ----- + + @login_required def membres_club(request, name): # Vérification des permissions : l'utilisateur doit être membre du burô @@ -495,6 +500,17 @@ def membres_club(request, name): return render(request, 'membres_clubs.html', {'club': club}) +@cof_required +def liste_clubs(request): + clubs = Club.objects + if request.user.profile.is_buro: + data = {'owned_clubs': clubs.all()} + else: + data = {'owned_clubs': request.user.clubs_geres, + 'other_clubs': clubs.exclude(respos=request.user)} + return render(request, 'liste_clubs.html', data) + + @buro_required def export_members(request): response = HttpResponse(content_type='text/csv') From 559ac5a39d23f97c8d048b802648c098b1491428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Tue, 23 Aug 2016 21:09:47 +0200 Subject: [PATCH 4/5] On peut changer de respos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tout se fait simplement depuis la vue `/clubs/membres/`. Il est déconseillé de passer par l'interface admin. --- gestioncof/templates/home.html | 1 + gestioncof/templates/liste_clubs.html | 4 +-- gestioncof/templates/membres_clubs.html | 35 ++++++++++++++++++++----- gestioncof/urls.py | 4 ++- gestioncof/views.py | 18 ++++++++++++- 5 files changed, 51 insertions(+), 11 deletions(-) diff --git a/gestioncof/templates/home.html b/gestioncof/templates/home.html index 9d32c40c..ccd72cce 100644 --- a/gestioncof/templates/home.html +++ b/gestioncof/templates/home.html @@ -70,6 +70,7 @@
  • Administration générale
  • Demandes de petits cours
  • Inscription d'un nouveau membre
  • +
  • Gestion des clubs
    • Évènements & Sondages

      diff --git a/gestioncof/templates/liste_clubs.html b/gestioncof/templates/liste_clubs.html index 0551817e..c248a7a6 100644 --- a/gestioncof/templates/liste_clubs.html +++ b/gestioncof/templates/liste_clubs.html @@ -8,7 +8,7 @@ {% for club in owned_clubs %}
    • - {{ club }} ({% for respo in club.respos.all %}{{ respo.get_full_name }}{% empty %}pas de respo{% endfor %}) + {{ club }} ({% for respo in club.respos.all %}{{ respo.get_full_name }}, {% empty %}pas de respo{% endfor %})
    • {% endfor %} @@ -16,7 +16,7 @@ {% for club in other_clubs %}
    • - {{ club }} ({% for respo in club.respos.all %}{{ respo.get_full_name }}{% empty %}pas de respo{% endfor %}) + {{ club }} ({% for respo in club.respos.all %}{{ respo.get_full_name }}, {% empty %}pas de respo{% endfor %})

    • {% endfor %} diff --git a/gestioncof/templates/membres_clubs.html b/gestioncof/templates/membres_clubs.html index d93e8cdb..8c932ed5 100644 --- a/gestioncof/templates/membres_clubs.html +++ b/gestioncof/templates/membres_clubs.html @@ -1,20 +1,41 @@ {% extends "base_title.html" %} + {% block realcontent %}

      {{ club }}

      + +{% if club.respos.exists %} +

      Respo{{ club.respos.all|pluralize }}

      +
    {{ member }}{{ member.email }}
    +{% for member in club.respos.all %} + + + + + +{% endfor %} +
    {{ member }}{{ member.email }}
    +{% else %} +

    Pas de respo

    +{% endif %} + + {% if club.membres.exists %}

    Liste des membres

    -{% for member in club.membres.all %} - - - - +{% for member in members_no_respo %} + + + + + {% endfor %} - +
    {{ member }}{{ member.email }}
    {{ member }}{{ member.email }}
    {% else %} -Ce club ne contient actuellement aucun membre. +Ce club ne comporte actuellement aucun membre. {% endif %} {% endblock %} diff --git a/gestioncof/urls.py b/gestioncof/urls.py index f0590b1e..ad108005 100644 --- a/gestioncof/urls.py +++ b/gestioncof/urls.py @@ -55,5 +55,7 @@ calendar_patterns = [ clubs_patterns = [ url(r'^membres/(?P\w+)', views.membres_club, name='membres-club'), - url(r'^liste', views.liste_clubs, name='liste-clubs') + url(r'^liste', views.liste_clubs, name='liste-clubs'), + url(r'^change_respo/(?P\w+)/(?P\d+)', + views.change_respo, name='change-respo'), ] diff --git a/gestioncof/views.py b/gestioncof/views.py index c74c1da6..b3b69902 100644 --- a/gestioncof/views.py +++ b/gestioncof/views.py @@ -497,7 +497,23 @@ def membres_club(request, name): if not request.user.profile.is_buro \ and club not in user.clubs_geres.all(): return HttpResponseForbidden('

    Permission denied

    ') - return render(request, 'membres_clubs.html', {'club': club}) + members_no_respo = club.membres.exclude(clubs_geres=club).all() + return render(request, 'membres_clubs.html', + {'club': club, + 'members_no_respo': members_no_respo}) + + +@buro_required +def change_respo(request, club_name, user_id): + club = get_object_or_404(Club, name=club_name) + user = get_object_or_404(User, id=user_id) + if user in club.respos.all(): + club.respos.remove(user) + elif user in club.membres.all(): + club.respos.add(user) + else: + raise Http404 + return redirect('membres-club', name=club_name) @cof_required From 14c90858871b4787912877a6b3f32b480fde1c46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Tue, 23 Aug 2016 22:15:31 +0200 Subject: [PATCH 5/5] =?UTF-8?q?Emp=C3=AAche=20d'assigner=20un=20respo=20no?= =?UTF-8?q?n=20membre=20du=20club?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dans l'interface admin --- gestioncof/admin.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/gestioncof/admin.py b/gestioncof/admin.py index 342317f3..7aedf089 100644 --- a/gestioncof/admin.py +++ b/gestioncof/admin.py @@ -4,6 +4,7 @@ from __future__ import division from __future__ import print_function from __future__ import unicode_literals +from django import forms from django.contrib import admin from gestioncof.models import SurveyQuestionAnswer, SurveyQuestion, \ CofProfile, EventOption, EventOptionChoice, Event, Club, CustomMail, \ @@ -232,6 +233,25 @@ class PetitCoursDemandeAdmin(admin.ModelAdmin): class CustomMailAdmin(admin.ModelAdmin): search_fields = ('shortname', 'title') + +class ClubAdminForm(forms.ModelForm): + def clean(self): + cleaned_data = super(ClubAdminForm, self).clean() + respos = cleaned_data.get('respos') + members = cleaned_data.get('membres') + for respo in respos.all(): + if respo not in members: + raise forms.ValidationError( + "Erreur : le respo %s n'est pas membre du club." + % respo.get_full_name()) + return cleaned_data + + +class ClubAdmin(admin.ModelAdmin): + list_display = ['name'] + form = ClubAdminForm + + admin.site.register(Survey, SurveyAdmin) admin.site.register(SurveyQuestion, SurveyQuestionAdmin) admin.site.register(Event, EventAdmin) @@ -239,7 +259,7 @@ admin.site.register(EventOption, EventOptionAdmin) admin.site.unregister(User) admin.site.register(User, UserProfileAdmin) admin.site.register(CofProfile) -admin.site.register(Club) +admin.site.register(Club, ClubAdmin) admin.site.register(CustomMail) admin.site.register(PetitCoursSubject) admin.site.register(PetitCoursAbility, PetitCoursAbilityAdmin)