diff --git a/bda/forms.py b/bda/forms.py new file mode 100644 index 00000000..8a652296 --- /dev/null +++ b/bda/forms.py @@ -0,0 +1,39 @@ +# coding: utf-8 + +from django import forms +from django.forms.models import inlineformset_factory, BaseInlineFormSet +from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution + +class BaseBdaFormSet(BaseInlineFormSet): + def clean(self): + """Checks that no two articles have the same title.""" + super(BaseBdaFormSet, self).clean() + if any(self.errors): + # Don't bother validating the formset unless each form is valid on its own + return + spectacles = [] + for i in range(0, self.total_form_count()): + form = self.forms[i] + if not form.cleaned_data: + continue + spectacle = form.cleaned_data['spectacle'] + delete = form.cleaned_data['DELETE'] + if not delete and spectacle in spectacles: + raise forms.ValidationError("Vous ne pouvez pas vous inscrire deux fois pour le même spectacle.") + spectacles.append(spectacle) + +class TokenForm(forms.Form): + token = forms.CharField(widget = forms.widgets.Textarea()) + +class SpectacleModelChoiceField(forms.ModelChoiceField): + def label_from_instance(self, obj): + return u"%s le %s (%s) à %.02f€" % (obj.title, obj.date_no_seconds(), obj.location, obj.price) + +class ResellForm(forms.Form): + count = forms.ChoiceField(choices = (("1","1"),("2","2"),)) + spectacle = SpectacleModelChoiceField(queryset = Spectacle.objects.none()) + + def __init__(self, participant, *args, **kwargs): + super(ResellForm, self).__init__(*args, **kwargs) + self.fields['spectacle'].queryset = participant.attributions.all().distinct() + diff --git a/bda/views.py b/bda/views.py index 1971686d..c100d128 100644 --- a/bda/views.py +++ b/bda/views.py @@ -5,8 +5,6 @@ from django.shortcuts import render, get_object_or_404 from django.contrib.auth.decorators import login_required from django.db import models from django.http import Http404 -from django import forms -from django.forms.models import inlineformset_factory, BaseInlineFormSet from django.core import serializers import hashlib @@ -20,23 +18,7 @@ from gestioncof.shared import send_custom_mail from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution from bda.algorithm import Algorithm -class BaseBdaFormSet(BaseInlineFormSet): - def clean(self): - """Checks that no two articles have the same title.""" - super(BaseBdaFormSet, self).clean() - if any(self.errors): - # Don't bother validating the formset unless each form is valid on its own - return - spectacles = [] - for i in range(0, self.total_form_count()): - form = self.forms[i] - if not form.cleaned_data: - continue - spectacle = form.cleaned_data['spectacle'] - delete = form.cleaned_data['DELETE'] - if not delete and spectacle in spectacles: - raise forms.ValidationError("Vous ne pouvez pas vous inscrire deux fois pour le même spectacle.") - spectacles.append(spectacle) +from bda.forms import BaseBdaFormSet, TokenForm, ResellForm @cof_required def etat_places(request, tirage): @@ -211,9 +193,6 @@ def do_tirage(request, tirage): else: return render(request, "bda-attrib.html", data) -class TokenForm(forms.Form): - token = forms.CharField(widget = forms.widgets.Textarea()) - @login_required def tirage(request, tirage): if request.POST: @@ -224,18 +203,6 @@ def tirage(request, tirage): form = TokenForm() return render(request, "bda-token.html", {"form": form}) -class SpectacleModelChoiceField(forms.ModelChoiceField): - def label_from_instance(self, obj): - return u"%s le %s (%s) à %.02f€" % (obj.title, obj.date_no_seconds(), obj.location, obj.price) - -class ResellForm(forms.Form): - count = forms.ChoiceField(choices = (("1","1"),("2","2"),)) - spectacle = SpectacleModelChoiceField(queryset = Spectacle.objects.none()) - - def __init__(self, participant, *args, **kwargs): - super(ResellForm, self).__init__(*args, **kwargs) - self.fields['spectacle'].queryset = participant.attributions.all().distinct() - def do_resell(request, form): spectacle = form.cleaned_data["spectacle"] count = form.cleaned_data["count"] diff --git a/cof/settings_dev.py b/cof/settings_dev.py index b83f09bc..3246b6e5 100644 --- a/cof/settings_dev.py +++ b/cof/settings_dev.py @@ -1,3 +1,5 @@ +# -*-coding:utf-8 -* + """ Django settings for cof project. @@ -41,11 +43,9 @@ INSTALLED_APPS = ( 'bda', 'bda2', 'bda3', - 'pads', - 'rezo', 'autocomplete_light', - 'eav', 'captcha', + 'django_cas_ng', 'debug_toolbar', ) @@ -148,3 +148,18 @@ AUTHENTICATION_BACKENDS = ( RECAPTCHA_PUBLIC_KEY = "DUMMY" RECAPTCHA_PRIVATE_KEY = "DUMMY" RECAPTCHA_USE_SSL = True + +# On ne veut pas la vérification de INTERNAL_IPS faite par la debug-toolbar car +# cela interfère avec l'utilisation de Vagrant. En effet, l'adresse de la +# machine physique n'est pas forcément connue, et peut difficilement être mise +# dans les INTERNAL_IPS. +def show_toolbar(request): + if not DEBUG: + return False + if request.is_ajax(): + return False + return True + +DEBUG_TOOLBAR_CONFIG = { + 'SHOW_TOOLBAR_CALLBACK': show_toolbar, +} diff --git a/cof/urls.py b/cof/urls.py index b5163952..cee7ae39 100644 --- a/cof/urls.py +++ b/cof/urls.py @@ -16,8 +16,8 @@ from gestioncof.petits_cours_views import DemandeListView urlpatterns = patterns('', url(r'^$', 'gestioncof.views.home', name = 'home'), url(r'^cof/denied$', TemplateView.as_view(template_name = 'cof-denied.html'), name = "cof-denied"), - url(r'^cas/login$', 'django_cas.views.login', name = "cas_login_view"), - url(r'^cas/logout$', 'django_cas.views.logout'), + url(r'^cas/login$', 'django_cas_ng.views.login', name = "cas_login_view"), + url(r'^cas/logout$', 'django_cas_ng.views.logout'), url(r'^outsider/login$', 'gestioncof.views.login_ext'), url(r'^outsider/logout$', 'django.contrib.auth.views.logout', {'next_page': '/gestion/'}), url(r'^outsider/password-change$', 'django.contrib.auth.views.password_change'), diff --git a/gestioncof/admin.py b/gestioncof/admin.py index 860f37a2..24f3dc38 100644 --- a/gestioncof/admin.py +++ b/gestioncof/admin.py @@ -8,7 +8,6 @@ from django.contrib.auth.admin import UserAdmin from django.core.urlresolvers import reverse from django.utils.safestring import mark_safe import django.forms as forms -#import eav.admin from django.utils.translation import ugettext_lazy as _ from django.contrib.admin import SimpleListFilter from django.db.models import Q @@ -69,9 +68,6 @@ class EventAdmin(admin.ModelAdmin): EventCommentFieldInline, ] -#from eav.forms import BaseDynamicEntityForm - -#class CofProfileInline(eav.admin.BaseEntityInline, admin.StackedInline): class CofProfileInline(admin.StackedInline): model = CofProfile #form = BaseDynamicEntityForm @@ -149,91 +145,6 @@ class EventRegistrationAdmin(admin.ModelAdmin): list_filter = ('paid',) search_fields = ('user__username', 'user__first_name', 'user__last_name', 'user__email', 'event__title') -""" -class VoterProfileInlineForm(BaseDynamicEntityForm): - def __init__(self, data=None, *args, **kwargs): - super(BaseDynamicEntityForm, self).__init__(data, *args, **kwargs) - config_cls = self.instance._eav_config_cls - self.entity = getattr(self.instance, config_cls.eav_attr) - self.base_fields = {} - self._build_dynamic_fields() - -class VoterProfileInline(eav.admin.BaseEntityInline, admin.StackedInline): - model = CofProfile - form = VoterProfileInlineForm - inline_classes = ("collapse open",) - fields = None - fieldsets = None - - def get_fieldsets(self, request, obj=None): - formset = self.get_formset(request) - fk_name = self.fk_name or formset.fk.name - kw = {fk_name: obj} if obj else {} - instance = self.model(**kw) - form = formset.form(request.POST, instance=instance) - - return [(None, {'fields': form.fields.keys()})] - -class VotedListFilter(SimpleListFilter): - # Human-readable title which will be displayed in the - # right admin sidebar just above the filter options. - title = _(u'A voté') - - # Parameter for the filter that will be used in the URL query. - parameter_name = 'voted' - - def lookups(self, request, model_admin): - return ( - ('1', _('Yes')), - ('0', _('No')), - ) - - def queryset(self, request, queryset): - # Returns the filtered queryset based on the value - # provided in the query string and retrievable via - # `self.value()`. - # - # Compare the requested value (either '80s' or '90s') - # to decide how to filter the queryset. - if self.value() == '1': - qs2 = User.objects.filter(profile__eav__a_vot = True) - return queryset.filter(pk__in = qs2.values_list('id', flat = True)) - return voters - if self.value() == '0': - qs2 = User.objects.filter(profile__eav__a_vot = False) - return queryset.filter(pk__in = qs2.values_list('id', flat = True)) - -class VoterAdmin(UserProfileAdmin): - - form = forms.ModelForm - fields = ('username','first_name','last_name') - readonly_fields = ('username','first_name','last_name') - fieldsets = None - - def is_cof(self, obj): - try: - return obj.profile.is_cof - except CofProfile.DoesNotExist: - return False - is_cof.short_description = 'Membre du COF' - is_cof.boolean = True - def a_vote(self, obj): - try: - if not obj.profile.eav.a_vot: - return False - else: - return True - except CofProfile.DoesNotExist: - return False - a_vote.short_description = 'A voté' - a_vote.boolean = True - list_display = ('profile_num',) + UserAdmin.list_display + ('is_cof','a_vote') - list_filter = ('profile__is_cof', 'profile__is_buro', VotedListFilter) - inlines = [ - VoterProfileInline, - ] -""" - class PetitCoursAbilityAdmin(admin.ModelAdmin): list_display = ('user','matiere','niveau','agrege') search_fields = ('user__username', 'user__first_name', 'user__last_name', 'user__email', 'matiere__name', 'niveau') @@ -265,5 +176,4 @@ admin.site.register(PetitCoursAbility, PetitCoursAbilityAdmin) admin.site.register(PetitCoursAttribution, PetitCoursAttributionAdmin) admin.site.register(PetitCoursAttributionCounter, PetitCoursAttributionCounterAdmin) admin.site.register(PetitCoursDemande, PetitCoursDemandeAdmin) -#admin.site.register(Voter, VoterAdmin) admin.site.register(EventRegistration, EventRegistrationAdmin) diff --git a/gestioncof/decorators.py b/gestioncof/decorators.py index b276d372..1a3d60c5 100644 --- a/gestioncof/decorators.py +++ b/gestioncof/decorators.py @@ -1,4 +1,4 @@ -from django_cas.decorators import user_passes_test +from django_cas_ng.decorators import user_passes_test def is_cof(user): try: diff --git a/gestioncof/forms.py b/gestioncof/forms.py new file mode 100644 index 00000000..2f0f78ac --- /dev/null +++ b/gestioncof/forms.py @@ -0,0 +1,294 @@ +# coding: utf-8 + +from django import forms +from django.utils.translation import ugettext_lazy as _ +from django.contrib.auth.models import User +from django.forms.widgets import RadioSelect, CheckboxSelectMultiple + +from gestioncof.models import CofProfile, EventCommentValue +from gestioncof.widgets import TriStateCheckbox +from gestioncof.shared import lock_table, unlock_table, send_custom_mail + +class EventForm(forms.Form): + def __init__(self, *args, **kwargs): + event = kwargs.pop("event") + self.event = event + current_choices = kwargs.pop("current_choices", None) + super(EventForm, self).__init__(*args, **kwargs) + choices = {} + if current_choices: + for choice in current_choices.all(): + if choice.event_option.id not in choices: + choices[choice.event_option.id] = [choice.id] + else: + choices[choice.event_option.id].append(choice.id) + all_choices = choices + for option in event.options.all(): + choices = [(choice.id, choice.value) for choice in option.choices.all()] + if option.multi_choices: + initial = [] if option.id not in all_choices else all_choices[option.id] + field = forms.MultipleChoiceField(label = option.name, + choices = choices, + widget = CheckboxSelectMultiple, + required = False, + initial = initial) + else: + initial = None if option.id not in all_choices else all_choices[option.id][0] + field = forms.ChoiceField(label = option.name, + choices = choices, + widget = RadioSelect, + required = False, + initial = initial) + field.option_id = option.id + self.fields["option_%d" % option.id] = field + + def choices(self): + for name, value in self.cleaned_data.items(): + if name.startswith('option_'): + yield (self.fields[name].option_id, value) + +class SurveyForm(forms.Form): + def __init__(self, *args, **kwargs): + survey = kwargs.pop("survey") + current_answers = kwargs.pop("current_answers", None) + super(SurveyForm, self).__init__(*args, **kwargs) + answers = {} + if current_answers: + for answer in current_answers.all(): + if answer.survey_question.id not in answers: + answers[answer.survey_question.id] = [answer.id] + else: + answers[answer.survey_question.id].append(answer.id) + for question in survey.questions.all(): + choices = [(answer.id, answer.answer) for answer in question.answers.all()] + if question.multi_answers: + initial = [] if question.id not in answers else answers[question.id] + field = forms.MultipleChoiceField(label = question.question, + choices = choices, + widget = CheckboxSelectMultiple, + required = False, + initial = initial) + else: + initial = None if question.id not in answers else answers[question.id][0] + field = forms.ChoiceField(label = question.question, + choices = choices, + widget = RadioSelect, + required = False, + initial = initial) + field.question_id = question.id + self.fields["question_%d" % question.id] = field + +class SurveyStatusFilterForm(forms.Form): + def __init__(self, *args, **kwargs): + survey = kwargs.pop("survey") + super(SurveyStatusFilterForm, self).__init__(*args, **kwargs) + answers = {} + for question in survey.questions.all(): + for answer in question.answers.all(): + name = "question_%d_answer_%d" % (question.id, answer.id) + if self.is_bound and self.data.get(self.add_prefix(name), None): + initial = self.data.get(self.add_prefix(name), None) + else: + initial = "none" + field = forms.ChoiceField(label = "%s : %s" % (question.question, answer.answer), + choices = [("yes", "yes"),("no","no"),("none","none")], + widget = TriStateCheckbox, + required = False, + initial = initial) + field.question_id = question.id + field.answer_id = answer.id + self.fields[name] = field + + def filters(self): + for name, value in self.cleaned_data.items(): + if name.startswith('question_'): + yield (self.fields[name].question_id, self.fields[name].answer_id, value) + +class EventStatusFilterForm(forms.Form): + def __init__(self, *args, **kwargs): + event = kwargs.pop("event") + super(EventStatusFilterForm, self).__init__(*args, **kwargs) + for option in event.options.all(): + for choice in option.choices.all(): + name = "option_%d_choice_%d" % (option.id, choice.id) + if self.is_bound and self.data.get(self.add_prefix(name), None): + initial = self.data.get(self.add_prefix(name), None) + else: + initial = "none" + field = forms.ChoiceField(label = "%s : %s" % (option.name, choice.value), + choices = [("yes", "yes"),("no","no"),("none","none")], + widget = TriStateCheckbox, + required = False, + initial = initial) + field.option_id = option.id + field.choice_id = choice.id + self.fields[name] = field + # has_paid + name = "event_has_paid" + if self.is_bound and self.data.get(self.add_prefix(name), None): + initial = self.data.get(self.add_prefix(name), None) + else: + initial = "none" + field = forms.ChoiceField(label = "Événement payé", + choices = [("yes", "yes"),("no","no"),("none","none")], + widget = TriStateCheckbox, + required = False, + initial = initial) + self.fields[name] = field + + def filters(self): + for name, value in self.cleaned_data.items(): + if name.startswith('option_'): + yield (self.fields[name].option_id, self.fields[name].choice_id, value) + elif name == "event_has_paid": + yield ("has_paid", None, value) + +class UserProfileForm(forms.ModelForm): + first_name = forms.CharField(label=_(u'Prénom'), max_length=30) + last_name = forms.CharField(label=_(u'Nom'), max_length=30) + + def __init__(self, *args, **kw): + super(UserProfileForm, self).__init__(*args, **kw) + self.fields['first_name'].initial = self.instance.user.first_name + self.fields['last_name'].initial = self.instance.user.last_name + + self.fields.keyOrder = [ + 'first_name', + 'last_name', + 'phone', + 'mailing_cof', + 'mailing_bda', + 'mailing_bda_revente', + ] + + def save(self, *args, **kw): + super(UserProfileForm, self).save(*args, **kw) + self.instance.user.first_name = self.cleaned_data.get('first_name') + self.instance.user.last_name = self.cleaned_data.get('last_name') + self.instance.user.save() + + class Meta: + model = CofProfile + fields = ("phone", "mailing_cof", "mailing_bda", "mailing_bda_revente",) + +class RegistrationUserForm(forms.ModelForm): + def __init__(self, *args, **kw): + super(RegistrationUserForm, self).__init__(*args, **kw) + self.fields['username'].help_text = "" + + class Meta: + model = User + fields = ("username", "first_name", "last_name", "email") + +class RegistrationProfileForm(forms.ModelForm): + def __init__(self, *args, **kw): + super(RegistrationProfileForm, self).__init__(*args, **kw) + self.fields['mailing_cof'].initial = True + self.fields['mailing_bda'].initial = True + self.fields['mailing_bda_revente'].initial = True + self.fields['num'].widget.attrs['readonly'] = True + + self.fields.keyOrder = [ + 'login_clipper', + 'phone', + 'occupation', + 'departement', + 'is_cof', + 'num', + 'type_cotiz', + 'mailing_cof', + 'mailing_bda', + 'mailing_bda_revente', + 'comments' + ] + + def save(self, *args, **kw): + instance = super(RegistrationProfileForm, self).save(*args, **kw) + if instance.is_cof and not instance.num: + # Generate new number + try: + lock_table(CofProfile) + aggregate = CofProfile.objects.aggregate(Max('num')) + instance.num = aggregate['num__max'] + 1 + instance.save() + self.cleaned_data['num'] = instance.num + self.data['num'] = instance.num + finally: + unlock_table(CofProfile) + return instance + + class Meta: + model = CofProfile + fields = ("login_clipper", "num", "phone", "occupation", "departement", "is_cof", "type_cotiz", "mailing_cof", "mailing_bda", "mailing_bda_revente", "comments") + +STATUS_CHOICES = (('no','Non'), + ('wait','Oui mais attente paiement'), + ('paid','Oui payé'),) + +class AdminEventForm(forms.Form): + status = forms.ChoiceField(label = "Inscription", choices = STATUS_CHOICES, widget = RadioSelect) + + def __init__(self, *args, **kwargs): + event = kwargs.pop("event") + self.event = event + registration = kwargs.pop("current_registration", None) + current_choices = registration.options.all() if registration is not None else [] + paid = kwargs.pop("paid", None) + if paid == True: + kwargs["initial"] = {"status":"paid"} + elif paid == False: + kwargs["initial"] = {"status":"wait"} + else: + kwargs["initial"] = {"status":"no"} + super(AdminEventForm, self).__init__(*args, **kwargs) + choices = {} + comments = {} + for choice in current_choices: + if choice.event_option.id not in choices: + choices[choice.event_option.id] = [choice.id] + else: + choices[choice.event_option.id].append(choice.id) + all_choices = choices + for option in event.options.all(): + choices = [(choice.id, choice.value) for choice in option.choices.all()] + if option.multi_choices: + initial = [] if option.id not in all_choices else all_choices[option.id] + field = forms.MultipleChoiceField(label = option.name, + choices = choices, + widget = CheckboxSelectMultiple, + required = False, + initial = initial) + else: + initial = None if option.id not in all_choices else all_choices[option.id][0] + field = forms.ChoiceField(label = option.name, + choices = choices, + widget = RadioSelect, + required = False, + initial = initial) + field.option_id = option.id + self.fields["option_%d" % option.id] = field + for commentfield in event.commentfields.all(): + initial = commentfield.default + if registration is not None: + try: + initial = registration.comments.get(commentfield = commentfield).content + except EventCommentValue.DoesNotExist: + pass + widget = forms.Textarea if commentfield.fieldtype == "text" else forms.TextInput + field = forms.CharField(label = commentfield.name, + widget = widget, + required = False, + initial = initial) + field.comment_id = commentfield.id + self.fields["comment_%d" % commentfield.id] = field + + def choices(self): + for name, value in self.cleaned_data.items(): + if name.startswith('option_'): + yield (self.fields[name].option_id, value) + + def comments(self): + for name, value in self.cleaned_data.items(): + if name.startswith('comment_'): + yield (self.fields[name].comment_id, value) + diff --git a/gestioncof/models.py b/gestioncof/models.py index 04cc829f..2086557e 100644 --- a/gestioncof/models.py +++ b/gestioncof/models.py @@ -198,20 +198,3 @@ class SurveyAnswer(models.Model): class Clipper(models.Model): username = models.CharField("Identifiant", max_length = 20) fullname = models.CharField("Nom complet", max_length = 200) - -""" -class Voter(User): - class Meta: - proxy = True - verbose_name = "voteur" - def __unicode__(self): - return u"%s %s" % (self.first_name, self.last_name) - -import eav -eav.register(CofProfile) - -class ManagerOnlyEavConfig(eav.registry.EavConfig): - manager_only = True - -eav.register(User, ManagerOnlyEavConfig) -""" diff --git a/gestioncof/shared.py b/gestioncof/shared.py index f46f7f90..5a6c7775 100644 --- a/gestioncof/shared.py +++ b/gestioncof/shared.py @@ -1,7 +1,8 @@ from django.contrib.sites.models import Site from django.conf import settings -from django_cas.backends import CASBackend, _verify as CASverify -from django_cas.models import User +from django_cas_ng.backends import CASBackend +from django_cas_ng.utils import get_cas_client +from django.contrib.auth import get_user_model from django.contrib.auth.models import User as DjangoUser from django.db import models, connection from django.core.mail import send_mail @@ -9,11 +10,14 @@ from django.template import Template, Context from gestioncof.models import CofProfile, CustomMail +User = get_user_model() + class COFCASBackend(CASBackend): def authenticate_cas(self, ticket, service, request): """Verifies CAS ticket and gets or creates User object""" - username, attributes = CASverify(ticket, service) + client = get_cas_client(service_url=service) + username, attributes, _= client.verify_ticket(ticket) if attributes: request.session['attributes'] = attributes if not username: diff --git a/gestioncof/templates/login_switch.html b/gestioncof/templates/login_switch.html index 84ae1878..fe816a78 100644 --- a/gestioncof/templates/login_switch.html +++ b/gestioncof/templates/login_switch.html @@ -3,7 +3,7 @@ {% block content %}
- + Compte clipper diff --git a/gestioncof/templates/tristate_js.html b/gestioncof/templates/tristate_js.html index 17b2d09a..af906ebe 100644 --- a/gestioncof/templates/tristate_js.html +++ b/gestioncof/templates/tristate_js.html @@ -2,9 +2,9 @@