diff --git a/preferences/admin.py b/preferences/admin.py index a8ce9335..96b4d9e1 100644 --- a/preferences/admin.py +++ b/preferences/admin.py @@ -20,35 +20,53 @@ # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - +""" +Classes admin pour les models de preferences +""" from __future__ import unicode_literals from django.contrib import admin from reversion.admin import VersionAdmin -from .models import OptionalUser, OptionalMachine, OptionalTopologie, GeneralOption, Service, AssoOption, MailMessageOption +from .models import OptionalUser, OptionalMachine, OptionalTopologie +from .models import GeneralOption, Service, AssoOption, MailMessageOption + class OptionalUserAdmin(VersionAdmin): + """Class admin options user""" pass + class OptionalTopologieAdmin(VersionAdmin): + """Class admin options topologie""" pass + class OptionalMachineAdmin(VersionAdmin): + """Class admin options machines""" pass + class GeneralOptionAdmin(VersionAdmin): + """Class admin options générales""" pass + class ServiceAdmin(VersionAdmin): + """Class admin gestion des services de la page d'accueil""" pass + class AssoOptionAdmin(VersionAdmin): + """Class admin options de l'asso""" pass + class MailMessageOptionAdmin(VersionAdmin): + """Class admin options mail""" pass + admin.site.register(OptionalUser, OptionalUserAdmin) admin.site.register(OptionalMachine, OptionalMachineAdmin) admin.site.register(OptionalTopologie, OptionalTopologieAdmin) diff --git a/preferences/forms.py b/preferences/forms.py index 887d768d..51cbb885 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -19,71 +19,116 @@ # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +""" +Formulaire d'edition des réglages : user, machine, topologie, asso... +""" from __future__ import unicode_literals -from django.forms import ModelForm, Form, ValidationError +from django.forms import ModelForm, Form from django import forms -from .models import OptionalUser, OptionalMachine, OptionalTopologie, GeneralOption, AssoOption, MailMessageOption, Service -from django.db.models import Q +from .models import OptionalUser, OptionalMachine, OptionalTopologie +from .models import GeneralOption, AssoOption, MailMessageOption, Service + class EditOptionalUserForm(ModelForm): + """Formulaire d'édition des options de l'user. (solde, telephone..)""" class Meta: model = OptionalUser fields = '__all__' def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(EditOptionalUserForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['is_tel_mandatory'].label = 'Exiger un numéro de téléphone' - self.fields['user_solde'].label = 'Activation du solde pour les utilisateurs' + super(EditOptionalUserForm, self).__init__( + *args, + prefix=prefix, + **kwargs + ) + self.fields['is_tel_mandatory'].label = 'Exiger un numéro de\ + téléphone' + self.fields['user_solde'].label = 'Activation du solde pour\ + les utilisateurs' + class EditOptionalMachineForm(ModelForm): + """Options machines (max de machines, etc)""" class Meta: model = OptionalMachine fields = '__all__' def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(EditOptionalMachineForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['password_machine'].label = "Possibilité d'attribuer un mot de passe par interface" - self.fields['max_lambdauser_interfaces'].label = "Maximum d'interfaces autorisées pour un user normal" - self.fields['max_lambdauser_aliases'].label = "Maximum d'alias dns autorisés pour un user normal" + super(EditOptionalMachineForm, self).__init__( + *args, + prefix=prefix, + **kwargs + ) + self.fields['password_machine'].label = "Possibilité d'attribuer\ + un mot de passe par interface" + self.fields['max_lambdauser_interfaces'].label = "Maximum\ + d'interfaces autorisées pour un user normal" + self.fields['max_lambdauser_aliases'].label = "Maximum d'alias\ + dns autorisés pour un user normal" + class EditOptionalTopologieForm(ModelForm): + """Options de topologie, formulaire d'edition (vlan par default etc)""" class Meta: model = OptionalTopologie fields = '__all__' def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(EditOptionalTopologieForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['vlan_decision_ok'].label = "Vlan où placer les machines après acceptation RADIUS" - self.fields['vlan_decision_nok'].label = "Vlan où placer les machines après rejet RADIUS" + super(EditOptionalTopologieForm, self).__init__( + *args, + prefix=prefix, + **kwargs + ) + self.fields['vlan_decision_ok'].label = "Vlan où placer les\ + machines après acceptation RADIUS" + self.fields['vlan_decision_nok'].label = "Vlan où placer les\ + machines après rejet RADIUS" + class EditGeneralOptionForm(ModelForm): + """Options générales (affichages de résultats de recherche, etc)""" class Meta: model = GeneralOption fields = '__all__' def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(EditGeneralOptionForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['search_display_page'].label = 'Resultats affichés dans une recherche' - self.fields['pagination_number'].label = 'Items par page, taille normale (ex users)' - self.fields['pagination_large_number'].label = 'Items par page, taille élevée (machines)' - self.fields['req_expire_hrs'].label = 'Temps avant expiration du lien de reinitialisation de mot de passe (en heures)' + super(EditGeneralOptionForm, self).__init__( + *args, + prefix=prefix, + **kwargs + ) + self.fields['search_display_page'].label = 'Resultats\ + affichés dans une recherche' + self.fields['pagination_number'].label = 'Items par page,\ + taille normale (ex users)' + self.fields['pagination_large_number'].label = 'Items par page,\ + taille élevée (machines)' + self.fields['req_expire_hrs'].label = 'Temps avant expiration du lien\ + de reinitialisation de mot de passe (en heures)' self.fields['site_name'].label = 'Nom du site web' - self.fields['email_from'].label = 'Adresse mail d\'expedition automatique' + self.fields['email_from'].label = "Adresse mail d\ + 'expedition automatique" + class EditAssoOptionForm(ModelForm): + """Options de l'asso (addresse, telephone, etc)""" class Meta: model = AssoOption fields = '__all__' def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(EditAssoOptionForm, self).__init__(*args, prefix=prefix, **kwargs) + super(EditAssoOptionForm, self).__init__( + *args, + prefix=prefix, + **kwargs + ) self.fields['name'].label = 'Nom de l\'asso' self.fields['siret'].label = 'SIRET' self.fields['adresse1'].label = 'Adresse (ligne 1)' @@ -91,20 +136,31 @@ class EditAssoOptionForm(ModelForm): self.fields['contact'].label = 'Email de contact' self.fields['telephone'].label = 'Numéro de téléphone' self.fields['pseudo'].label = 'Pseudo d\'usage' - self.fields['utilisateur_asso'].label = 'Compte utilisé pour faire les modifications depuis /admin' + self.fields['utilisateur_asso'].label = 'Compte utilisé pour\ + faire les modifications depuis /admin' + class EditMailMessageOptionForm(ModelForm): + """Formulaire d'edition des messages de bienvenue personnalisés""" class Meta: model = MailMessageOption fields = '__all__' def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(EditMailMessageOptionForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['welcome_mail_fr'].label = 'Message dans le mail de bienvenue en français' - self.fields['welcome_mail_en'].label = 'Message dans le mail de bienvenue en anglais' + super(EditMailMessageOptionForm, self).__init__( + *args, + prefix=prefix, + **kwargs + ) + self.fields['welcome_mail_fr'].label = 'Message dans le\ + mail de bienvenue en français' + self.fields['welcome_mail_en'].label = 'Message dans le\ + mail de bienvenue en anglais' + class ServiceForm(ModelForm): + """Edition, ajout de services sur la page d'accueil""" class Meta: model = Service fields = '__all__' @@ -113,6 +169,11 @@ class ServiceForm(ModelForm): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(ServiceForm, self).__init__(*args, prefix=prefix, **kwargs) -class DelServiceForm(Form): - services = forms.ModelMultipleChoiceField(queryset=Service.objects.all(), label="Enregistrements service actuels", widget=forms.CheckboxSelectMultiple) +class DelServiceForm(Form): + """Suppression de services sur la page d'accueil""" + services = forms.ModelMultipleChoiceField( + queryset=Service.objects.all(), + label="Enregistrements service actuels", + widget=forms.CheckboxSelectMultiple + ) diff --git a/preferences/models.py b/preferences/models.py index 34c4c0b1..dc1412e7 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -20,26 +20,38 @@ # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - +""" +Reglages généraux, machines, utilisateurs, mail, general pour l'application. +""" from __future__ import unicode_literals from django.db import models from cotisations.models import Paiement -from machines.models import Vlan + class OptionalUser(models.Model): + """Options pour l'user : obligation ou nom du telephone, + activation ou non du solde, autorisation du negatif, fingerprint etc""" PRETTY_NAME = "Options utilisateur" is_tel_mandatory = models.BooleanField(default=True) user_solde = models.BooleanField(default=False) - solde_negatif = models.DecimalField(max_digits=5, decimal_places=2, default=0) + solde_negatif = models.DecimalField( + max_digits=5, + decimal_places=2, + default=0 + ) gpg_fingerprint = models.BooleanField(default=True) def clean(self): + """Creation du mode de paiement par solde""" if self.user_solde: Paiement.objects.get_or_create(moyen="Solde") + class OptionalMachine(models.Model): + """Options pour les machines : maximum de machines ou d'alias par user + sans droit, activation de l'ipv6""" PRETTY_NAME = "Options machines" password_machine = models.BooleanField(default=False) @@ -47,21 +59,43 @@ class OptionalMachine(models.Model): max_lambdauser_aliases = models.IntegerField(default=10) ipv6 = models.BooleanField(default=False) + class OptionalTopologie(models.Model): + """Reglages pour la topologie : mode d'accès radius, vlan où placer + les machines en accept ou reject""" PRETTY_NAME = "Options topologie" MACHINE = 'MACHINE' DEFINED = 'DEFINED' CHOICE_RADIUS = ( - (MACHINE, 'Sur le vlan de la plage ip machine'), - (DEFINED, 'Prédéfini dans "Vlan où placer les machines après acceptation RADIUS"'), + (MACHINE, 'Sur le vlan de la plage ip machine'), + (DEFINED, 'Prédéfini dans "Vlan où placer les machines\ + après acceptation RADIUS"'), + ) + + radius_general_policy = models.CharField( + max_length=32, + choices=CHOICE_RADIUS, + default='DEFINED' + ) + vlan_decision_ok = models.OneToOneField( + 'machines.Vlan', + on_delete=models.PROTECT, + related_name='decision_ok', + blank=True, + null=True + ) + vlan_decision_nok = models.OneToOneField( + 'machines.Vlan', + on_delete=models.PROTECT, + related_name='decision_nok', + blank=True, + null=True ) - radius_general_policy = models.CharField(max_length=32, choices=CHOICE_RADIUS, default='DEFINED') - vlan_decision_ok = models.OneToOneField('machines.Vlan', on_delete=models.PROTECT, related_name='decision_ok', blank=True, null=True) - vlan_decision_nok = models.OneToOneField('machines.Vlan', on_delete=models.PROTECT, related_name='decision_nok', blank=True, null=True) - class GeneralOption(models.Model): + """Options générales : nombre de resultats par page, nom du site, + temps où les liens sont valides""" PRETTY_NAME = "Options générales" search_display_page = models.IntegerField(default=15) @@ -71,30 +105,44 @@ class GeneralOption(models.Model): site_name = models.CharField(max_length=32, default="Re2o") email_from = models.EmailField(default="www-data@serveur.net") + class Service(models.Model): + """Liste des services affichés sur la page d'accueil : url, description, + image et nom""" name = models.CharField(max_length=32) url = models.URLField() description = models.TextField() - image = models.ImageField(upload_to='logo', blank=True) + image = models.ImageField(upload_to='logo', blank=True) def __str__(self): return str(self.name) + class AssoOption(models.Model): + """Options générales de l'asso : siret, addresse, nom, etc""" PRETTY_NAME = "Options de l'association" - name = models.CharField(default="Association réseau école machin", max_length=256) + name = models.CharField( + default="Association réseau école machin", + max_length=256 + ) siret = models.CharField(default="00000000000000", max_length=32) adresse1 = models.CharField(default="1 Rue de exemple", max_length=128) adresse2 = models.CharField(default="94230 Cachan", max_length=128) contact = models.EmailField(default="contact@example.org") telephone = models.CharField(max_length=15, default="0000000000") pseudo = models.CharField(default="Asso", max_length=32) - utilisateur_asso = models.OneToOneField('users.User', on_delete=models.PROTECT, blank=True, null=True) + utilisateur_asso = models.OneToOneField( + 'users.User', + on_delete=models.PROTECT, + blank=True, + null=True + ) + class MailMessageOption(models.Model): + """Reglages, mail de bienvenue et autre""" PRETTY_NAME = "Options de corps de mail" welcome_mail_fr = models.TextField(default="") welcome_mail_en = models.TextField(default="") - diff --git a/preferences/urls.py b/preferences/urls.py index 624d2e75..2169f83c 100644 --- a/preferences/urls.py +++ b/preferences/urls.py @@ -19,6 +19,9 @@ # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +""" +Urls de l'application preferences, pointant vers les fonctions de views +""" from __future__ import unicode_literals @@ -28,15 +31,47 @@ from . import views urlpatterns = [ - url(r'^edit_options/(?P
OptionalUser)$', views.edit_options, name='edit-options'), - url(r'^edit_options/(?P
OptionalMachine)$', views.edit_options, name='edit-options'), - url(r'^edit_options/(?P
OptionalTopologie)$', views.edit_options, name='edit-options'), - url(r'^edit_options/(?P
GeneralOption)$', views.edit_options, name='edit-options'), - url(r'^edit_options/(?P
AssoOption)$', views.edit_options, name='edit-options'), - url(r'^edit_options/(?P
MailMessageOption)$', views.edit_options, name='edit-options'), + url( + r'^edit_options/(?P
OptionalUser)$', + views.edit_options, + name='edit-options' + ), + url( + r'^edit_options/(?P
OptionalMachine)$', + views.edit_options, + name='edit-options' + ), + url( + r'^edit_options/(?P
OptionalTopologie)$', + views.edit_options, + name='edit-options' + ), + url( + r'^edit_options/(?P
GeneralOption)$', + views.edit_options, + name='edit-options' + ), + url( + r'^edit_options/(?P
AssoOption)$', + views.edit_options, + name='edit-options' + ), + url( + r'^edit_options/(?P
MailMessageOption)$', + views.edit_options, + name='edit-options' + ), url(r'^add_services/$', views.add_services, name='add-services'), - url(r'^edit_services/(?P[0-9]+)$', views.edit_services, name='edit-services'), + url( + r'^edit_services/(?P[0-9]+)$', + views.edit_services, + name='edit-services' + ), url(r'^del_services/$', views.del_services, name='del-services'), - url(r'^history/(?Pservice)/(?P[0-9]+)$', views.history, name='history'), + url( + r'^history/(?Pservice)/(?P[0-9]+)$', + views.history, + name='history' + ), url(r'^$', views.display_options, name='display-options'), ] diff --git a/preferences/views.py b/preferences/views.py index 5fe1cff5..1e2c433e 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -23,48 +23,53 @@ # App de gestion des machines pour re2o # Gabriel Détraz, Augustin Lemesle # Gplv2 +""" +Vue d'affichage, et de modification des réglages (réglages machine, +topologie, users, service...) +""" from __future__ import unicode_literals -from django.shortcuts import render -from django.shortcuts import get_object_or_404, render, redirect -from django.template.context_processors import csrf +from django.shortcuts import render, redirect from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger -from django.template import Context, RequestContext, loader from django.contrib import messages from django.contrib.auth.decorators import login_required, permission_required -from django.db.models import Max, ProtectedError -from django.db import IntegrityError -from django.core.mail import send_mail -from django.utils import timezone -from django.core.urlresolvers import reverse +from django.db.models import ProtectedError from django.db import transaction from reversion.models import Version from reversion import revisions as reversion +from re2o.views import form from .forms import ServiceForm, DelServiceForm -from .models import Service, OptionalUser, OptionalMachine, AssoOption, MailMessageOption, GeneralOption, OptionalTopologie +from .models import Service, OptionalUser, OptionalMachine, AssoOption +from .models import MailMessageOption, GeneralOption, OptionalTopologie from . import models from . import forms -def form(ctx, template, request): - c = ctx - c.update(csrf(request)) - return render(request, template, c) - @login_required @permission_required('cableur') def display_options(request): - useroptions, created = OptionalUser.objects.get_or_create() - machineoptions, created = OptionalMachine.objects.get_or_create() - topologieoptions, created = OptionalTopologie.objects.get_or_create() - generaloptions, created = GeneralOption.objects.get_or_create() - assooptions, created = AssoOption.objects.get_or_create() - mailmessageoptions, created = MailMessageOption.objects.get_or_create() + """Vue pour affichage des options (en vrac) classé selon les models + correspondants dans un tableau""" + useroptions, _created = OptionalUser.objects.get_or_create() + machineoptions, _created = OptionalMachine.objects.get_or_create() + topologieoptions, _created = OptionalTopologie.objects.get_or_create() + generaloptions, _created = GeneralOption.objects.get_or_create() + assooptions, _created = AssoOption.objects.get_or_create() + mailmessageoptions, _created = MailMessageOption.objects.get_or_create() service_list = Service.objects.all() - return form({'useroptions': useroptions, 'machineoptions': machineoptions, 'topologieoptions': topologieoptions, 'generaloptions': generaloptions, 'assooptions' : assooptions, 'mailmessageoptions' : mailmessageoptions, 'service_list':service_list}, 'preferences/display_preferences.html', request) + return form({ + 'useroptions': useroptions, + 'machineoptions': machineoptions, + 'topologieoptions': topologieoptions, + 'generaloptions': generaloptions, + 'assooptions': assooptions, + 'mailmessageoptions': mailmessageoptions, + 'service_list': service_list + }, 'preferences/display_preferences.html', request) + @login_required @permission_required('admin') @@ -73,23 +78,36 @@ def edit_options(request, section): model = getattr(models, section, None) form_instance = getattr(forms, 'Edit' + section + 'Form', None) if model and form: - options_instance, created = model.objects.get_or_create() - options = form_instance(request.POST or None, instance=options_instance) + options_instance, _created = model.objects.get_or_create() + options = form_instance( + request.POST or None, + instance=options_instance + ) if options.is_valid(): with transaction.atomic(), reversion.create_revision(): options.save() reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in options.changed_data)) + reversion.set_comment( + "Champs modifié(s) : %s" % ', '.join( + field for field in options.changed_data + ) + ) messages.success(request, "Préférences modifiées") return redirect("/preferences/") - return form({'options': options}, 'preferences/edit_preferences.html', request) + return form( + {'options': options}, + 'preferences/edit_preferences.html', + request + ) else: messages.error(request, "Objet inconnu") return redirect("/preferences/") + @login_required @permission_required('admin') def add_services(request): + """Ajout d'un service de la page d'accueil""" services = ServiceForm(request.POST or None) if services.is_valid(): with transaction.atomic(), reversion.create_revision(): @@ -98,29 +116,45 @@ def add_services(request): reversion.set_comment("Création") messages.success(request, "Cet enregistrement ns a été ajouté") return redirect("/preferences/") - return form({'preferenceform': services}, 'preferences/preferences.html', request) + return form( + {'preferenceform': services}, + 'preferences/preferences.html', + request + ) + @login_required @permission_required('admin') def edit_services(request, servicesid): + """Edition des services affichés sur la page d'accueil""" try: services_instance = Service.objects.get(pk=servicesid) except Service.DoesNotExist: - messages.error(request, u"Entrée inexistante" ) + messages.error(request, u"Entrée inexistante") return redirect("/preferences/") services = ServiceForm(request.POST or None, instance=services_instance) if services.is_valid(): with transaction.atomic(), reversion.create_revision(): services.save() reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in services.changed_data)) + reversion.set_comment( + "Champs modifié(s) : %s" % ', '.join( + field for field in services.changed_data + ) + ) messages.success(request, "Service modifié") return redirect("/preferences/") - return form({'preferenceform': services}, 'preferences/preferences.html', request) + return form( + {'preferenceform': services}, + 'preferences/preferences.html', + request + ) + @login_required @permission_required('admin') def del_services(request): + """Suppression d'un service de la page d'accueil""" services = DelServiceForm(request.POST or None) if services.is_valid(): services_dels = services.cleaned_data['services'] @@ -131,20 +165,28 @@ def del_services(request): reversion.set_user(request.user) messages.success(request, "Le services a été supprimée") except ProtectedError: - messages.error(request, "Erreur le service suivant %s ne peut être supprimé" % services_del) + messages.error(request, "Erreur le service\ + suivant %s ne peut être supprimé" % services_del) return redirect("/preferences/") - return form({'preferenceform': services}, 'preferences/preferences.html', request) + return form( + {'preferenceform': services}, + 'preferences/preferences.html', + request + ) + @login_required @permission_required('cableur') -def history(request, object, id): - if object == 'service': +def history(request, object_name, object_id): + """Historique de creation et de modification d'un service affiché sur + la page d'accueil""" + if object_name == 'service': try: - object_instance = Service.objects.get(pk=id) + object_instance = Service.objects.get(pk=object_id) except Service.DoesNotExist: - messages.error(request, "Service inexistant") - return redirect("/preferences/") - options, created = GeneralOption.objects.get_or_create() + messages.error(request, "Service inexistant") + return redirect("/preferences/") + options, _created = GeneralOption.objects.get_or_create() pagination_number = options.pagination_number reversions = Version.objects.get_for_object(object_instance) paginator = Paginator(reversions, pagination_number) @@ -157,4 +199,7 @@ def history(request, object, id): except EmptyPage: # If page is out of range (e.g. 9999), deliver last page of results. reversions = paginator.page(paginator.num_pages) - return render(request, 're2o/history.html', {'reversions': reversions, 'object': object_instance}) + return render(request, 're2o/history.html', { + 'reversions': reversions, + 'object': object_instance + })