From 1795d26c55fee80d90405aaa93d2a124e28f95c5 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 31 Mar 2018 17:18:39 +0200 Subject: [PATCH 01/11] Gestion de l'historique avec un mixin forms et model --- cotisations/forms.py | 20 ++-- cotisations/models.py | 15 +-- cotisations/views.py | 108 ++++--------------- machines/forms.py | 61 +++++------ machines/models.py | 40 +++---- machines/views.py | 235 +++++++++--------------------------------- re2o/mixins.py | 24 +++++ re2o/settings.py | 1 + topologie/forms.py | 12 +-- topologie/models.py | 12 +-- topologie/views.py | 177 ++++++------------------------- users/forms.py | 40 ++++--- users/models.py | 16 +-- users/views.py | 157 ++++++---------------------- 14 files changed, 271 insertions(+), 647 deletions(-) diff --git a/cotisations/forms.py b/cotisations/forms.py index ba4c066f..3459f0de 100644 --- a/cotisations/forms.py +++ b/cotisations/forms.py @@ -45,9 +45,9 @@ from preferences.models import OptionalUser from users.models import User from re2o.field_permissions import FieldPermissionFormMixin +from re2o.mixins import FormRevMixin - -class NewFactureForm(ModelForm): +class NewFactureForm(FormRevMixin, ModelForm): """Creation d'une facture, moyen de paiement, banque et numero de cheque""" def __init__(self, *args, **kwargs): @@ -96,7 +96,7 @@ class CreditSoldeForm(NewFactureForm): montant = forms.DecimalField(max_digits=5, decimal_places=2, required=True) -class SelectUserArticleForm(Form): +class SelectUserArticleForm(FormRevMixin, Form): """Selection d'un article lors de la creation d'une facture""" article = forms.ModelChoiceField( queryset=Article.objects.filter(Q(type_user='All') | Q(type_user='Adherent')), @@ -158,7 +158,7 @@ class EditFactureForm(FieldPermissionFormMixin, NewFactureForm): self.fields['valid'].label = 'Validité de la facture' -class ArticleForm(ModelForm): +class ArticleForm(FormRevMixin, ModelForm): """Creation d'un article. Champs : nom, cotisation, durée""" class Meta: model = Article @@ -170,7 +170,7 @@ class ArticleForm(ModelForm): self.fields['name'].label = "Désignation de l'article" -class DelArticleForm(Form): +class DelArticleForm(FormRevMixin, Form): """Suppression d'un ou plusieurs articles en vente. Choix parmis les modèles""" articles = forms.ModelMultipleChoiceField( @@ -188,7 +188,7 @@ class DelArticleForm(Form): self.fields['articles'].queryset = Article.objects.all() -class PaiementForm(ModelForm): +class PaiementForm(FormRevMixin, ModelForm): """Creation d'un moyen de paiement, champ text moyen et type permettant d'indiquer si il s'agit d'un chèque ou non pour le form""" class Meta: @@ -202,7 +202,7 @@ class PaiementForm(ModelForm): self.fields['type_paiement'].label = 'Type de paiement à ajouter' -class DelPaiementForm(Form): +class DelPaiementForm(FormRevMixin, Form): """Suppression d'un ou plusieurs moyens de paiements, selection parmis les models""" paiements = forms.ModelMultipleChoiceField( @@ -220,7 +220,7 @@ class DelPaiementForm(Form): self.fields['paiements'].queryset = Paiement.objects.all() -class BanqueForm(ModelForm): +class BanqueForm(FormRevMixin, ModelForm): """Creation d'une banque, field name""" class Meta: model = Banque @@ -232,7 +232,7 @@ class BanqueForm(ModelForm): self.fields['name'].label = 'Banque à ajouter' -class DelBanqueForm(Form): +class DelBanqueForm(FormRevMixin, Form): """Selection d'une ou plusieurs banques, pour suppression""" banques = forms.ModelMultipleChoiceField( queryset=Banque.objects.none(), @@ -283,7 +283,7 @@ class NewFactureSoldeForm(NewFactureForm): return cleaned_data -class RechargeForm(Form): +class RechargeForm(FormRevMixin, Form): value = forms.FloatField( label='Valeur', min_value=0.01, diff --git a/cotisations/models.py b/cotisations/models.py index cbf44b79..10aa868e 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -57,9 +57,10 @@ from django.utils import timezone from machines.models import regen from re2o.field_permissions import FieldPermissionModelMixin -from re2o.mixins import AclMixin +from re2o.mixins import AclMixin, RevMixin -class Facture(AclMixin, FieldPermissionModelMixin, models.Model): + +class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): """ Définition du modèle des factures. Une facture regroupe une ou plusieurs ventes, rattachée à un user, et reliée à un moyen de paiement et si il y a lieu un numero pour les chèques. Possède les valeurs @@ -180,7 +181,7 @@ def facture_post_delete(sender, **kwargs): user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) -class Vente(AclMixin, models.Model): +class Vente(RevMixin, AclMixin, models.Model): """Objet vente, contient une quantité, une facture parente, un nom, un prix. Peut-être relié à un objet cotisation, via le boolean iscotisation""" @@ -325,7 +326,7 @@ def vente_post_delete(sender, **kwargs): user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) -class Article(AclMixin, models.Model): +class Article(RevMixin, AclMixin, models.Model): """Liste des articles en vente : prix, nom, et attribut iscotisation et duree si c'est une cotisation""" PRETTY_NAME = "Articles en vente" @@ -381,7 +382,7 @@ class Article(AclMixin, models.Model): return self.name -class Banque(AclMixin, models.Model): +class Banque(RevMixin, AclMixin, models.Model): """Liste des banques""" PRETTY_NAME = "Banques enregistrées" @@ -396,7 +397,7 @@ class Banque(AclMixin, models.Model): return self.name -class Paiement(AclMixin, models.Model): +class Paiement(RevMixin, AclMixin, models.Model): """Moyens de paiement""" PRETTY_NAME = "Moyens de paiement" PAYMENT_TYPES = ( @@ -426,7 +427,7 @@ class Paiement(AclMixin, models.Model): super(Paiement, self).save(*args, **kwargs) -class Cotisation(AclMixin, models.Model): +class Cotisation(RevMixin, AclMixin, models.Model): """Objet cotisation, debut et fin, relié en onetoone à une vente""" PRETTY_NAME = "Cotisations" diff --git a/cotisations/views.py b/cotisations/views.py index a9c996d6..974f659e 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -39,8 +39,6 @@ from django.forms import modelformset_factory, formset_factory from django.utils import timezone from django.views.decorators.csrf import csrf_exempt from django.views.decorators.debug import sensitive_variables -from reversion import revisions as reversion -from reversion.models import Version # Import des models, forms et fonctions re2o from users.models import User from re2o.settings import LOGO_PATH @@ -126,10 +124,7 @@ def new_facture(request, user, userid): 'users:profil', kwargs={'userid': userid} )) - with transaction.atomic(), reversion.create_revision(): - new_facture_instance.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + new_facture_instance.save() for art_item in articles: if art_item.cleaned_data: article = art_item.cleaned_data['article'] @@ -142,10 +137,7 @@ def new_facture(request, user, userid): duration=article.duration, number=quantity ) - with transaction.atomic(), reversion.create_revision(): - new_vente.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + new_vente.save() if any(art_item.cleaned_data['article'].type_cotisation for art_item in articles if art_item.cleaned_data): messages.success( @@ -257,13 +249,8 @@ def edit_facture(request, facture, factureid): ) vente_form = vente_form_set(request.POST or None, queryset=ventes_objects) if facture_form.is_valid() and vente_form.is_valid(): - with transaction.atomic(), reversion.create_revision(): - facture_form.save() - vente_form.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for form in vente_form for field - in facture_form.changed_data + form.changed_data)) + facture_form.save() + vente_form.save() messages.success(request, "La facture a bien été modifiée") return redirect(reverse('cotisations:index')) return form({ @@ -278,9 +265,7 @@ def del_facture(request, facture, factureid): """Suppression d'une facture. Supprime en cascade les ventes et cotisations filles""" if request.method == "POST": - with transaction.atomic(), reversion.create_revision(): - facture.delete() - reversion.set_user(request.user) + facture.delete() messages.success(request, "La facture a été détruite") return redirect(reverse('cotisations:index')) return form({ @@ -297,21 +282,15 @@ def credit_solde(request, user, userid): facture = CreditSoldeForm(request.POST or None) if facture.is_valid(): facture_instance = facture.save(commit=False) - with transaction.atomic(), reversion.create_revision(): - facture_instance.user = user - facture_instance.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + facture_instance.user = user + facture_instance.save() new_vente = Vente.objects.create( facture=facture_instance, name="solde", prix=facture.cleaned_data['montant'], number=1 ) - with transaction.atomic(), reversion.create_revision(): - new_vente.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + new_vente.save() messages.success(request, "Solde modifié") return redirect(reverse('cotisations:index')) return form({'factureform': facture, 'action_name' : 'Créditer'}, 'cotisations/facture.html', request) @@ -329,10 +308,7 @@ def add_article(request): PAS de conséquence sur les ventes déjà faites""" article = ArticleForm(request.POST or None) if article.is_valid(): - with transaction.atomic(), reversion.create_revision(): - article.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + article.save() messages.success(request, "L'article a été ajouté") return redirect(reverse('cotisations:index-article')) return form({'factureform': article, 'action_name' : 'Ajouter'}, 'cotisations/facture.html', request) @@ -345,14 +321,7 @@ def edit_article(request, article_instance, articleid): Réservé au trésorier""" article = ArticleForm(request.POST or None, instance=article_instance) if article.is_valid(): - with transaction.atomic(), reversion.create_revision(): - article.save() - reversion.set_user(request.user) - reversion.set_comment( - "Champs modifié(s) : %s" % ', '.join( - field for field in article.changed_data - ) - ) + article.save() messages.success(request, "Type d'article modifié") return redirect(reverse('cotisations:index-article')) return form({'factureform': article, 'action_name' : 'Editer'}, 'cotisations/facture.html', request) @@ -365,9 +334,7 @@ def del_article(request, instances): article = DelArticleForm(request.POST or None, instances=instances) if article.is_valid(): article_del = article.cleaned_data['articles'] - with transaction.atomic(), reversion.create_revision(): - article_del.delete() - reversion.set_user(request.user) + article_del.delete() messages.success(request, "Le/les articles ont été supprimé") return redirect(reverse('cotisations:index-article')) return form({'factureform': article, 'action_name' : 'Supprimer'}, 'cotisations/facture.html', request) @@ -380,10 +347,7 @@ def add_paiement(request): via foreign key""" paiement = PaiementForm(request.POST or None) if paiement.is_valid(): - with transaction.atomic(), reversion.create_revision(): - paiement.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + paiement.save() messages.success(request, "Le moyen de paiement a été ajouté") return redirect(reverse('cotisations:index-paiement')) return form({'factureform': paiement, 'action_name' : 'Ajouter'}, 'cotisations/facture.html', request) @@ -395,14 +359,7 @@ def edit_paiement(request, paiement_instance, paiementid): """Edition d'un moyen de paiement""" paiement = PaiementForm(request.POST or None, instance=paiement_instance) if paiement.is_valid(): - with transaction.atomic(), reversion.create_revision(): - paiement.save() - reversion.set_user(request.user) - reversion.set_comment( - "Champs modifié(s) : %s" % ', '.join( - field for field in paiement.changed_data - ) - ) + paiement.save() messages.success(request, "Type de paiement modifié") return redirect(reverse('cotisations:index-paiement')) return form({'factureform': paiement, 'action_name' : 'Editer'}, 'cotisations/facture.html', request) @@ -417,10 +374,7 @@ def del_paiement(request, instances): paiement_dels = paiement.cleaned_data['paiements'] for paiement_del in paiement_dels: try: - with transaction.atomic(), reversion.create_revision(): - paiement_del.delete() - reversion.set_user(request.user) - reversion.set_comment("Destruction") + paiement_del.delete() messages.success( request, "Le moyen de paiement a été supprimé" @@ -441,10 +395,7 @@ def add_banque(request): """Ajoute une banque à la liste des banques""" banque = BanqueForm(request.POST or None) if banque.is_valid(): - with transaction.atomic(), reversion.create_revision(): - banque.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + banque.save() messages.success(request, "La banque a été ajoutée") return redirect(reverse('cotisations:index-banque')) return form({'factureform': banque, 'action_name' : 'Ajouter'}, 'cotisations/facture.html', request) @@ -456,14 +407,7 @@ def edit_banque(request, banque_instance, banqueid): """Edite le nom d'une banque""" banque = BanqueForm(request.POST or None, instance=banque_instance) if banque.is_valid(): - with transaction.atomic(), reversion.create_revision(): - banque.save() - reversion.set_user(request.user) - reversion.set_comment( - "Champs modifié(s) : %s" % ', '.join( - field for field in banque.changed_data - ) - ) + banque.save() messages.success(request, "Banque modifiée") return redirect(reverse('cotisations:index-banque')) return form({'factureform': banque, 'action_name' : 'Editer'}, 'cotisations/facture.html', request) @@ -478,10 +422,7 @@ def del_banque(request, instances): banque_dels = banque.cleaned_data['banques'] for banque_del in banque_dels: try: - with transaction.atomic(), reversion.create_revision(): - banque_del.delete() - reversion.set_user(request.user) - reversion.set_comment("Destruction") + banque_del.delete() messages.success(request, "La banque a été supprimée") except ProtectedError: messages.error(request, "La banque %s est affectée à au moins\ @@ -519,10 +460,7 @@ def control(request): facture_list = paginator.page(paginator.num.pages) controlform = controlform_set(request.POST or None, queryset=facture_list.object_list) if controlform.is_valid(): - with transaction.atomic(), reversion.create_revision(): - controlform.save() - reversion.set_user(request.user) - reversion.set_comment("Controle trésorier") + controlform.save() return redirect(reverse('cotisations:control')) return render(request, 'cotisations/control.html', { 'facture_list': facture_list, @@ -630,10 +568,7 @@ def new_facture_solde(request, userid): 'users:profil', kwargs={'userid': userid} )) - with transaction.atomic(), reversion.create_revision(): - facture.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + facture.save() for art_item in articles: if art_item.cleaned_data: article = art_item.cleaned_data['article'] @@ -646,10 +581,7 @@ def new_facture_solde(request, userid): duration=article.duration, number=quantity ) - with transaction.atomic(), reversion.create_revision(): - new_vente.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + new_vente.save() if any(art_item.cleaned_data['article'].type_cotisation for art_item in articles if art_item.cleaned_data): messages.success( diff --git a/machines/forms.py b/machines/forms.py index 5dc9dd8b..6ece03e8 100644 --- a/machines/forms.py +++ b/machines/forms.py @@ -39,6 +39,7 @@ from django.forms import ModelForm, Form from django import forms from re2o.field_permissions import FieldPermissionFormMixin +from re2o.mixins import FormRevMixin from .models import ( Domain, @@ -61,7 +62,7 @@ from .models import ( ) -class EditMachineForm(FieldPermissionFormMixin, ModelForm): +class EditMachineForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): """Formulaire d'édition d'une machine""" class Meta: model = Machine @@ -79,7 +80,7 @@ class NewMachineForm(EditMachineForm): fields = ['name'] -class EditInterfaceForm(FieldPermissionFormMixin, ModelForm): +class EditInterfaceForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): """Edition d'une interface. Edition complète""" class Meta: model = Interface @@ -125,7 +126,7 @@ class AddInterfaceForm(EditInterfaceForm): fields = ['type', 'ipv4', 'mac_address', 'details'] -class AliasForm(ModelForm): +class AliasForm(FormRevMixin, ModelForm): """Ajout d'un alias (et edition), CNAME, contenant nom et extension""" class Meta: model = Domain @@ -142,7 +143,7 @@ class AliasForm(ModelForm): ) -class DomainForm(ModelForm): +class DomainForm(FormRevMixin, ModelForm): """Ajout et edition d'un enregistrement de nom, relié à interface""" class Meta: model = Domain @@ -158,7 +159,7 @@ class DomainForm(ModelForm): super(DomainForm, self).__init__(*args, prefix=prefix, **kwargs) -class DelAliasForm(Form): +class DelAliasForm(FormRevMixin, Form): """Suppression d'un ou plusieurs objets alias""" alias = forms.ModelMultipleChoiceField( queryset=Domain.objects.all(), @@ -174,7 +175,7 @@ class DelAliasForm(Form): ) -class MachineTypeForm(ModelForm): +class MachineTypeForm(FormRevMixin, ModelForm): """Ajout et edition d'un machinetype, relié à un iptype""" class Meta: model = MachineType @@ -187,7 +188,7 @@ class MachineTypeForm(ModelForm): self.fields['ip_type'].label = "Type d'ip relié" -class DelMachineTypeForm(Form): +class DelMachineTypeForm(FormRevMixin, Form): """Suppression d'un ou plusieurs machinetype""" machinetypes = forms.ModelMultipleChoiceField( queryset=MachineType.objects.none(), @@ -204,7 +205,7 @@ class DelMachineTypeForm(Form): self.fields['machinetypes'].queryset = MachineType.objects.all() -class IpTypeForm(ModelForm): +class IpTypeForm(FormRevMixin, ModelForm): """Formulaire d'ajout d'un iptype. Pas d'edition de l'ip de start et de stop après creation""" class Meta: @@ -226,7 +227,7 @@ class EditIpTypeForm(IpTypeForm): 'ouverture_ports'] -class DelIpTypeForm(Form): +class DelIpTypeForm(FormRevMixin, Form): """Suppression d'un ou plusieurs iptype""" iptypes = forms.ModelMultipleChoiceField( queryset=IpType.objects.none(), @@ -243,7 +244,7 @@ class DelIpTypeForm(Form): self.fields['iptypes'].queryset = IpType.objects.all() -class ExtensionForm(ModelForm): +class ExtensionForm(FormRevMixin, ModelForm): """Formulaire d'ajout et edition d'une extension""" class Meta: model = Extension @@ -258,7 +259,7 @@ class ExtensionForm(ModelForm): self.fields['soa'].label = 'En-tête SOA à utiliser' -class DelExtensionForm(Form): +class DelExtensionForm(FormRevMixin, Form): """Suppression d'une ou plusieurs extensions""" extensions = forms.ModelMultipleChoiceField( queryset=Extension.objects.none(), @@ -275,7 +276,7 @@ class DelExtensionForm(Form): self.fields['extensions'].queryset = Extension.objects.all() -class Ipv6ListForm(FieldPermissionFormMixin, ModelForm): +class Ipv6ListForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): """Gestion des ipv6 d'une machine""" class Meta: model = Ipv6List @@ -286,7 +287,7 @@ class Ipv6ListForm(FieldPermissionFormMixin, ModelForm): super(Ipv6ListForm, self).__init__(*args, prefix=prefix, **kwargs) -class SOAForm(ModelForm): +class SOAForm(FormRevMixin, ModelForm): """Ajout et edition d'un SOA""" class Meta: model = SOA @@ -297,7 +298,7 @@ class SOAForm(ModelForm): super(SOAForm, self).__init__(*args, prefix=prefix, **kwargs) -class DelSOAForm(Form): +class DelSOAForm(FormRevMixin, Form): """Suppression d'un ou plusieurs SOA""" soa = forms.ModelMultipleChoiceField( queryset=SOA.objects.none(), @@ -314,7 +315,7 @@ class DelSOAForm(Form): self.fields['soa'].queryset = SOA.objects.all() -class MxForm(ModelForm): +class MxForm(FormRevMixin, ModelForm): """Ajout et edition d'un MX""" class Meta: model = Mx @@ -327,7 +328,7 @@ class MxForm(ModelForm): interface_parent=None ).select_related('extension') -class DelMxForm(Form): +class DelMxForm(FormRevMixin, Form): """Suppression d'un ou plusieurs MX""" mx = forms.ModelMultipleChoiceField( queryset=Mx.objects.none(), @@ -344,7 +345,7 @@ class DelMxForm(Form): self.fields['mx'].queryset = Mx.objects.all() -class NsForm(ModelForm): +class NsForm(FormRevMixin, ModelForm): """Ajout d'un NS pour une zone On exclue les CNAME dans les objets domain (interdit par la rfc) donc on prend uniquemet """ @@ -360,7 +361,7 @@ class NsForm(ModelForm): ).select_related('extension') -class DelNsForm(Form): +class DelNsForm(FormRevMixin, Form): """Suppresion d'un ou plusieurs NS""" ns = forms.ModelMultipleChoiceField( queryset=Ns.objects.none(), @@ -377,7 +378,7 @@ class DelNsForm(Form): self.fields['ns'].queryset = Ns.objects.all() -class TxtForm(ModelForm): +class TxtForm(FormRevMixin, ModelForm): """Ajout d'un txt pour une zone""" class Meta: model = Txt @@ -388,7 +389,7 @@ class TxtForm(ModelForm): super(TxtForm, self).__init__(*args, prefix=prefix, **kwargs) -class DelTxtForm(Form): +class DelTxtForm(FormRevMixin, Form): """Suppression d'un ou plusieurs TXT""" txt = forms.ModelMultipleChoiceField( queryset=Txt.objects.none(), @@ -405,7 +406,7 @@ class DelTxtForm(Form): self.fields['txt'].queryset = Txt.objects.all() -class SrvForm(ModelForm): +class SrvForm(FormRevMixin, ModelForm): """Ajout d'un srv pour une zone""" class Meta: model = Srv @@ -416,7 +417,7 @@ class SrvForm(ModelForm): super(SrvForm, self).__init__(*args, prefix=prefix, **kwargs) -class DelSrvForm(Form): +class DelSrvForm(FormRevMixin, Form): """Suppression d'un ou plusieurs Srv""" srv = forms.ModelMultipleChoiceField( queryset=Srv.objects.none(), @@ -433,7 +434,7 @@ class DelSrvForm(Form): self.fields['srv'].queryset = Srv.objects.all() -class NasForm(ModelForm): +class NasForm(FormRevMixin, ModelForm): """Ajout d'un type de nas (machine d'authentification, swicths, bornes...)""" class Meta: @@ -445,7 +446,7 @@ class NasForm(ModelForm): super(NasForm, self).__init__(*args, prefix=prefix, **kwargs) -class DelNasForm(Form): +class DelNasForm(FormRevMixin, Form): """Suppression d'un ou plusieurs nas""" nas = forms.ModelMultipleChoiceField( queryset=Nas.objects.none(), @@ -462,7 +463,7 @@ class DelNasForm(Form): self.fields['nas'].queryset = Nas.objects.all() -class ServiceForm(ModelForm): +class ServiceForm(FormRevMixin, ModelForm): """Ajout et edition d'une classe de service : dns, dhcp, etc""" class Meta: model = Service @@ -482,7 +483,7 @@ class ServiceForm(ModelForm): return instance -class DelServiceForm(Form): +class DelServiceForm(FormRevMixin, Form): """Suppression d'un ou plusieurs service""" service = forms.ModelMultipleChoiceField( queryset=Service.objects.none(), @@ -499,7 +500,7 @@ class DelServiceForm(Form): self.fields['service'].queryset = Service.objects.all() -class VlanForm(ModelForm): +class VlanForm(FormRevMixin, ModelForm): """Ajout d'un vlan : id, nom""" class Meta: model = Vlan @@ -510,7 +511,7 @@ class VlanForm(ModelForm): super(VlanForm, self).__init__(*args, prefix=prefix, **kwargs) -class DelVlanForm(Form): +class DelVlanForm(FormRevMixin, Form): """Suppression d'un ou plusieurs vlans""" vlan = forms.ModelMultipleChoiceField( queryset=Vlan.objects.none(), @@ -527,7 +528,7 @@ class DelVlanForm(Form): self.fields['vlan'].queryset = Vlan.objects.all() -class EditOuverturePortConfigForm(ModelForm): +class EditOuverturePortConfigForm(FormRevMixin, ModelForm): """Edition de la liste des profils d'ouverture de ports pour l'interface""" class Meta: @@ -543,7 +544,7 @@ class EditOuverturePortConfigForm(ModelForm): ) -class EditOuverturePortListForm(ModelForm): +class EditOuverturePortListForm(FormRevMixin, ModelForm): """Edition de la liste des ports et profils d'ouverture des ports""" class Meta: diff --git a/machines/models.py b/machines/models.py index d4368b2e..c7d9fe83 100644 --- a/machines/models.py +++ b/machines/models.py @@ -39,13 +39,13 @@ from django.core.validators import MaxValueValidator from macaddress.fields import MACAddressField from re2o.field_permissions import FieldPermissionModelMixin -from re2o.mixins import AclMixin +from re2o.mixins import AclMixin, RevMixin import users.models import preferences.models -class Machine(FieldPermissionModelMixin, models.Model): +class Machine(RevMixin, FieldPermissionModelMixin, models.Model): """ Class définissant une machine, object parent user, objets fils interfaces""" PRETTY_NAME = "Machine" @@ -163,7 +163,7 @@ class Machine(FieldPermissionModelMixin, models.Model): return str(self.user) + ' - ' + str(self.id) + ' - ' + str(self.name) -class MachineType(AclMixin, models.Model): +class MachineType(RevMixin, AclMixin, models.Model): """ Type de machine, relié à un type d'ip, affecté aux interfaces""" PRETTY_NAME = "Type de machine" @@ -203,7 +203,7 @@ class MachineType(AclMixin, models.Model): return self.type -class IpType(AclMixin, models.Model): +class IpType(RevMixin, AclMixin, models.Model): """ Type d'ip, définissant un range d'ip, affecté aux machine types""" PRETTY_NAME = "Type d'ip" @@ -333,7 +333,7 @@ class IpType(AclMixin, models.Model): return self.type -class Vlan(AclMixin, models.Model): +class Vlan(RevMixin, AclMixin, models.Model): """ Un vlan : vlan_id et nom On limite le vlan id entre 0 et 4096, comme défini par la norme""" PRETTY_NAME = "Vlans" @@ -351,7 +351,7 @@ class Vlan(AclMixin, models.Model): return self.name -class Nas(AclMixin, models.Model): +class Nas(RevMixin, AclMixin, models.Model): """ Les nas. Associé à un machine_type. Permet aussi de régler le port_access_mode (802.1X ou mac-address) pour le radius. Champ autocapture de la mac à true ou false""" @@ -390,7 +390,7 @@ class Nas(AclMixin, models.Model): return self.name -class SOA(AclMixin, models.Model): +class SOA(RevMixin, AclMixin, models.Model): """ Un enregistrement SOA associé à une extension Les valeurs par défault viennent des recommandations RIPE : @@ -467,7 +467,7 @@ class SOA(AclMixin, models.Model): -class Extension(AclMixin, models.Model): +class Extension(RevMixin, AclMixin, models.Model): """ Extension dns type example.org. Précise si tout le monde peut l'utiliser, associé à un origin (ip d'origine)""" PRETTY_NAME = "Extensions dns" @@ -530,7 +530,7 @@ class Extension(AclMixin, models.Model): super(Extension, self).clean(*args, **kwargs) -class Mx(AclMixin, models.Model): +class Mx(RevMixin, AclMixin, models.Model): """ Entrées des MX. Enregistre la zone (extension) associée et la priorité Todo : pouvoir associer un MX à une interface """ @@ -555,7 +555,7 @@ class Mx(AclMixin, models.Model): return str(self.zone) + ' ' + str(self.priority) + ' ' + str(self.name) -class Ns(AclMixin, models.Model): +class Ns(RevMixin, AclMixin, models.Model): """Liste des enregistrements name servers par zone considéérée""" PRETTY_NAME = "Enregistrements NS" @@ -576,7 +576,7 @@ class Ns(AclMixin, models.Model): return str(self.zone) + ' ' + str(self.ns) -class Txt(AclMixin, models.Model): +class Txt(RevMixin, AclMixin, models.Model): """ Un enregistrement TXT associé à une extension""" PRETTY_NAME = "Enregistrement TXT" @@ -599,7 +599,7 @@ class Txt(AclMixin, models.Model): return str(self.field1).ljust(15) + " IN TXT " + str(self.field2) -class Srv(AclMixin, models.Model): +class Srv(RevMixin, AclMixin, models.Model): PRETTY_NAME = "Enregistrement Srv" TCP = 'TCP' @@ -661,7 +661,7 @@ class Srv(AclMixin, models.Model): str(self.port) + ' ' + str(self.target) + '.' -class Interface(AclMixin, FieldPermissionModelMixin,models.Model): +class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model): """ Une interface. Objet clef de l'application machine : - une address mac unique. Possibilité de la rendre unique avec le typemachine @@ -908,7 +908,7 @@ class Interface(AclMixin, FieldPermissionModelMixin,models.Model): return self.ipv4 and not self.has_private_ip() -class Ipv6List(AclMixin, FieldPermissionModelMixin, models.Model): +class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): PRETTY_NAME = 'Enregistrements Ipv6 des machines' ipv6 = models.GenericIPAddressField( @@ -1012,7 +1012,7 @@ class Ipv6List(AclMixin, FieldPermissionModelMixin, models.Model): return str(self.ipv6) -class Domain(AclMixin, models.Model): +class Domain(RevMixin, AclMixin, models.Model): """ Objet domain. Enregistrement A et CNAME en même temps : permet de stocker les alias et les nom de machines, suivant si interface_parent ou cname sont remplis""" @@ -1170,7 +1170,7 @@ class Domain(AclMixin, models.Model): return str(self.name) + str(self.extension) -class IpList(AclMixin, models.Model): +class IpList(RevMixin, AclMixin, models.Model): PRETTY_NAME = "Addresses ipv4" ipv4 = models.GenericIPAddressField(protocol='IPv4', unique=True) @@ -1202,7 +1202,7 @@ class IpList(AclMixin, models.Model): return self.ipv4 -class Service(AclMixin, models.Model): +class Service(RevMixin, AclMixin, models.Model): """ Definition d'un service (dhcp, dns, etc)""" PRETTY_NAME = "Services à générer (dhcp, dns, etc)" @@ -1256,7 +1256,7 @@ def regen(service): return -class Service_link(AclMixin, models.Model): +class Service_link(RevMixin, AclMixin, models.Model): """ Definition du lien entre serveurs et services""" PRETTY_NAME = "Relation entre service et serveur" @@ -1287,7 +1287,7 @@ class Service_link(AclMixin, models.Model): return str(self.server) + " " + str(self.service) -class OuverturePortList(AclMixin, models.Model): +class OuverturePortList(RevMixin, AclMixin, models.Model): """Liste des ports ouverts sur une interface.""" PRETTY_NAME = "Profil d'ouverture de ports" @@ -1346,7 +1346,7 @@ class OuverturePortList(AclMixin, models.Model): ) -class OuverturePort(AclMixin, models.Model): +class OuverturePort(RevMixin, AclMixin, models.Model): """ Représente un simple port ou une plage de ports. diff --git a/machines/views.py b/machines/views.py index eb7086c4..dcc43ff9 100644 --- a/machines/views.py +++ b/machines/views.py @@ -39,7 +39,6 @@ from django.contrib import messages from django.contrib.auth.decorators import login_required, permission_required from django.db.models import ProtectedError, F from django.forms import ValidationError, modelformset_factory -from django.db import transaction from django.contrib.auth import authenticate, login from django.views.decorators.csrf import csrf_exempt @@ -238,20 +237,11 @@ def new_machine(request, user, userid): domain.instance.interface_parent = new_interface if domain.is_valid(): new_domain = domain.save(commit=False) - with transaction.atomic(), reversion.create_revision(): - new_machine.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + new_machine.save() new_interface.machine = new_machine - with transaction.atomic(), reversion.create_revision(): - new_interface.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + new_interface.save() new_domain.interface_parent = new_interface - with transaction.atomic(), reversion.create_revision(): - new_domain.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + new_domain.save() messages.success(request, "La machine a été créée") return redirect(reverse( 'users:profil', @@ -287,18 +277,9 @@ def edit_interface(request, interface_instance, interfaceid): new_machine = machine_form.save(commit=False) new_interface = interface_form.save(commit=False) new_domain = domain_form.save(commit=False) - with transaction.atomic(), reversion.create_revision(): - new_machine.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in machine_form.changed_data)) - with transaction.atomic(), reversion.create_revision(): - new_interface.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in interface_form.changed_data)) - with transaction.atomic(), reversion.create_revision(): - new_domain.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in domain_form.changed_data)) + new_machine.save() + new_interface.save() + new_domain.save() messages.success(request, "La machine a été modifiée") return redirect(reverse( 'users:profil', @@ -318,9 +299,7 @@ def edit_interface(request, interface_instance, interfaceid): def del_machine(request, machine, machineid): """ Supprime une machine, interfaces en mode cascade""" if request.method == "POST": - with transaction.atomic(), reversion.create_revision(): - machine.delete() - reversion.set_user(request.user) + machine.delete() messages.success(request, "La machine a été détruite") return redirect(reverse( 'users:profil', @@ -342,15 +321,9 @@ def new_interface(request, machine, machineid): new_interface.machine = machine if domain_form.is_valid(): new_domain = domain_form.save(commit=False) - with transaction.atomic(), reversion.create_revision(): - new_interface.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + new_interface.save() new_domain.interface_parent = new_interface - with transaction.atomic(), reversion.create_revision(): - new_domain.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + new_domain.save() messages.success(request, "L'interface a été ajoutée") return redirect(reverse( 'users:profil', @@ -370,11 +343,9 @@ def del_interface(request, interface, interfaceid): """ Supprime une interface. Domain objet en mode cascade""" if request.method == "POST": machine = interface.machine - with transaction.atomic(), reversion.create_revision(): - interface.delete() - if not machine.interface_set.all(): - machine.delete() - reversion.set_user(request.user) + interface.delete() + if not machine.interface_set.all(): + machine.delete() messages.success(request, "L'interface a été détruite") return redirect(reverse( 'users:profil', @@ -390,10 +361,7 @@ def new_ipv6list(request, interface, interfaceid): ipv6list_instance = Ipv6List(interface=interface) ipv6 = Ipv6ListForm(request.POST or None, instance=ipv6list_instance, user=request.user) if ipv6.is_valid(): - with transaction.atomic(), reversion.create_revision(): - ipv6.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + ipv6.save() messages.success(request, "Ipv6 ajoutée") return redirect(reverse( 'machines:index-ipv6', @@ -407,10 +375,7 @@ def edit_ipv6list(request, ipv6list_instance, ipv6listid): """Edition d'une ipv6""" ipv6 = Ipv6ListForm(request.POST or None, instance=ipv6list_instance, user=request.user) if ipv6.is_valid(): - with transaction.atomic(), reversion.create_revision(): - ipv6.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in ipv6.changed_data)) + ipv6.save() messages.success(request, "Ipv6 modifiée") return redirect(reverse( 'machines:index-ipv6', @@ -424,9 +389,7 @@ def del_ipv6list(request, ipv6list, ipv6listid): """ Supprime une ipv6""" if request.method == "POST": interfaceid = ipv6list.interface.id - with transaction.atomic(), reversion.create_revision(): - ipv6list.delete() - reversion.set_user(request.user) + ipv6list.delete() messages.success(request, "L'ipv6 a été détruite") return redirect(reverse( 'machines:index-ipv6', @@ -441,10 +404,7 @@ def add_iptype(request): iptype = IpTypeForm(request.POST or None) if iptype.is_valid(): - with transaction.atomic(), reversion.create_revision(): - iptype.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + iptype.save() messages.success(request, "Ce type d'ip a été ajouté") return redirect(reverse('machines:index-iptype')) return form({'iptypeform': iptype, 'action_name' : 'Créer'}, 'machines/machine.html', request) @@ -456,10 +416,7 @@ def edit_iptype(request, iptype_instance, iptypeid): iptype = EditIpTypeForm(request.POST or None, instance=iptype_instance) if iptype.is_valid(): - with transaction.atomic(), reversion.create_revision(): - iptype.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in iptype.changed_data)) + iptype.save() messages.success(request, "Type d'ip modifié") return redirect(reverse('machines:index-iptype')) return form({'iptypeform': iptype, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -473,9 +430,7 @@ def del_iptype(request, instances): iptype_dels = iptype.cleaned_data['iptypes'] for iptype_del in iptype_dels: try: - with transaction.atomic(), reversion.create_revision(): - iptype_del.delete() - reversion.set_user(request.user) + iptype_del.delete() messages.success(request, "Le type d'ip a été supprimé") except ProtectedError: messages.error(request, "Le type d'ip %s est affectée à au moins une machine, vous ne pouvez pas le supprimer" % iptype_del) @@ -488,10 +443,7 @@ def add_machinetype(request): machinetype = MachineTypeForm(request.POST or None) if machinetype.is_valid(): - with transaction.atomic(), reversion.create_revision(): - machinetype.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + machinetype.save() messages.success(request, "Ce type de machine a été ajouté") return redirect(reverse('machines:index-machinetype')) return form({'machinetypeform': machinetype, 'action_name' : 'Créer'}, 'machines/machine.html', request) @@ -502,10 +454,7 @@ def edit_machinetype(request, machinetype_instance, machinetypeid): machinetype = MachineTypeForm(request.POST or None, instance=machinetype_instance) if machinetype.is_valid(): - with transaction.atomic(), reversion.create_revision(): - machinetype.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in machinetype.changed_data)) + machinetype.save() messages.success(request, "Type de machine modifié") return redirect(reverse('machines:index-machinetype')) return form({'machinetypeform': machinetype, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -518,9 +467,7 @@ def del_machinetype(request, instances): machinetype_dels = machinetype.cleaned_data['machinetypes'] for machinetype_del in machinetype_dels: try: - with transaction.atomic(), reversion.create_revision(): - machinetype_del.delete() - reversion.set_user(request.user) + machinetype_del.delete() messages.success(request, "Le type de machine a été supprimé") except ProtectedError: messages.error(request, "Le type de machine %s est affectée à au moins une machine, vous ne pouvez pas le supprimer" % machinetype_del) @@ -533,10 +480,7 @@ def add_extension(request): extension = ExtensionForm(request.POST or None) if extension.is_valid(): - with transaction.atomic(), reversion.create_revision(): - extension.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + extension.save() messages.success(request, "Cette extension a été ajoutée") return redirect(reverse('machines:index-extension')) return form({'extensionform': extension, 'action_name' : 'Créer'}, 'machines/machine.html', request) @@ -547,10 +491,7 @@ def edit_extension(request, extension_instance, extensionid): extension = ExtensionForm(request.POST or None, instance=extension_instance) if extension.is_valid(): - with transaction.atomic(), reversion.create_revision(): - extension.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in extension.changed_data)) + extension.save() messages.success(request, "Extension modifiée") return redirect(reverse('machines:index-extension')) return form({'extensionform': extension, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -563,9 +504,7 @@ def del_extension(request, instances): extension_dels = extension.cleaned_data['extensions'] for extension_del in extension_dels: try: - with transaction.atomic(), reversion.create_revision(): - extension_del.delete() - reversion.set_user(request.user) + extension_del.delete() messages.success(request, "L'extension a été supprimée") except ProtectedError: messages.error(request, "L'extension %s est affectée à au moins un type de machine, vous ne pouvez pas la supprimer" % extension_del) @@ -578,10 +517,7 @@ def add_soa(request): soa = SOAForm(request.POST or None) if soa.is_valid(): - with transaction.atomic(), reversion.create_revision(): - soa.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + soa.save() messages.success(request, "Cet enregistrement SOA a été ajouté") return redirect(reverse('machines:index-extension')) return form({'soaform': soa, 'action_name' : 'Créer'}, 'machines/machine.html', request) @@ -592,10 +528,7 @@ def edit_soa(request, soa_instance, soaid): soa = SOAForm(request.POST or None, instance=soa_instance) if soa.is_valid(): - with transaction.atomic(), reversion.create_revision(): - soa.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in soa.changed_data)) + soa.save() messages.success(request, "SOA modifié") return redirect(reverse('machines:index-extension')) return form({'soaform': soa, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -608,9 +541,7 @@ def del_soa(request, instances): soa_dels = soa.cleaned_data['soa'] for soa_del in soa_dels: try: - with transaction.atomic(), reversion.create_revision(): - soa_del.delete() - reversion.set_user(request.user) + soa_del.delete() messages.success(request, "Le SOA a été supprimée") except ProtectedError: messages.error(request, "Erreur le SOA suivant %s ne peut être supprimé" % soa_del) @@ -623,10 +554,7 @@ def add_mx(request): mx = MxForm(request.POST or None) if mx.is_valid(): - with transaction.atomic(), reversion.create_revision(): - mx.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + mx.save() messages.success(request, "Cet enregistrement mx a été ajouté") return redirect(reverse('machines:index-extension')) return form({'mxform': mx, 'action_name' : 'Créer'}, 'machines/machine.html', request) @@ -637,10 +565,7 @@ def edit_mx(request, mx_instance, mxid): mx = MxForm(request.POST or None, instance=mx_instance) if mx.is_valid(): - with transaction.atomic(), reversion.create_revision(): - mx.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in mx.changed_data)) + mx.save() messages.success(request, "Mx modifié") return redirect(reverse('machines:index-extension')) return form({'mxform': mx, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -653,9 +578,7 @@ def del_mx(request, instances): mx_dels = mx.cleaned_data['mx'] for mx_del in mx_dels: try: - with transaction.atomic(), reversion.create_revision(): - mx_del.delete() - reversion.set_user(request.user) + mx_del.delete() messages.success(request, "L'mx a été supprimée") except ProtectedError: messages.error(request, "Erreur le Mx suivant %s ne peut être supprimé" % mx_del) @@ -668,10 +591,7 @@ def add_ns(request): ns = NsForm(request.POST or None) if ns.is_valid(): - with transaction.atomic(), reversion.create_revision(): - ns.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + ns.save() messages.success(request, "Cet enregistrement ns a été ajouté") return redirect(reverse('machines:index-extension')) return form({'nsform': ns, 'action_name' : 'Créer'}, 'machines/machine.html', request) @@ -682,10 +602,7 @@ def edit_ns(request, ns_instance, nsid): ns = NsForm(request.POST or None, instance=ns_instance) if ns.is_valid(): - with transaction.atomic(), reversion.create_revision(): - ns.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in ns.changed_data)) + ns.save() messages.success(request, "Ns modifié") return redirect(reverse('machines:index-extension')) return form({'nsform': ns, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -698,9 +615,7 @@ def del_ns(request, instances): ns_dels = ns.cleaned_data['ns'] for ns_del in ns_dels: try: - with transaction.atomic(), reversion.create_revision(): - ns_del.delete() - reversion.set_user(request.user) + ns_del.delete() messages.success(request, "Le ns a été supprimée") except ProtectedError: messages.error(request, "Erreur le Ns suivant %s ne peut être supprimé" % ns_del) @@ -713,10 +628,7 @@ def add_txt(request): txt = TxtForm(request.POST or None) if txt.is_valid(): - with transaction.atomic(), reversion.create_revision(): - txt.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + txt.save() messages.success(request, "Cet enregistrement text a été ajouté") return redirect(reverse('machines:index-extension')) return form({'txtform': txt, 'action_name' : 'Créer'}, 'machines/machine.html', request) @@ -727,10 +639,7 @@ def edit_txt(request, txt_instance, txtid): txt = TxtForm(request.POST or None, instance=txt_instance) if txt.is_valid(): - with transaction.atomic(), reversion.create_revision(): - txt.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in txt.changed_data)) + txt.save() messages.success(request, "Txt modifié") return redirect(reverse('machines:index-extension')) return form({'txtform': txt, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -743,9 +652,7 @@ def del_txt(request, instances): txt_dels = txt.cleaned_data['txt'] for txt_del in txt_dels: try: - with transaction.atomic(), reversion.create_revision(): - txt_del.delete() - reversion.set_user(request.user) + txt_del.delete() messages.success(request, "Le txt a été supprimé") except ProtectedError: messages.error(request, "Erreur le Txt suivant %s ne peut être supprimé" % txt_del) @@ -758,10 +665,7 @@ def add_srv(request): srv = SrvForm(request.POST or None) if srv.is_valid(): - with transaction.atomic(), reversion.create_revision(): - srv.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + srv.save() messages.success(request, "Cet enregistrement srv a été ajouté") return redirect(reverse('machines:index-extension')) return form({'srvform': srv, 'action_name' : 'Créer'}, 'machines/machine.html', request) @@ -772,10 +676,7 @@ def edit_srv(request, srv_instance, srvid): srv = SrvForm(request.POST or None, instance=srv_instance) if srv.is_valid(): - with transaction.atomic(), reversion.create_revision(): - srv.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in srv.changed_data)) + srv.save() messages.success(request, "Srv modifié") return redirect(reverse('machines:index-extension')) return form({'srvform': srv, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -788,9 +689,7 @@ def del_srv(request, instances): srv_dels = srv.cleaned_data['srv'] for srv_del in srv_dels: try: - with transaction.atomic(), reversion.create_revision(): - srv_del.delete() - reversion.set_user(request.user) + srv_del.delete() messages.success(request, "L'srv a été supprimée") except ProtectedError: messages.error(request, "Erreur le Srv suivant %s ne peut être supprimé" % srv_del) @@ -806,10 +705,7 @@ def add_alias(request, interface, interfaceid): if alias.is_valid(): alias = alias.save(commit=False) alias.cname = interface.domain - with transaction.atomic(), reversion.create_revision(): - alias.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + alias.save() messages.success(request, "Cet alias a été ajouté") return redirect(reverse( 'machines:index-alias', @@ -823,10 +719,7 @@ def edit_alias(request, domain_instance, domainid): alias = AliasForm(request.POST or None, instance=domain_instance, user=request.user) if alias.is_valid(): - with transaction.atomic(), reversion.create_revision(): - domain_instance = alias.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in alias.changed_data)) + domain_instance = alias.save() messages.success(request, "Alias modifié") return redirect(reverse( 'machines:index-alias', @@ -842,9 +735,7 @@ def del_alias(request, interface, interfaceid): alias_dels = alias.cleaned_data['alias'] for alias_del in alias_dels: try: - with transaction.atomic(), reversion.create_revision(): - alias_del.delete() - reversion.set_user(request.user) + alias_del.delete() messages.success(request, "L'alias %s a été supprimé" % alias_del) except ProtectedError: messages.error(request, "Erreur l'alias suivant %s ne peut être supprimé" % alias_del) @@ -861,10 +752,7 @@ def add_service(request): service = ServiceForm(request.POST or None) if service.is_valid(): - with transaction.atomic(), reversion.create_revision(): - service.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + service.save() messages.success(request, "Cet enregistrement service a été ajouté") return redirect(reverse('machines:index-service')) return form({'serviceform': service, 'action_name' : 'Créer'}, 'machines/machine.html', request) @@ -875,10 +763,7 @@ def edit_service(request, service_instance, serviceid): service = ServiceForm(request.POST or None, instance=service_instance) if service.is_valid(): - with transaction.atomic(), reversion.create_revision(): - service.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in service.changed_data)) + service.save() messages.success(request, "Service modifié") return redirect(reverse('machines:index-service')) return form({'serviceform': service, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -891,9 +776,7 @@ def del_service(request, instances): service_dels = service.cleaned_data['service'] for service_del in service_dels: try: - with transaction.atomic(), reversion.create_revision(): - service_del.delete() - reversion.set_user(request.user) + service_del.delete() messages.success(request, "Le service a été supprimée") except ProtectedError: messages.error(request, "Erreur le service suivant %s ne peut être supprimé" % service_del) @@ -906,10 +789,7 @@ def add_vlan(request): vlan = VlanForm(request.POST or None) if vlan.is_valid(): - with transaction.atomic(), reversion.create_revision(): - vlan.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + vlan.save() messages.success(request, "Cet enregistrement vlan a été ajouté") return redirect(reverse('machines:index-vlan')) return form({'vlanform': vlan, 'action_name' : 'Créer'}, 'machines/machine.html', request) @@ -920,10 +800,7 @@ def edit_vlan(request, vlan_instance, vlanid): vlan = VlanForm(request.POST or None, instance=vlan_instance) if vlan.is_valid(): - with transaction.atomic(), reversion.create_revision(): - vlan.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in vlan.changed_data)) + vlan.save() messages.success(request, "Vlan modifié") return redirect(reverse('machines:index-vlan')) return form({'vlanform': vlan, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -936,9 +813,7 @@ def del_vlan(request, instances): vlan_dels = vlan.cleaned_data['vlan'] for vlan_del in vlan_dels: try: - with transaction.atomic(), reversion.create_revision(): - vlan_del.delete() - reversion.set_user(request.user) + vlan_del.delete() messages.success(request, "Le vlan a été supprimée") except ProtectedError: messages.error(request, "Erreur le Vlan suivant %s ne peut être supprimé" % vlan_del) @@ -951,10 +826,7 @@ def add_nas(request): nas = NasForm(request.POST or None) if nas.is_valid(): - with transaction.atomic(), reversion.create_revision(): - nas.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + nas.save() messages.success(request, "Cet enregistrement nas a été ajouté") return redirect(reverse('machines:index-nas')) return form({'nasform': nas, 'action_name' : 'Créer'}, 'machines/machine.html', request) @@ -965,10 +837,7 @@ def edit_nas(request, nas_instance, nasid): nas = NasForm(request.POST or None, instance=nas_instance) if nas.is_valid(): - with transaction.atomic(), reversion.create_revision(): - nas.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in nas.changed_data)) + nas.save() messages.success(request, "Nas modifié") return redirect(reverse('machines:index-nas')) return form({'nasform': nas, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -981,9 +850,7 @@ def del_nas(request, instances): nas_dels = nas.cleaned_data['nas'] for nas_del in nas_dels: try: - with transaction.atomic(), reversion.create_revision(): - nas_del.delete() - reversion.set_user(request.user) + nas_del.delete() messages.success(request, "Le nas a été supprimé") except ProtectedError: messages.error(request, "Erreur le Nas suivant %s ne peut être supprimé" % nas_del) diff --git a/re2o/mixins.py b/re2o/mixins.py index 09c197d5..1432d7d9 100644 --- a/re2o/mixins.py +++ b/re2o/mixins.py @@ -4,6 +4,7 @@ # quelques clics. # # Copyright © 2018 Gabriel Détraz +# Copyright © 2017 Charlie Jacomme # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -19,6 +20,29 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +from reversion import revisions as reversion + + +class RevMixin(object): + def save(self, *args, **kwargs): + if self.pk is None: + reversion.set_comment("Création") + return super(RevMixin, self).save(*args, **kwargs) + + def delete(self, *args, **kwargs): + reversion.set_comment("Suppresion") + return super(RevMixin, self).delete(*args, **kwargs) + + +class FormRevMixin(object): + def save(self, *args, **kwargs): + if reversion.get_comment() != "" and self.changed_data != []: + reversion.set_comment(reversion.get_comment() + ",%s" % ', '.join(field for field in self.changed_data)) + elif self.changed_data != None: + reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in self.changed_data)) + return super(FormRevMixin, self).save(*args, **kwargs) + + class AclMixin(object): """This mixin is used in nearly every class/models defined in re2o apps. It is used by acl, in models (decorators can_...) and in templates tags diff --git a/re2o/settings.py b/re2o/settings.py index d1c27192..c101460b 100644 --- a/re2o/settings.py +++ b/re2o/settings.py @@ -89,6 +89,7 @@ MIDDLEWARE_CLASSES = ( 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', + 'reversion.middleware.RevisionMiddleware', ) ROOT_URLCONF = 're2o.urls' diff --git a/topologie/forms.py b/topologie/forms.py index 7b8f0955..f6bfaf26 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -50,9 +50,9 @@ from .models import ( ConstructorSwitch, AccessPoint ) +from re2o.mixins import FormRevMixin - -class PortForm(ModelForm): +class PortForm(FormRevMixin, ModelForm): """Formulaire pour la création d'un port d'un switch Relié directement au modèle port""" class Meta: @@ -149,7 +149,7 @@ class NewSwitchForm(NewMachineForm): fields = ['name', 'location', 'number', 'stack', 'stack_member_id'] -class EditRoomForm(ModelForm): +class EditRoomForm(FormRevMixin, ModelForm): """Permet d'éediter le nom et commentaire d'une prise murale""" class Meta: model = Room @@ -160,13 +160,13 @@ class EditRoomForm(ModelForm): super(EditRoomForm, self).__init__(*args, prefix=prefix, **kwargs) -class CreatePortsForm(forms.Form): +class CreatePortsForm(FormRevMixin, forms.Form): """Permet de créer une liste de ports pour un switch.""" begin = forms.IntegerField(label="Début :", min_value=0) end = forms.IntegerField(label="Fin :", min_value=0) -class EditModelSwitchForm(ModelForm): +class EditModelSwitchForm(FormRevMixin, ModelForm): """Permet d'éediter un modèle de switch : nom et constructeur""" class Meta: model = ModelSwitch @@ -177,7 +177,7 @@ class EditModelSwitchForm(ModelForm): super(EditModelSwitchForm, self).__init__(*args, prefix=prefix, **kwargs) -class EditConstructorSwitchForm(ModelForm): +class EditConstructorSwitchForm(FormRevMixin, ModelForm): """Permet d'éediter le nom d'un constructeur""" class Meta: model = ConstructorSwitch diff --git a/topologie/models.py b/topologie/models.py index 9cef5c44..7e08917c 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -48,9 +48,9 @@ from django.db import transaction from reversion import revisions as reversion from machines.models import Machine, Interface, regen -from re2o.mixins import AclMixin +from re2o.mixins import AclMixin, RevMixin -class Stack(AclMixin, models.Model): +class Stack(AclMixin, RevMixin, models.Model): """Un objet stack. Regrouppe des switchs en foreign key ,contient une id de stack, un switch id min et max dans le stack""" @@ -187,7 +187,7 @@ class Switch(AclMixin, Machine): return str(self.interface_set.first()) -class ModelSwitch(AclMixin, models.Model): +class ModelSwitch(AclMixin, RevMixin, models.Model): """Un modèle (au sens constructeur) de switch""" PRETTY_NAME = "Modèle de switch" reference = models.CharField(max_length=255) @@ -205,7 +205,7 @@ class ModelSwitch(AclMixin, models.Model): return str(self.constructor) + ' ' + self.reference -class ConstructorSwitch(AclMixin, models.Model): +class ConstructorSwitch(AclMixin, RevMixin, models.Model): """Un constructeur de switch""" PRETTY_NAME = "Constructeur de switch" name = models.CharField(max_length=255) @@ -219,7 +219,7 @@ class ConstructorSwitch(AclMixin, models.Model): return self.name -class Port(AclMixin, models.Model): +class Port(AclMixin, RevMixin, models.Model): """ Definition d'un port. Relié à un switch(foreign_key), un port peut etre relié de manière exclusive à : - une chambre (room) @@ -335,7 +335,7 @@ class Port(AclMixin, models.Model): return str(self.switch) + " - " + str(self.port) -class Room(AclMixin, models.Model): +class Room(AclMixin, RevMixin, models.Model): """Une chambre/local contenant une prise murale""" PRETTY_NAME = "Chambre/ Prise murale" diff --git a/topologie/views.py b/topologie/views.py index 97b01ebb..f942f377 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -44,8 +44,6 @@ from django.db import transaction from django.db.models import ProtectedError, Prefetch from django.core.exceptions import ValidationError from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger -from reversion import revisions as reversion -from reversion.models import Version from topologie.models import ( Switch, @@ -262,10 +260,7 @@ def new_port(request, switchid): port = port.save(commit=False) port.switch = switch try: - with transaction.atomic(), reversion.create_revision(): - port.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + port.save() messages.success(request, "Port ajouté") except IntegrityError: messages.error(request, "Ce port existe déjà") @@ -284,12 +279,7 @@ def edit_port(request, port_object, portid): port = EditPortForm(request.POST or None, instance=port_object) if port.is_valid(): - with transaction.atomic(), reversion.create_revision(): - port.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for field in port.changed_data - )) + port.save() messages.success(request, "Le port a bien été modifié") return redirect(reverse( 'topologie:index-port', @@ -304,11 +294,8 @@ def del_port(request, port, portid): """ Supprime le port""" if request.method == "POST": try: - with transaction.atomic(), reversion.create_revision(): - port.delete() - reversion.set_user(request.user) - reversion.set_comment("Destruction") - messages.success(request, "Le port a été détruit") + port.delete() + messages.success(request, "Le port a été détruit") except ProtectedError: messages.error(request, "Le port %s est affecté à un autre objet,\ impossible de le supprimer" % port) @@ -325,10 +312,7 @@ def new_stack(request): """Ajoute un nouveau stack : stackid_min, max, et nombre de switches""" stack = StackForm(request.POST or None) if stack.is_valid(): - with transaction.atomic(), reversion.create_revision(): - stack.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + stack.save() messages.success(request, "Stack crée") return form({'topoform': stack, 'action_name' : 'Créer'}, 'topologie/topo.html', request) @@ -340,15 +324,8 @@ def edit_stack(request, stack, stackid): stack = StackForm(request.POST or None, instance=stack) if stack.is_valid(): - with transaction.atomic(), reversion.create_revision(): - stack.save() - reversion.set_user(request.user) - reversion.set_comment( - "Champs modifié(s) : %s" % ', '.join( - field for field in stack.changed_data - ) - ) - return redirect(reverse('topologie:index-stack')) + stack.save() + return redirect(reverse('topologie:index-stack')) return form({'topoform': stack, 'action_name' : 'Editer'}, 'topologie/topo.html', request) @@ -358,11 +335,8 @@ def del_stack(request, stack, stackid): """Supprime un stack""" if request.method == "POST": try: - with transaction.atomic(), reversion.create_revision(): - stack.delete() - reversion.set_user(request.user) - reversion.set_comment("Destruction") - messages.success(request, "La stack a eté détruite") + stack.delete() + messages.success(request, "La stack a eté détruite") except ProtectedError: messages.error(request, "La stack %s est affectée à un autre\ objet, impossible de la supprimer" % stack) @@ -412,20 +386,11 @@ def new_switch(request): domain.instance.interface_parent = new_interface_instance if domain.is_valid(): new_domain_instance = domain.save(commit=False) - with transaction.atomic(), reversion.create_revision(): - new_switch.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + new_switch.save() new_interface_instance.machine = new_switch - with transaction.atomic(), reversion.create_revision(): - new_interface_instance.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + new_interface_instance.save() new_domain_instance.interface_parent = new_interface_instance - with transaction.atomic(), reversion.create_revision(): - new_domain_instance.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + new_domain_instance.save() messages.success(request, "Le switch a été créé") return redirect(reverse('topologie:index')) i_mbf_param = generate_ipv4_mbf_param(interface, False) @@ -500,26 +465,9 @@ def edit_switch(request, switch, switchid): new_switch = switch_form.save(commit=False) new_interface_instance = interface_form.save(commit=False) new_domain = domain_form.save(commit=False) - with transaction.atomic(), reversion.create_revision(): - new_switch.save() - reversion.set_user(request.user) - reversion.set_comment( - "Champs modifié(s) : %s" % ', '.join( - field for field in switch_form.changed_data - ) - ) - with transaction.atomic(), reversion.create_revision(): - new_interface_instance.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for field in interface_form.changed_data) - ) - with transaction.atomic(), reversion.create_revision(): - new_domain.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for field in domain_form.changed_data) - ) + new_switch.save() + new_interface_instance.save() + new_domain.save() messages.success(request, "Le switch a bien été modifié") return redirect(reverse('topologie:index')) i_mbf_param = generate_ipv4_mbf_param(interface_form, False ) @@ -562,20 +510,11 @@ def new_ap(request): domain.instance.interface_parent = new_interface if domain.is_valid(): new_domain_instance = domain.save(commit=False) - with transaction.atomic(), reversion.create_revision(): - new_ap.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + new_ap.save() new_interface.machine = new_ap - with transaction.atomic(), reversion.create_revision(): - new_interface.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + new_interface.save() new_domain_instance.interface_parent = new_interface - with transaction.atomic(), reversion.create_revision(): - new_domain_instance.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + new_domain_instance.save() messages.success(request, "La borne a été créé") return redirect(reverse('topologie:index-ap')) i_mbf_param = generate_ipv4_mbf_param(interface, False) @@ -616,26 +555,9 @@ def edit_ap(request, ap, accesspointid): new_ap = ap_form.save(commit=False) new_interface = interface_form.save(commit=False) new_domain = domain_form.save(commit=False) - with transaction.atomic(), reversion.create_revision(): - new_ap.save() - reversion.set_user(request.user) - reversion.set_comment( - "Champs modifié(s) : %s" % ', '.join( - field for field in ap_form.changed_data) - ) - with transaction.atomic(), reversion.create_revision(): - new_interface.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for field in interface_form.changed_data) - ) - reversion.set_comment("Création") - with transaction.atomic(), reversion.create_revision(): - new_domain.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for field in domain_form.changed_data) - ) + new_ap.save() + new_interface.save() + new_domain.save() messages.success(request, "La borne a été modifiée") return redirect(reverse('topologie:index-ap')) i_mbf_param = generate_ipv4_mbf_param(interface_form, False ) @@ -654,10 +576,7 @@ def new_room(request): """Nouvelle chambre """ room = EditRoomForm(request.POST or None) if room.is_valid(): - with transaction.atomic(), reversion.create_revision(): - room.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + room.save() messages.success(request, "La chambre a été créé") return redirect(reverse('topologie:index-room')) return form({'topoform': room, 'action_name' : 'Ajouter'}, 'topologie/topo.html', request) @@ -670,12 +589,7 @@ def edit_room(request, room, roomid): room = EditRoomForm(request.POST or None, instance=room) if room.is_valid(): - with transaction.atomic(), reversion.create_revision(): - room.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for field in room.changed_data) - ) + room.save() messages.success(request, "La chambre a bien été modifiée") return redirect(reverse('topologie:index-room')) return form({'topoform': room, 'action_name' : 'Editer'}, 'topologie/topo.html', request) @@ -687,11 +601,8 @@ def del_room(request, room, roomid): """ Suppression d'un chambre""" if request.method == "POST": try: - with transaction.atomic(), reversion.create_revision(): - room.delete() - reversion.set_user(request.user) - reversion.set_comment("Destruction") - messages.success(request, "La chambre/prise a été détruite") + room.delete() + messages.success(request, "La chambre/prise a été détruite") except ProtectedError: messages.error(request, "La chambre %s est affectée à un autre objet,\ impossible de la supprimer (switch ou user)" % room) @@ -708,10 +619,7 @@ def new_model_switch(request): """Nouveau modèle de switch""" model_switch = EditModelSwitchForm(request.POST or None) if model_switch.is_valid(): - with transaction.atomic(), reversion.create_revision(): - model_switch.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + model_switch.save() messages.success(request, "Le modèle a été créé") return redirect(reverse('topologie:index-model-switch')) return form({'topoform': model_switch, 'action_name' : 'Ajouter'}, 'topologie/topo.html', request) @@ -724,12 +632,7 @@ def edit_model_switch(request, model_switch, modelswitchid): model_switch = EditModelSwitchForm(request.POST or None, instance=model_switch) if model_switch.is_valid(): - with transaction.atomic(), reversion.create_revision(): - model_switch.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for field in model_switch.changed_data) - ) + model_switch.save() messages.success(request, "Le modèle a bien été modifié") return redirect(reverse('topologie:index-model-switch')) return form({'topoform': model_switch, 'action_name' : 'Editer'}, 'topologie/topo.html', request) @@ -741,11 +644,8 @@ def del_model_switch(request, model_switch, modelswitchid): """ Suppression d'un modèle de switch""" if request.method == "POST": try: - with transaction.atomic(), reversion.create_revision(): - model_switch.delete() - reversion.set_user(request.user) - reversion.set_comment("Destruction") - messages.success(request, "Le modèle a été détruit") + model_switch.delete() + messages.success(request, "Le modèle a été détruit") except ProtectedError: messages.error(request, "Le modèle %s est affectée à un autre objet,\ impossible de la supprimer (switch ou user)" % model_switch) @@ -762,10 +662,7 @@ def new_constructor_switch(request): """Nouveau constructeur de switch""" constructor_switch = EditConstructorSwitchForm(request.POST or None) if constructor_switch.is_valid(): - with transaction.atomic(), reversion.create_revision(): - constructor_switch.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + constructor_switch.save() messages.success(request, "Le constructeur a été créé") return redirect(reverse('topologie:index-model-switch')) return form({'topoform': constructor_switch, 'action_name' : 'Ajouter'}, 'topologie/topo.html', request) @@ -778,12 +675,7 @@ def edit_constructor_switch(request, constructor_switch, constructorswitchid): constructor_switch = EditConstructorSwitchForm(request.POST or None, instance=constructor_switch) if constructor_switch.is_valid(): - with transaction.atomic(), reversion.create_revision(): - constructor_switch.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for field in constructor_switch.changed_data) - ) + constructor_switch.save() messages.success(request, "Le modèle a bien été modifié") return redirect(reverse('topologie:index-model-switch')) return form({'topoform': constructor_switch, 'action_name' : 'Editer'}, 'topologie/topo.html', request) @@ -795,11 +687,8 @@ def del_constructor_switch(request, constructor_switch, constructorswitchid): """ Suppression d'un constructeur de switch""" if request.method == "POST": try: - with transaction.atomic(), reversion.create_revision(): - constructor_switch.delete() - reversion.set_user(request.user) - reversion.set_comment("Destruction") - messages.success(request, "Le constructeur a été détruit") + constructor_switch.delete() + messages.success(request, "Le constructeur a été détruit") except ProtectedError: messages.error(request, "Le constructeur %s est affecté à un autre objet,\ impossible de la supprimer (switch ou user)" % constructor_switch) diff --git a/users/forms.py b/users/forms.py index 81f1e343..3617b26f 100644 --- a/users/forms.py +++ b/users/forms.py @@ -53,13 +53,11 @@ from .models import ( Club ) from re2o.utils import remove_user_room - +from re2o.mixins import FormRevMixin from re2o.field_permissions import FieldPermissionFormMixin -NOW = timezone.now() - -class PassForm(FieldPermissionFormMixin, forms.ModelForm): +class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm): """Formulaire de changement de mot de passe. Verifie que les 2 nouveaux mots de passe renseignés sont identiques et respectent une norme""" @@ -107,7 +105,7 @@ class PassForm(FieldPermissionFormMixin, forms.ModelForm): user.save() -class UserCreationForm(forms.ModelForm): +class UserCreationForm(FormRevMixin, forms.ModelForm): """A form for creating new users. Includes all the required fields, plus a repeated password. @@ -154,7 +152,7 @@ class UserCreationForm(forms.ModelForm): return user -class ServiceUserCreationForm(forms.ModelForm): +class ServiceUserCreationForm(FormRevMixin, forms.ModelForm): """A form for creating new users. Includes all the required fields, plus a repeated password. @@ -202,7 +200,7 @@ class ServiceUserCreationForm(forms.ModelForm): return user -class UserChangeForm(forms.ModelForm): +class UserChangeForm(FormRevMixin, forms.ModelForm): """A form for updating users. Includes all the fields on the user, but replaces the password field with admin's password hash display field. @@ -238,7 +236,7 @@ class UserChangeForm(forms.ModelForm): return user -class ServiceUserChangeForm(forms.ModelForm): +class ServiceUserChangeForm(FormRevMixin, forms.ModelForm): """A form for updating users. Includes all the fields on the user, but replaces the password field with admin's password hash display field. @@ -281,12 +279,12 @@ class MassArchiveForm(forms.Form): cleaned_data = super(MassArchiveForm, self).clean() date = cleaned_data.get("date") if date: - if date > NOW: + if date > timezone.now(): raise forms.ValidationError("Impossible d'archiver des\ utilisateurs dont la fin d'accès se situe dans le futur !") -class AdherentForm(FieldPermissionFormMixin, ModelForm): +class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): """Formulaire de base d'edition d'un user. Formulaire de base, utilisé pour l'edition de self par self ou un cableur. On formate les champs avec des label plus jolis""" @@ -339,7 +337,7 @@ class AdherentForm(FieldPermissionFormMixin, ModelForm): return -class ClubForm(FieldPermissionFormMixin, ModelForm): +class ClubForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): """Formulaire de base d'edition d'un user. Formulaire de base, utilisé pour l'edition de self par self ou un cableur. On formate les champs avec des label plus jolis""" @@ -379,7 +377,7 @@ class ClubForm(FieldPermissionFormMixin, ModelForm): return telephone -class ClubAdminandMembersForm(ModelForm): +class ClubAdminandMembersForm(FormRevMixin, ModelForm): """Permet d'éditer la liste des membres et des administrateurs d'un club""" class Meta: @@ -391,7 +389,7 @@ class ClubAdminandMembersForm(ModelForm): super(ClubAdminandMembersForm, self).__init__(*args, prefix=prefix, **kwargs) -class PasswordForm(ModelForm): +class PasswordForm(FormRevMixin, ModelForm): """ Formulaire de changement brut de mot de passe. Ne pas utiliser sans traitement""" class Meta: @@ -403,7 +401,7 @@ class PasswordForm(ModelForm): super(PasswordForm, self).__init__(*args, prefix=prefix, **kwargs) -class ServiceUserForm(ModelForm): +class ServiceUserForm(FormRevMixin, ModelForm): """ Modification d'un service user""" password = forms.CharField( label=u'Nouveau mot de passe', @@ -429,7 +427,7 @@ class EditServiceUserForm(ServiceUserForm): fields = ['access_group', 'comment'] -class StateForm(ModelForm): +class StateForm(FormRevMixin, ModelForm): """ Changement de l'état d'un user""" class Meta: model = User @@ -440,7 +438,7 @@ class StateForm(ModelForm): super(StateForm, self).__init__(*args, prefix=prefix, **kwargs) -class GroupForm(ModelForm): +class GroupForm(FormRevMixin, ModelForm): """ Gestion des groupes d'un user""" groups = forms.ModelMultipleChoiceField( Group.objects.all(), @@ -457,7 +455,7 @@ class GroupForm(ModelForm): super(GroupForm, self).__init__(*args, prefix=prefix, **kwargs) -class SchoolForm(ModelForm): +class SchoolForm(FormRevMixin, ModelForm): """Edition, creation d'un école""" class Meta: model = School @@ -469,7 +467,7 @@ class SchoolForm(ModelForm): self.fields['name'].label = 'Établissement' -class ShellForm(ModelForm): +class ShellForm(FormRevMixin, ModelForm): """Edition, creation d'un école""" class Meta: model = ListShell @@ -481,7 +479,7 @@ class ShellForm(ModelForm): self.fields['shell'].label = 'Nom du shell' -class ListRightForm(ModelForm): +class ListRightForm(FormRevMixin, ModelForm): """Edition, d'un groupe , équivalent à un droit Ne peremet pas d'editer le gid, car il sert de primary key""" permissions = forms.ModelMultipleChoiceField( @@ -545,7 +543,7 @@ class DelSchoolForm(Form): self.fields['schools'].queryset = School.objects.all() -class BanForm(ModelForm): +class BanForm(FormRevMixin, ModelForm): """Creation, edition d'un objet bannissement""" def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) @@ -557,7 +555,7 @@ class BanForm(ModelForm): exclude = ['user'] -class WhitelistForm(ModelForm): +class WhitelistForm(FormRevMixin, ModelForm): """Creation, edition d'un objet whitelist""" def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) diff --git a/users/models.py b/users/models.py index 512259ae..caa03ce3 100644 --- a/users/models.py +++ b/users/models.py @@ -76,7 +76,7 @@ import ldapdb.models.fields from re2o.settings import RIGHTS_LINK, LDAP, GID_RANGES, UID_RANGES from re2o.login import hashNT from re2o.field_permissions import FieldPermissionModelMixin -from re2o.mixins import AclMixin +from re2o.mixins import AclMixin, RevMixin from cotisations.models import Cotisation, Facture, Paiement, Vente from machines.models import Domain, Interface, Machine, regen @@ -171,7 +171,7 @@ class UserManager(BaseUserManager): """ return self._create_user(pseudo, surname, email, password, True) -class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin, AclMixin): +class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin, AclMixin): """ Definition de l'utilisateur de base. Champs principaux : name, surnname, pseudo, email, room, password Herite du django BaseUser et du système d'auth django""" @@ -907,7 +907,7 @@ def user_post_delete(sender, **kwargs): user.ldap_del() regen('mailing') -class ServiceUser(AclMixin, AbstractBaseUser): +class ServiceUser(RevMixin, AclMixin, AbstractBaseUser): """ Classe des users daemons, règle leurs accès au ldap""" readonly = 'readonly' ACCESS = ( @@ -991,7 +991,7 @@ def service_user_post_delete(sender, **kwargs): service_user.ldap_del() -class School(AclMixin, models.Model): +class School(RevMixin, AclMixin, models.Model): """ Etablissement d'enseignement""" PRETTY_NAME = "Établissements enregistrés" @@ -1006,7 +1006,7 @@ class School(AclMixin, models.Model): return self.name -class ListRight(AclMixin, Group): +class ListRight(RevMixin, AclMixin, Group): """ Ensemble des droits existants. Chaque droit crée un groupe ldap synchronisé, avec gid. Permet de gérer facilement les accès serveurs et autres @@ -1073,7 +1073,7 @@ def listright_post_delete(sender, **kwargs): right.ldap_del() -class ListShell(AclMixin, models.Model): +class ListShell(RevMixin, AclMixin, models.Model): """Un shell possible. Pas de check si ce shell existe, les admin sont des grands""" PRETTY_NAME = "Liste des shells disponibles" @@ -1093,7 +1093,7 @@ class ListShell(AclMixin, models.Model): return self.shell -class Ban(AclMixin, models.Model): +class Ban(RevMixin, AclMixin, models.Model): """ Bannissement. Actuellement a un effet tout ou rien. Gagnerait à être granulaire""" PRETTY_NAME = "Liste des bannissements" @@ -1189,7 +1189,7 @@ def ban_post_delete(sender, **kwargs): regen('mac_ip_list') -class Whitelist(AclMixin, models.Model): +class Whitelist(RevMixin, AclMixin, models.Model): """Accès à titre gracieux. L'utilisateur ne paye pas; se voit accorder un accès internet pour une durée défini. Moins fort qu'un ban quel qu'il soit""" diff --git a/users/views.py b/users/views.py index 73332e8b..3374fea9 100644 --- a/users/views.py +++ b/users/views.py @@ -115,10 +115,7 @@ def new_user(request): GTU = GeneralOption.get_cached_value('GTU') if user.is_valid(): user = user.save(commit=False) - with transaction.atomic(), reversion.create_revision(): - user.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + user.save() user.reset_passwd_mail(request) messages.success(request, "L'utilisateur %s a été crée, un mail\ pour l'initialisation du mot de passe a été envoyé" % user.pseudo) @@ -137,10 +134,7 @@ def new_club(request): club = ClubForm(request.POST or None, user=request.user) if club.is_valid(): club = club.save(commit=False) - with transaction.atomic(), reversion.create_revision(): - club.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + club.save() club.reset_passwd_mail(request) messages.success(request, "L'utilisateur %s a été crée, un mail\ pour l'initialisation du mot de passe a été envoyé" % club.pseudo) @@ -158,12 +152,7 @@ def edit_club_admin_members(request, club_instance, clubid): membres d'un club""" club = ClubAdminandMembersForm(request.POST or None, instance=club_instance) if club.is_valid(): - with transaction.atomic(), reversion.create_revision(): - club.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for field in club.changed_data - )) + club.save() messages.success(request, "Le club a bien été modifié") return redirect(reverse( 'users:profil', @@ -191,12 +180,7 @@ def edit_info(request, user, userid): user=request.user ) if user.is_valid(): - with transaction.atomic(), reversion.create_revision(): - user.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for field in user.changed_data - )) + user.save() messages.success(request, "L'user a bien été modifié") return redirect(reverse( 'users:profil', @@ -212,18 +196,13 @@ def state(request, user, userid): need droit bureau """ state = StateForm(request.POST or None, instance=user) if state.is_valid(): - with transaction.atomic(), reversion.create_revision(): - if state.cleaned_data['state'] == User.STATE_ARCHIVE: - user.archive() - elif state.cleaned_data['state'] == User.STATE_ACTIVE: - user.unarchive() - elif state.cleaned_data['state'] == User.STATE_DISABLED: - user.state = User.STATE_DISABLED - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for field in state.changed_data - )) - user.save() + if state.cleaned_data['state'] == User.STATE_ARCHIVE: + user.archive() + elif state.cleaned_data['state'] == User.STATE_ACTIVE: + user.unarchive() + elif state.cleaned_data['state'] == User.STATE_DISABLED: + user.state = User.STATE_DISABLED + user.save() messages.success(request, "Etat changé avec succès") return redirect(reverse( 'users:profil', @@ -237,11 +216,6 @@ def state(request, user, userid): def groups(request, user, userid): group = GroupForm(request.POST or None, instance=user) if group.is_valid(): - with transaction.atomic(), reversion.create_revision(): - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for field in group.changed_data - )) group.save() messages.success(request, "Groupes changés avec succès") return redirect(reverse( @@ -259,10 +233,7 @@ def password(request, user, userid): pour tous si droit bureau """ u_form = PassForm(request.POST or None, instance=user, user=request.user) if u_form.is_valid(): - with transaction.atomic(), reversion.create_revision(): - u_form.save() - reversion.set_user(request.user) - reversion.set_comment("Changement du mot de passe") + u_form.save() messages.success(request, "Le mot de passe a changé") return redirect(reverse( 'users:profil', @@ -274,12 +245,9 @@ def password(request, user, userid): @login_required @can_edit(User, 'groups') def del_group(request, user, userid, listrightid): - with transaction.atomic(), reversion.create_revision(): - user.groups.remove(ListRight.objects.get(id=listrightid)) - user.save() - reversion.set_user(request.user) - reversion.set_comment("Suppression de droit") - messages.success(request, "Droit supprimé à %s" % user) + user.groups.remove(ListRight.objects.get(id=listrightid)) + user.save() + messages.success(request, "Droit supprimé à %s" % user) return HttpResponseRedirect(request.META.get('HTTP_REFERER')) @@ -290,11 +258,8 @@ def new_serviceuser(request): user = ServiceUserForm(request.POST or None) if user.is_valid(): user_object = user.save(commit=False) - with transaction.atomic(), reversion.create_revision(): - user_object.set_password(user.cleaned_data['password']) - user_object.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + user_object.set_password(user.cleaned_data['password']) + user_object.save() messages.success( request, "L'utilisateur %s a été crée" % user_object.pseudo @@ -310,14 +275,9 @@ def edit_serviceuser(request, user, userid): user = EditServiceUserForm(request.POST or None, instance=user) if user.is_valid(): user_object = user.save(commit=False) - with transaction.atomic(), reversion.create_revision(): - if user.cleaned_data['password']: - user_object.set_password(user.cleaned_data['password']) - user_object.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for field in user.changed_data - )) + if user.cleaned_data['password']: + user_object.set_password(user.cleaned_data['password']) + user_object.save() messages.success(request, "L'user a bien été modifié") return redirect(reverse('users:index-serviceusers')) return form({'userform': user, 'action_name':'Editer un serviceuser'}, 'users/user.html', request) @@ -328,9 +288,7 @@ def edit_serviceuser(request, user, userid): def del_serviceuser(request, user, userid): """Suppression d'un ou plusieurs serviceusers""" if request.method == "POST": - with transaction.atomic(), reversion.create_revision(): - user.delete() - reversion.set_user(request.user) + user.delete() messages.success(request, "L'user a été détruite") return redirect(reverse('users:index-serviceusers')) return form( @@ -350,10 +308,7 @@ def add_ban(request, user, userid): ban_instance = Ban(user=user) ban = BanForm(request.POST or None, instance=ban_instance) if ban.is_valid(): - with transaction.atomic(), reversion.create_revision(): - _ban_object = ban.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + _ban_object = ban.save() messages.success(request, "Bannissement ajouté") return redirect(reverse( 'users:profil', @@ -374,12 +329,7 @@ def edit_ban(request, ban_instance, banid): Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement""" ban = BanForm(request.POST or None, instance=ban_instance) if ban.is_valid(): - with transaction.atomic(), reversion.create_revision(): - ban.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for field in ban.changed_data - )) + ban.save() messages.success(request, "Bannissement modifié") return redirect(reverse('users:index')) return form({'userform': ban, 'action_name': 'Editer un ban'}, 'users/user.html', request) @@ -399,10 +349,7 @@ def add_whitelist(request, user, userid): instance=whitelist_instance ) if whitelist.is_valid(): - with transaction.atomic(), reversion.create_revision(): - whitelist.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + whitelist.save() messages.success(request, "Accès à titre gracieux accordé") return redirect(reverse( 'users:profil', @@ -428,12 +375,7 @@ def edit_whitelist(request, whitelist_instance, whitelistid): instance=whitelist_instance ) if whitelist.is_valid(): - with transaction.atomic(), reversion.create_revision(): - whitelist.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for field in whitelist.changed_data - )) + whitelist.save() messages.success(request, "Whitelist modifiée") return redirect(reverse('users:index')) return form({'userform': whitelist, 'action_name': 'Editer une whitelist'}, 'users/user.html', request) @@ -446,10 +388,7 @@ def add_school(request): need cableur""" school = SchoolForm(request.POST or None) if school.is_valid(): - with transaction.atomic(), reversion.create_revision(): - school.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + school.save() messages.success(request, "L'établissement a été ajouté") return redirect(reverse('users:index-school')) return form({'userform': school, 'action_name':'Ajouter'}, 'users/user.html', request) @@ -462,12 +401,7 @@ def edit_school(request, school_instance, schoolid): la base de donnée, need cableur""" school = SchoolForm(request.POST or None, instance=school_instance) if school.is_valid(): - with transaction.atomic(), reversion.create_revision(): - school.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for field in school.changed_data - )) + school.save() messages.success(request, "Établissement modifié") return redirect(reverse('users:index-school')) return form({'userform': school, 'action_name':'Editer'}, 'users/user.html', request) @@ -485,9 +419,7 @@ def del_school(request, instances): school_dels = school.cleaned_data['schools'] for school_del in school_dels: try: - with transaction.atomic(), reversion.create_revision(): - school_del.delete() - reversion.set_comment("Destruction") + school_del.delete() messages.success(request, "L'établissement a été supprimé") except ProtectedError: messages.error( @@ -504,10 +436,7 @@ def add_shell(request): """ Ajouter un shell à la base de donnée""" shell = ShellForm(request.POST or None) if shell.is_valid(): - with transaction.atomic(), reversion.create_revision(): - shell.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + shell.save() messages.success(request, "Le shell a été ajouté") return redirect(reverse('users:index-shell')) return form({'userform': shell, 'action_name':'Ajouter'}, 'users/user.html', request) @@ -519,12 +448,7 @@ def edit_shell(request, shell_instance, listshellid): """ Editer un shell à partir du listshellid""" shell = ShellForm(request.POST or None, instance=shell_instance) if shell.is_valid(): - with transaction.atomic(), reversion.create_revision(): - shell.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for field in shell.changed_data - )) + shell.save() messages.success(request, "Le shell a été modifié") return redirect(reverse('users:index-shell')) return form({'userform': shell, 'action_name':'Editer'}, 'users/user.html', request) @@ -535,9 +459,7 @@ def edit_shell(request, shell_instance, listshellid): def del_shell(request, shell, listshellid): """Destruction d'un shell""" if request.method == "POST": - with transaction.atomic(), reversion.create_revision(): - shell.delete() - reversion.set_user(request.user) + shell.delete() messages.success(request, "Le shell a été détruit") return redirect(reverse('users:index-shell')) return form( @@ -554,10 +476,7 @@ def add_listright(request): Obligation de fournir un gid pour la synchro ldap, unique """ listright = NewListRightForm(request.POST or None) if listright.is_valid(): - with transaction.atomic(), reversion.create_revision(): - listright.save() - reversion.set_user(request.user) - reversion.set_comment("Création") + listright.save() messages.success(request, "Le droit/groupe a été ajouté") return redirect(reverse('users:index-listright')) return form({'userform': listright, 'action_name': 'Ajouter'}, 'users/user.html', request) @@ -573,12 +492,7 @@ def edit_listright(request, listright_instance, listrightid): instance=listright_instance ) if listright.is_valid(): - with transaction.atomic(), reversion.create_revision(): - listright.save() - reversion.set_user(request.user) - reversion.set_comment("Champs modifié(s) : %s" % ', '.join( - field for field in listright.changed_data - )) + listright.save() messages.success(request, "Droit modifié") return redirect(reverse('users:index-listright')) return form({'userform': listright, 'action_name': 'Editer'}, 'users/user.html', request) @@ -594,9 +508,7 @@ def del_listright(request, instances): listright_dels = listright.cleaned_data['listrights'] for listright_del in listright_dels: try: - with transaction.atomic(), reversion.create_revision(): - listright_del.delete() - reversion.set_comment("Destruction") + listright_del.delete() messages.success(request, "Le droit/groupe a été supprimé") except ProtectedError: messages.error( @@ -625,7 +537,6 @@ def mass_archive(request): with transaction.atomic(), reversion.create_revision(): user.archive() user.save() - reversion.set_user(request.user) reversion.set_comment("Archivage") messages.success(request, "%s users ont été archivés" % len( to_archive_list From bb5c9603debf68c1311151a053b2c41d9d0c11ea Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 31 Mar 2018 17:27:24 +0200 Subject: [PATCH 02/11] Fix history shell --- users/templates/users/aff_shell.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/templates/users/aff_shell.html b/users/templates/users/aff_shell.html index ad325ddf..a660f88b 100644 --- a/users/templates/users/aff_shell.html +++ b/users/templates/users/aff_shell.html @@ -39,7 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% can_edit shell %} {% include 'buttons/edit.html' with href='users:edit-shell' id=shell.id %} {% acl_end %} - {% include 'buttons/history.html' with href='users:history' name='shell' id=shell.id %} + {% include 'buttons/history.html' with href='users:history' name='listshell' id=shell.id %} {% endfor %} From 30a576c01099b1263d185c997ab31b1a6d04cab9 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 31 Mar 2018 17:42:16 +0200 Subject: [PATCH 03/11] Gestion historique sur les ports et model de ports --- topologie/forms.py | 8 ++++---- topologie/views.py | 3 --- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/topologie/forms.py b/topologie/forms.py index f6bfaf26..b8c3d8d1 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -64,7 +64,7 @@ class PortForm(FormRevMixin, ModelForm): super(PortForm, self).__init__(*args, prefix=prefix, **kwargs) -class EditPortForm(ModelForm): +class EditPortForm(FormRevMixin, ModelForm): """Form pour l'édition d'un port de switche : changement des reglages radius ou vlan, ou attribution d'une chambre, autre port ou machine @@ -89,7 +89,7 @@ class EditPortForm(ModelForm): )) -class AddPortForm(ModelForm): +class AddPortForm(FormRevMixin, ModelForm): """Permet d'ajouter un port de switch. Voir EditPortForm pour plus d'informations""" class Meta(PortForm.Meta): @@ -108,7 +108,7 @@ class AddPortForm(ModelForm): )) -class StackForm(ModelForm): +class StackForm(FormRevMixin, ModelForm): """Permet d'edition d'une stack : stack_id, et switches membres de la stack""" class Meta: @@ -160,7 +160,7 @@ class EditRoomForm(FormRevMixin, ModelForm): super(EditRoomForm, self).__init__(*args, prefix=prefix, **kwargs) -class CreatePortsForm(FormRevMixin, forms.Form): +class CreatePortsForm(forms.Form): """Permet de créer une liste de ports pour un switch.""" begin = forms.IntegerField(label="Début :", min_value=0) end = forms.IntegerField(label="Fin :", min_value=0) diff --git a/topologie/views.py b/topologie/views.py index f942f377..b2ac8d71 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -321,7 +321,6 @@ def new_stack(request): @can_edit(Stack) def edit_stack(request, stack, stackid): """Edition d'un stack (nombre de switches, nom...)""" - stack = StackForm(request.POST or None, instance=stack) if stack.is_valid(): stack.save() @@ -433,7 +432,6 @@ def create_ports(request, switchid): messages.success(request, "Ports créés.") except ValidationError as e: messages.error(request, ''.join(e)) - return redirect(reverse( 'topologie:index-port', kwargs={'switchid':switchid} @@ -586,7 +584,6 @@ def new_room(request): @can_edit(Room) def edit_room(request, room, roomid): """ Edition numero et details de la chambre""" - room = EditRoomForm(request.POST or None, instance=room) if room.is_valid(): room.save() From a553789bad9705fe896eecd8e458d6acff0dacf5 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 31 Mar 2018 18:10:24 +0200 Subject: [PATCH 04/11] =?UTF-8?q?Draft=20:=20on=20save=20que=20si=20l'obje?= =?UTF-8?q?t=20a=20=C3=A9t=C3=A9=20modifi=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- re2o/mixins.py | 2 +- topologie/management/commands/graph_topo.py | 228 +++++++++++ topologie/management/commands/modelviz.py | 405 ++++++++++++++++++++ topologie/views.py | 43 ++- 4 files changed, 661 insertions(+), 17 deletions(-) create mode 100644 topologie/management/commands/graph_topo.py create mode 100644 topologie/management/commands/modelviz.py diff --git a/re2o/mixins.py b/re2o/mixins.py index 1432d7d9..307074ff 100644 --- a/re2o/mixins.py +++ b/re2o/mixins.py @@ -38,7 +38,7 @@ class FormRevMixin(object): def save(self, *args, **kwargs): if reversion.get_comment() != "" and self.changed_data != []: reversion.set_comment(reversion.get_comment() + ",%s" % ', '.join(field for field in self.changed_data)) - elif self.changed_data != None: + elif self.changed_data: reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in self.changed_data)) return super(FormRevMixin, self).save(*args, **kwargs) diff --git a/topologie/management/commands/graph_topo.py b/topologie/management/commands/graph_topo.py new file mode 100644 index 00000000..b5d61a80 --- /dev/null +++ b/topologie/management/commands/graph_topo.py @@ -0,0 +1,228 @@ +# -*- coding: utf-8 -*- +import sys +import json + +import six +from django.conf import settings +from django.core.management.base import BaseCommand, CommandError + +from topologie.management.modelviz import ModelGraph, generate_dot +from django_extensions.management.utils import signalcommand + +try: + import pygraphviz + HAS_PYGRAPHVIZ = True +except ImportError: + HAS_PYGRAPHVIZ = False + +try: + try: + import pydotplus as pydot + except ImportError: + import pydot + HAS_PYDOT = True +except ImportError: + HAS_PYDOT = False + + +class Command(BaseCommand): + help = "Creates a GraphViz dot file for the specified app names. You can pass multiple app names and they will all be combined into a single model. Output is usually directed to a dot file." + + can_import_settings = True + + def __init__(self, *args, **kwargs): + """Allow defaults for arguments to be set in settings.GRAPH_MODELS. + Each argument in self.arguments is a dict where the key is the + space-separated args and the value is our kwarg dict. + The default from settings is keyed as the long arg name with '--' + removed and any '-' replaced by '_'. + """ + self.arguments = { + '--pygraphviz': { + 'action': 'store_true', 'dest': 'pygraphviz', + 'help': 'Use PyGraphViz to generate the image.'}, + + '--pydot': {'action': 'store_true', 'dest': 'pydot', + 'help': 'Use PyDot(Plus) to generate the image.'}, + + '--disable-fields -d': { + 'action': 'store_true', 'dest': 'disable_fields', + 'help': 'Do not show the class member fields'}, + + '--group-models -g': { + 'action': 'store_true', 'dest': 'group_models', + 'help': 'Group models together respective to their ' + 'application'}, + + '--all-applications -a': { + 'action': 'store_true', 'dest': 'all_applications', + 'help': 'Automatically include all applications from ' + 'INSTALLED_APPS'}, + + '--output -o': { + 'action': 'store', 'dest': 'outputfile', + 'help': 'Render output file. Type of output dependend on file ' + 'extensions. Use png or jpg to render graph to image.'}, + + '--layout -l': { + 'action': 'store', 'dest': 'layout', 'default': 'dot', + 'help': 'Layout to be used by GraphViz for visualization. ' + 'Layouts: circo dot fdp neato nop nop1 nop2 twopi'}, + + '--verbose-names -n': { + 'action': 'store_true', 'dest': 'verbose_names', + 'help': 'Use verbose_name of models and fields'}, + + '--language -L': { + 'action': 'store', 'dest': 'language', + 'help': 'Specify language used for verbose_name localization'}, + + '--exclude-columns -x': { + 'action': 'store', 'dest': 'exclude_columns', + 'help': 'Exclude specific column(s) from the graph. ' + 'Can also load exclude list from file.'}, + + '--exclude-models -X': { + 'action': 'store', 'dest': 'exclude_models', + 'help': 'Exclude specific model(s) from the graph. Can also ' + 'load exclude list from file. Wildcards (*) are allowed.'}, + + '--include-models -I': { + 'action': 'store', 'dest': 'include_models', + 'help': 'Restrict the graph to specified models. Wildcards ' + '(*) are allowed.'}, + + '--inheritance -e': { + 'action': 'store_true', 'dest': 'inheritance', 'default': True, + 'help': 'Include inheritance arrows (default)'}, + + '--no-inheritance -E': { + 'action': 'store_false', 'dest': 'inheritance', + 'help': 'Do not include inheritance arrows'}, + + '--hide-relations-from-fields -R': { + 'action': 'store_false', 'dest': 'relations_as_fields', + 'default': True, + 'help': 'Do not show relations as fields in the graph.'}, + + '--disable-sort-fields -S': { + 'action': 'store_false', 'dest': 'sort_fields', + 'default': True, 'help': 'Do not sort fields'}, + + '--json': {'action': 'store_true', 'dest': 'json', + 'help': 'Output graph data as JSON'} + } + + defaults = getattr(settings, 'GRAPH_MODELS', None) + + if defaults: + for argument in self.arguments: + arg_split = argument.split(' ') + setting_opt = arg_split[0].lstrip('-').replace('-', '_') + if setting_opt in defaults: + self.arguments[argument]['default'] = defaults[setting_opt] + + super(Command, self).__init__(*args, **kwargs) + + def add_arguments(self, parser): + """Unpack self.arguments for parser.add_arguments.""" + parser.add_argument('app_label', nargs='*') + for argument in self.arguments: + parser.add_argument(*argument.split(' '), + **self.arguments[argument]) + + @signalcommand + def handle(self, *args, **options): + args = options['app_label'] + if len(args) < 1 and not options['all_applications']: + raise CommandError("need one or more arguments for appname") + + use_pygraphviz = options.get('pygraphviz', False) + use_pydot = options.get('pydot', False) + use_json = options.get('json', False) + if use_json and (use_pydot or use_pygraphviz): + raise CommandError("Cannot specify --json with --pydot or --pygraphviz") + + cli_options = ' '.join(sys.argv[2:]) + graph_models = ModelGraph(args, cli_options=cli_options, **options) + graph_models.generate_graph_data() + graph_data = graph_models.get_graph_data(as_json=use_json) + if use_json: + self.render_output_json(graph_data, **options) + return + + dotdata = generate_dot(graph_data) + if not six.PY3: + dotdata = dotdata.encode('utf-8') + if options['outputfile']: + if not use_pygraphviz and not use_pydot: + if HAS_PYGRAPHVIZ: + use_pygraphviz = True + elif HAS_PYDOT: + use_pydot = True + if use_pygraphviz: + self.render_output_pygraphviz(dotdata, **options) + elif use_pydot: + self.render_output_pydot(dotdata, **options) + else: + raise CommandError("Neither pygraphviz nor pydotplus could be found to generate the image") + else: + self.print_output(dotdata) + + def print_output(self, dotdata): + if six.PY3 and isinstance(dotdata, six.binary_type): + dotdata = dotdata.decode() + + print(dotdata) + + def render_output_json(self, graph_data, **kwargs): + output_file = kwargs.get('outputfile') + if output_file: + with open(output_file, 'wt') as json_output_f: + json.dump(graph_data, json_output_f) + else: + print(json.dumps(graph_data)) + + def render_output_pygraphviz(self, dotdata, **kwargs): + """Renders the image using pygraphviz""" + if not HAS_PYGRAPHVIZ: + raise CommandError("You need to install pygraphviz python module") + + version = pygraphviz.__version__.rstrip("-svn") + try: + if tuple(int(v) for v in version.split('.')) < (0, 36): + # HACK around old/broken AGraph before version 0.36 (ubuntu ships with this old version) + import tempfile + tmpfile = tempfile.NamedTemporaryFile() + tmpfile.write(dotdata) + tmpfile.seek(0) + dotdata = tmpfile.name + except ValueError: + pass + + graph = pygraphviz.AGraph(dotdata) + graph.layout(prog=kwargs['layout']) + graph.draw(kwargs['outputfile']) + + def render_output_pydot(self, dotdata, **kwargs): + """Renders the image using pydot""" + if not HAS_PYDOT: + raise CommandError("You need to install pydot python module") + + graph = pydot.graph_from_dot_data(dotdata) + if not graph: + raise CommandError("pydot returned an error") + if isinstance(graph, (list, tuple)): + if len(graph) > 1: + sys.stderr.write("Found more then one graph, rendering only the first one.\n") + graph = graph[0] + + output_file = kwargs['outputfile'] + formats = ['bmp', 'canon', 'cmap', 'cmapx', 'cmapx_np', 'dot', 'dia', 'emf', + 'em', 'fplus', 'eps', 'fig', 'gd', 'gd2', 'gif', 'gv', 'imap', + 'imap_np', 'ismap', 'jpe', 'jpeg', 'jpg', 'metafile', 'pdf', + 'pic', 'plain', 'plain-ext', 'png', 'pov', 'ps', 'ps2', 'svg', + 'svgz', 'tif', 'tiff', 'tk', 'vml', 'vmlz', 'vrml', 'wbmp', 'xdot'] + ext = output_file[output_file.rfind('.') + 1:] + format = ext if ext in formats else 'raw' + graph.write(output_file, format=format) diff --git a/topologie/management/commands/modelviz.py b/topologie/management/commands/modelviz.py new file mode 100644 index 00000000..0f01bb4d --- /dev/null +++ b/topologie/management/commands/modelviz.py @@ -0,0 +1,405 @@ +# -*- coding: utf-8 -*- +""" +modelviz.py - DOT file generator for Django Models +Based on: + Django model to DOT (Graphviz) converter + by Antonio Cavedoni + Adapted to be used with django-extensions +""" + +import datetime +import os +import re + +import six +from django.apps import apps +from django.db.models.fields.related import ( + ForeignKey, ManyToManyField, OneToOneField, RelatedField, +) +from django.contrib.contenttypes.fields import GenericRelation +from django.template import Context, Template, loader +from django.utils.encoding import force_bytes, force_str +from django.utils.safestring import mark_safe +from django.utils.translation import activate as activate_language + + +__version__ = "1.1" +__license__ = "Python" +__author__ = "Bas van Oostveen ", +__contributors__ = [ + "Antonio Cavedoni " + "Stefano J. Attardi ", + "limodou ", + "Carlo C8E Miron", + "Andre Campos ", + "Justin Findlay ", + "Alexander Houben ", + "Joern Hees ", + "Kevin Cherepski ", + "Jose Tomas Tocino ", + "Adam Dobrawy ", + "Mikkel Munch Mortensen ", + "Andrzej Bistram ", +] + + +def parse_file_or_list(arg): + if not arg: + return [] + if isinstance(arg, (list, tuple, set)): + return arg + if ',' not in arg and os.path.isfile(arg): + return [e.strip() for e in open(arg).readlines()] + return [e.strip() for e in arg.split(',')] + + +class ModelGraph(object): + def __init__(self, app_labels, **kwargs): + self.graphs = [] + self.cli_options = kwargs.get('cli_options', None) + self.disable_fields = kwargs.get('disable_fields', False) + self.include_models = parse_file_or_list( + kwargs.get('include_models', "") + ) + self.all_applications = kwargs.get('all_applications', False) + self.use_subgraph = kwargs.get('group_models', False) + self.verbose_names = kwargs.get('verbose_names', False) + self.inheritance = kwargs.get('inheritance', True) + self.relations_as_fields = kwargs.get("relations_as_fields", True) + self.sort_fields = kwargs.get("sort_fields", True) + self.language = kwargs.get('language', None) + if self.language is not None: + activate_language(self.language) + self.exclude_columns = parse_file_or_list( + kwargs.get('exclude_columns', "") + ) + self.exclude_models = parse_file_or_list( + kwargs.get('exclude_models', "") + ) + if self.all_applications: + self.app_labels = [app.label for app in apps.get_app_configs()] + else: + self.app_labels = app_labels + + def generate_graph_data(self): + self.process_apps() + + nodes = [] + for graph in self.graphs: + nodes.extend([e['name'] for e in graph['models']]) + + for graph in self.graphs: + for model in graph['models']: + for relation in model['relations']: + if relation is not None: + if relation['target'] in nodes: + relation['needs_node'] = False + + def get_graph_data(self, as_json=False): + now = datetime.datetime.now() + graph_data = { + 'created_at': now.strftime("%Y-%m-%d %H:%M"), + 'cli_options': self.cli_options, + 'disable_fields': self.disable_fields, + 'use_subgraph': self.use_subgraph, + } + + if as_json: + graph_data['graphs'] = [context.flatten() for context in self.graphs] + else: + graph_data['graphs'] = self.graphs + + return graph_data + + def add_attributes(self, field, abstract_fields): + if self.verbose_names and field.verbose_name: + label = force_bytes(field.verbose_name) + if label.islower(): + label = label.capitalize() + else: + label = field.name + + t = type(field).__name__ + if isinstance(field, (OneToOneField, ForeignKey)): + remote_field = field.remote_field if hasattr(field, 'remote_field') else field.rel # Remove me after Django 1.8 is unsupported + t += " ({0})".format(remote_field.field_name) + # TODO: ManyToManyField, GenericRelation + + return { + 'name': field.name, + 'label': label, + 'type': t, + 'blank': field.blank, + 'abstract': field in abstract_fields, + 'relation': isinstance(field, RelatedField), + 'primary_key': field.primary_key, + } + + def add_relation(self, field, model, extras=""): + if self.verbose_names and field.verbose_name: + label = force_bytes(field.verbose_name) + if label.islower(): + label = label.capitalize() + else: + label = field.name + + # show related field name + if hasattr(field, 'related_query_name'): + related_query_name = field.related_query_name() + if self.verbose_names and related_query_name.islower(): + related_query_name = related_query_name.replace('_', ' ').capitalize() + label = '{} ({})'.format(label, force_str(related_query_name)) + + # handle self-relationships and lazy-relationships + remote_field = field.remote_field if hasattr(field, 'remote_field') else field.rel # Remove me after Django 1.8 is unsupported + remote_field_model = remote_field.model if hasattr(remote_field, 'model') else remote_field.to # Remove me after Django 1.8 is unsupported + if isinstance(remote_field_model, six.string_types): + if remote_field_model == 'self': + target_model = field.model + else: + if '.' in remote_field_model: + app_label, model_name = remote_field_model.split('.', 1) + else: + app_label = field.model._meta.app_label + model_name = remote_field_model + target_model = apps.get_model(app_label, model_name) + else: + target_model = remote_field_model + + _rel = self.get_relation_context(target_model, field, label, extras) + + if _rel not in model['relations'] and self.use_model(_rel['target']): + return _rel + + def get_abstract_models(self, appmodels): + abstract_models = [] + for appmodel in appmodels: + abstract_models += [abstract_model for abstract_model in + appmodel.__bases__ if + hasattr(abstract_model, '_meta') and + abstract_model._meta.abstract] + abstract_models = list(set(abstract_models)) # remove duplicates + return abstract_models + + def get_app_context(self, app): + return Context({ + 'name': '"%s"' % app.name, + 'app_name': "%s" % app.name, + 'cluster_app_name': "cluster_%s" % app.name.replace(".", "_"), + 'models': [] + }) + + def get_appmodel_attributes(self, appmodel): + if self.relations_as_fields: + attributes = [field for field in appmodel._meta.local_fields] + else: + # Find all the 'real' attributes. Relations are depicted as graph edges instead of attributes + attributes = [field for field in appmodel._meta.local_fields if not + isinstance(field, RelatedField)] + return attributes + + def get_appmodel_abstracts(self, appmodel): + return [abstract_model.__name__ for abstract_model in + appmodel.__bases__ if + hasattr(abstract_model, '_meta') and + abstract_model._meta.abstract] + + def get_appmodel_context(self, appmodel, appmodel_abstracts): + context = { + 'app_name': appmodel.__module__.replace(".", "_"), + 'name': appmodel.__name__, + 'abstracts': appmodel_abstracts, + 'fields': [], + 'relations': [] + } + + if self.verbose_names and appmodel._meta.verbose_name: + context['label'] = force_bytes(appmodel._meta.verbose_name) + else: + context['label'] = context['name'] + + return context + + def get_bases_abstract_fields(self, c): + _abstract_fields = [] + for e in c.__bases__: + if hasattr(e, '_meta') and e._meta.abstract: + _abstract_fields.extend(e._meta.fields) + _abstract_fields.extend(self.get_bases_abstract_fields(e)) + return _abstract_fields + + def get_inheritance_context(self, appmodel, parent): + label = "multi-table" + if parent._meta.abstract: + label = "abstract" + if appmodel._meta.proxy: + label = "proxy" + label += r"\ninheritance" + return { + 'target_app': parent.__module__.replace(".", "_"), + 'target': parent.__name__, + 'type': "inheritance", + 'name': "inheritance", + 'label': label, + 'arrows': '[arrowhead=empty, arrowtail=none, dir=both]', + 'needs_node': True, + } + + def get_models(self, app): + appmodels = list(app.get_models()) + return appmodels + + def get_relation_context(self, target_model, field, label, extras): + return { + 'target_app': target_model.__module__.replace('.', '_'), + 'target': target_model.__name__, + 'type': type(field).__name__, + 'name': field.name, + 'label': label, + 'arrows': extras, + 'needs_node': True + } + + def process_attributes(self, field, model, pk, abstract_fields): + newmodel = model.copy() + if self.skip_field(field) or pk and field == pk: + return newmodel + newmodel['fields'].append(self.add_attributes(field, abstract_fields)) + return newmodel + + def process_apps(self): + for app_label in self.app_labels: + app = apps.get_app_config(app_label) + if not app: + continue + app_graph = self.get_app_context(app) + app_models = self.get_models(app) + abstract_models = self.get_abstract_models(app_models) + app_models = abstract_models + app_models + + for appmodel in app_models: + if not self.use_model(appmodel._meta.object_name): + continue + appmodel_abstracts = self.get_appmodel_abstracts(appmodel) + abstract_fields = self.get_bases_abstract_fields(appmodel) + model = self.get_appmodel_context(appmodel, appmodel_abstracts) + attributes = self.get_appmodel_attributes(appmodel) + + # find primary key and print it first, ignoring implicit id if other pk exists + pk = appmodel._meta.pk + if pk and not appmodel._meta.abstract and pk in attributes: + model['fields'].append(self.add_attributes(pk, abstract_fields)) + + for field in attributes: + model = self.process_attributes(field, model, pk, abstract_fields) + + if self.sort_fields: + model = self.sort_model_fields(model) + + for field in appmodel._meta.local_fields: + model = self.process_local_fields(field, model, abstract_fields) + + for field in appmodel._meta.local_many_to_many: + model = self.process_local_many_to_many(field, model) + + if self.inheritance: + # add inheritance arrows + for parent in appmodel.__bases__: + model = self.process_parent(parent, appmodel, model) + + app_graph['models'].append(model) + if app_graph['models']: + self.graphs.append(app_graph) + + def process_local_fields(self, field, model, abstract_fields): + newmodel = model.copy() + if (field.attname.endswith('_ptr_id') or # excluding field redundant with inheritance relation + field in abstract_fields or # excluding fields inherited from abstract classes. they too show as local_fields + self.skip_field(field)): + return newmodel + if isinstance(field, OneToOneField): + newmodel['relations'].append(self.add_relation(field, newmodel, '[arrowhead=none, arrowtail=none, dir=both]')) + elif isinstance(field, ForeignKey): + newmodel['relations'].append(self.add_relation(field, newmodel, '[arrowhead=none, arrowtail=dot, dir=both]')) + return newmodel + + def process_local_many_to_many(self, field, model): + newmodel = model.copy() + if self.skip_field(field): + return newmodel + if isinstance(field, ManyToManyField): + remote_field = field.remote_field if hasattr(field, 'remote_field') else field.rel # Remove me after Django 1.8 is unsupported + if hasattr(remote_field.through, '_meta') and remote_field.through._meta.auto_created: + newmodel['relations'].append(self.add_relation(field, newmodel, '[arrowhead=dot arrowtail=dot, dir=both]')) + elif isinstance(field, GenericRelation): + newmodel['relations'].append(self.add_relation(field, newmodel, mark_safe('[style="dotted", arrowhead=normal, arrowtail=normal, dir=both]'))) + return newmodel + + def process_parent(self, parent, appmodel, model): + newmodel = model.copy() + if hasattr(parent, "_meta"): # parent is a model + _rel = self.get_inheritance_context(appmodel, parent) + # TODO: seems as if abstract models aren't part of models.getModels, which is why they are printed by this without any attributes. + if _rel not in newmodel['relations'] and self.use_model(_rel['target']): + newmodel['relations'].append(_rel) + return newmodel + + def sort_model_fields(self, model): + newmodel = model.copy() + newmodel['fields'] = sorted(newmodel['fields'], key=lambda field: (not field['primary_key'], not field['relation'], field['label'])) + return newmodel + + def use_model(self, model_name): + """ + Decide whether to use a model, based on the model name and the lists of + models to exclude and include. + """ + # Check against exclude list. + if self.exclude_models: + for model_pattern in self.exclude_models: + model_pattern = '^%s$' % model_pattern.replace('*', '.*') + if re.search(model_pattern, model_name): + return False + # Check against exclude list. + elif self.include_models: + for model_pattern in self.include_models: + model_pattern = '^%s$' % model_pattern.replace('*', '.*') + if re.search(model_pattern, model_name): + return True + # Return `True` if `include_models` is falsey, otherwise return `False`. + return not self.include_models + + def skip_field(self, field): + if self.exclude_columns: + if self.verbose_names and field.verbose_name: + if field.verbose_name in self.exclude_columns: + return True + if field.name in self.exclude_columns: + return True + return False + + +def generate_dot(graph_data, template='django_extensions/graph_models/digraph.dot'): + t = loader.get_template(template) + + if not isinstance(t, Template) and not (hasattr(t, 'template') and isinstance(t.template, Template)): + raise Exception("Default Django template loader isn't used. " + "This can lead to the incorrect template rendering. " + "Please, check the settings.") + + c = Context(graph_data).flatten() + dot = t.render(c) + + return dot + + +def generate_graph_data(*args, **kwargs): + generator = ModelGraph(*args, **kwargs) + generator.generate_graph_data() + return generator.get_graph_data() + + +def use_model(model, include_models, exclude_models): + generator = ModelGraph([], include_models=include_models, exclude_models=exclude_models) + return generator.use_model(model) + diff --git a/topologie/views.py b/topologie/views.py index b2ac8d71..f87213fa 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -279,8 +279,9 @@ def edit_port(request, port_object, portid): port = EditPortForm(request.POST or None, instance=port_object) if port.is_valid(): - port.save() - messages.success(request, "Le port a bien été modifié") + if port.changed_data: + port.save() + messages.success(request, "Le port a bien été modifié") return redirect(reverse( 'topologie:index-port', kwargs={'switchid': str(port_object.switch.id)} @@ -323,8 +324,9 @@ def edit_stack(request, stack, stackid): """Edition d'un stack (nombre de switches, nom...)""" stack = StackForm(request.POST or None, instance=stack) if stack.is_valid(): - stack.save() - return redirect(reverse('topologie:index-stack')) + if stack.changed_data: + stack.save() + return redirect(reverse('topologie:index-stack')) return form({'topoform': stack, 'action_name' : 'Editer'}, 'topologie/topo.html', request) @@ -463,9 +465,12 @@ def edit_switch(request, switch, switchid): new_switch = switch_form.save(commit=False) new_interface_instance = interface_form.save(commit=False) new_domain = domain_form.save(commit=False) - new_switch.save() - new_interface_instance.save() - new_domain.save() + if switch_form.changed_data: + new_switch.save() + if interface_form.changed_data: + new_interface_instance.save() + if domain_form.changed_data: + new_domain.save() messages.success(request, "Le switch a bien été modifié") return redirect(reverse('topologie:index')) i_mbf_param = generate_ipv4_mbf_param(interface_form, False ) @@ -553,9 +558,12 @@ def edit_ap(request, ap, accesspointid): new_ap = ap_form.save(commit=False) new_interface = interface_form.save(commit=False) new_domain = domain_form.save(commit=False) - new_ap.save() - new_interface.save() - new_domain.save() + if ap_form.changed_data: + new_ap.save() + if interface_form.changed_data: + new_interface.save() + if domain_form.changed_data: + new_domain.save() messages.success(request, "La borne a été modifiée") return redirect(reverse('topologie:index-ap')) i_mbf_param = generate_ipv4_mbf_param(interface_form, False ) @@ -586,8 +594,9 @@ def edit_room(request, room, roomid): """ Edition numero et details de la chambre""" room = EditRoomForm(request.POST or None, instance=room) if room.is_valid(): - room.save() - messages.success(request, "La chambre a bien été modifiée") + if room.changed_data: + room.save() + messages.success(request, "La chambre a bien été modifiée") return redirect(reverse('topologie:index-room')) return form({'topoform': room, 'action_name' : 'Editer'}, 'topologie/topo.html', request) @@ -629,8 +638,9 @@ def edit_model_switch(request, model_switch, modelswitchid): model_switch = EditModelSwitchForm(request.POST or None, instance=model_switch) if model_switch.is_valid(): - model_switch.save() - messages.success(request, "Le modèle a bien été modifié") + if model_switch.changed_data: + model_switch.save() + messages.success(request, "Le modèle a bien été modifié") return redirect(reverse('topologie:index-model-switch')) return form({'topoform': model_switch, 'action_name' : 'Editer'}, 'topologie/topo.html', request) @@ -672,8 +682,9 @@ def edit_constructor_switch(request, constructor_switch, constructorswitchid): constructor_switch = EditConstructorSwitchForm(request.POST or None, instance=constructor_switch) if constructor_switch.is_valid(): - constructor_switch.save() - messages.success(request, "Le modèle a bien été modifié") + if constructor_switch.changed_data: + constructor_switch.save() + messages.success(request, "Le modèle a bien été modifié") return redirect(reverse('topologie:index-model-switch')) return form({'topoform': constructor_switch, 'action_name' : 'Editer'}, 'topologie/topo.html', request) From 543037b66747764dcbd52891a1aec56c0e54f946 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sun, 1 Apr 2018 00:06:44 +0200 Subject: [PATCH 05/11] =?UTF-8?q?Save=20que=20si=20l'objet=20a=20=C3=A9t?= =?UTF-8?q?=C3=A9=20modifi=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cotisations/views.py | 21 +++++---- machines/views.py | 107 +++++++++++++++++++++---------------------- topologie/models.py | 3 ++ users/views.py | 53 ++++++++++++--------- 4 files changed, 100 insertions(+), 84 deletions(-) diff --git a/cotisations/views.py b/cotisations/views.py index 974f659e..6bf58994 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -249,8 +249,10 @@ def edit_facture(request, facture, factureid): ) vente_form = vente_form_set(request.POST or None, queryset=ventes_objects) if facture_form.is_valid() and vente_form.is_valid(): - facture_form.save() - vente_form.save() + if facture_form.changed_data: + facture_form.save() + if vente_form.changed_data: + vente_form.save() messages.success(request, "La facture a bien été modifiée") return redirect(reverse('cotisations:index')) return form({ @@ -321,8 +323,9 @@ def edit_article(request, article_instance, articleid): Réservé au trésorier""" article = ArticleForm(request.POST or None, instance=article_instance) if article.is_valid(): - article.save() - messages.success(request, "Type d'article modifié") + if article.changed_data: + article.save() + messages.success(request, "Type d'article modifié") return redirect(reverse('cotisations:index-article')) return form({'factureform': article, 'action_name' : 'Editer'}, 'cotisations/facture.html', request) @@ -359,8 +362,9 @@ def edit_paiement(request, paiement_instance, paiementid): """Edition d'un moyen de paiement""" paiement = PaiementForm(request.POST or None, instance=paiement_instance) if paiement.is_valid(): - paiement.save() - messages.success(request, "Type de paiement modifié") + if paiement.changed_data: + paiement.save() + messages.success(request, "Type de paiement modifié") return redirect(reverse('cotisations:index-paiement')) return form({'factureform': paiement, 'action_name' : 'Editer'}, 'cotisations/facture.html', request) @@ -407,8 +411,9 @@ def edit_banque(request, banque_instance, banqueid): """Edite le nom d'une banque""" banque = BanqueForm(request.POST or None, instance=banque_instance) if banque.is_valid(): - banque.save() - messages.success(request, "Banque modifiée") + if banque.changed_data: + banque.save() + messages.success(request, "Banque modifiée") return redirect(reverse('cotisations:index-banque')) return form({'factureform': banque, 'action_name' : 'Editer'}, 'cotisations/facture.html', request) diff --git a/machines/views.py b/machines/views.py index dcc43ff9..a3cfe209 100644 --- a/machines/views.py +++ b/machines/views.py @@ -277,9 +277,12 @@ def edit_interface(request, interface_instance, interfaceid): new_machine = machine_form.save(commit=False) new_interface = interface_form.save(commit=False) new_domain = domain_form.save(commit=False) - new_machine.save() - new_interface.save() - new_domain.save() + if machine_form.changed_data: + new_machine.save() + if interface_form.changed_data: + new_interface.save() + if domain_form.changed_data: + new_domain.save() messages.success(request, "La machine a été modifiée") return redirect(reverse( 'users:profil', @@ -375,8 +378,9 @@ def edit_ipv6list(request, ipv6list_instance, ipv6listid): """Edition d'une ipv6""" ipv6 = Ipv6ListForm(request.POST or None, instance=ipv6list_instance, user=request.user) if ipv6.is_valid(): - ipv6.save() - messages.success(request, "Ipv6 modifiée") + if ipv6.changed_data: + ipv6.save() + messages.success(request, "Ipv6 modifiée") return redirect(reverse( 'machines:index-ipv6', kwargs={'interfaceid':str(ipv6list_instance.interface.id)} @@ -416,8 +420,9 @@ def edit_iptype(request, iptype_instance, iptypeid): iptype = EditIpTypeForm(request.POST or None, instance=iptype_instance) if iptype.is_valid(): - iptype.save() - messages.success(request, "Type d'ip modifié") + if iptype.changed_data: + iptype.save() + messages.success(request, "Type d'ip modifié") return redirect(reverse('machines:index-iptype')) return form({'iptypeform': iptype, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -451,11 +456,11 @@ def add_machinetype(request): @login_required @can_edit(MachineType) def edit_machinetype(request, machinetype_instance, machinetypeid): - machinetype = MachineTypeForm(request.POST or None, instance=machinetype_instance) if machinetype.is_valid(): - machinetype.save() - messages.success(request, "Type de machine modifié") + if machinetype.changed_data: + machinetype.save() + messages.success(request, "Type de machine modifié") return redirect(reverse('machines:index-machinetype')) return form({'machinetypeform': machinetype, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -477,7 +482,6 @@ def del_machinetype(request, instances): @login_required @can_create(Extension) def add_extension(request): - extension = ExtensionForm(request.POST or None) if extension.is_valid(): extension.save() @@ -488,11 +492,11 @@ def add_extension(request): @login_required @can_edit(Extension) def edit_extension(request, extension_instance, extensionid): - extension = ExtensionForm(request.POST or None, instance=extension_instance) if extension.is_valid(): - extension.save() - messages.success(request, "Extension modifiée") + if extension.changed_data: + extension.save() + messages.success(request, "Extension modifiée") return redirect(reverse('machines:index-extension')) return form({'extensionform': extension, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -514,7 +518,6 @@ def del_extension(request, instances): @login_required @can_create(SOA) def add_soa(request): - soa = SOAForm(request.POST or None) if soa.is_valid(): soa.save() @@ -525,11 +528,11 @@ def add_soa(request): @login_required @can_edit(SOA) def edit_soa(request, soa_instance, soaid): - soa = SOAForm(request.POST or None, instance=soa_instance) if soa.is_valid(): - soa.save() - messages.success(request, "SOA modifié") + if soa.changed_data: + soa.save() + messages.success(request, "SOA modifié") return redirect(reverse('machines:index-extension')) return form({'soaform': soa, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -551,7 +554,6 @@ def del_soa(request, instances): @login_required @can_create(Mx) def add_mx(request): - mx = MxForm(request.POST or None) if mx.is_valid(): mx.save() @@ -562,11 +564,11 @@ def add_mx(request): @login_required @can_edit(Mx) def edit_mx(request, mx_instance, mxid): - mx = MxForm(request.POST or None, instance=mx_instance) if mx.is_valid(): - mx.save() - messages.success(request, "Mx modifié") + if mx.changed_data: + mx.save() + messages.success(request, "Mx modifié") return redirect(reverse('machines:index-extension')) return form({'mxform': mx, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -588,7 +590,6 @@ def del_mx(request, instances): @login_required @can_create(Ns) def add_ns(request): - ns = NsForm(request.POST or None) if ns.is_valid(): ns.save() @@ -599,11 +600,11 @@ def add_ns(request): @login_required @can_edit(Ns) def edit_ns(request, ns_instance, nsid): - ns = NsForm(request.POST or None, instance=ns_instance) if ns.is_valid(): - ns.save() - messages.success(request, "Ns modifié") + if ns.changed_data: + ns.save() + messages.success(request, "Ns modifié") return redirect(reverse('machines:index-extension')) return form({'nsform': ns, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -625,7 +626,6 @@ def del_ns(request, instances): @login_required @can_create(Txt) def add_txt(request): - txt = TxtForm(request.POST or None) if txt.is_valid(): txt.save() @@ -636,11 +636,11 @@ def add_txt(request): @login_required @can_edit(Txt) def edit_txt(request, txt_instance, txtid): - txt = TxtForm(request.POST or None, instance=txt_instance) if txt.is_valid(): - txt.save() - messages.success(request, "Txt modifié") + if txt.changed_data: + txt.save() + messages.success(request, "Txt modifié") return redirect(reverse('machines:index-extension')) return form({'txtform': txt, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -662,7 +662,6 @@ def del_txt(request, instances): @login_required @can_create(Srv) def add_srv(request): - srv = SrvForm(request.POST or None) if srv.is_valid(): srv.save() @@ -673,11 +672,11 @@ def add_srv(request): @login_required @can_edit(Srv) def edit_srv(request, srv_instance, srvid): - srv = SrvForm(request.POST or None, instance=srv_instance) if srv.is_valid(): - srv.save() - messages.success(request, "Srv modifié") + if srv.changed_data: + srv.save() + messages.success(request, "Srv modifié") return redirect(reverse('machines:index-extension')) return form({'srvform': srv, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -700,7 +699,6 @@ def del_srv(request, instances): @can_create(Domain) @can_edit(Interface) def add_alias(request, interface, interfaceid): - alias = AliasForm(request.POST or None, user=request.user) if alias.is_valid(): alias = alias.save(commit=False) @@ -716,11 +714,11 @@ def add_alias(request, interface, interfaceid): @login_required @can_edit(Domain) def edit_alias(request, domain_instance, domainid): - alias = AliasForm(request.POST or None, instance=domain_instance, user=request.user) if alias.is_valid(): - domain_instance = alias.save() - messages.success(request, "Alias modifié") + if alias.changed_data: + domain_instance = alias.save() + messages.success(request, "Alias modifié") return redirect(reverse( 'machines:index-alias', kwargs={'interfaceid':str(domain_instance.cname.interface_parent.id)} @@ -749,7 +747,6 @@ def del_alias(request, interface, interfaceid): @login_required @can_create(Service) def add_service(request): - service = ServiceForm(request.POST or None) if service.is_valid(): service.save() @@ -760,11 +757,11 @@ def add_service(request): @login_required @can_edit(Service) def edit_service(request, service_instance, serviceid): - service = ServiceForm(request.POST or None, instance=service_instance) if service.is_valid(): - service.save() - messages.success(request, "Service modifié") + if service.changed_data: + service.save() + messages.success(request, "Service modifié") return redirect(reverse('machines:index-service')) return form({'serviceform': service, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -786,7 +783,6 @@ def del_service(request, instances): @login_required @can_create(Vlan) def add_vlan(request): - vlan = VlanForm(request.POST or None) if vlan.is_valid(): vlan.save() @@ -797,11 +793,11 @@ def add_vlan(request): @login_required @can_edit(Vlan) def edit_vlan(request, vlan_instance, vlanid): - vlan = VlanForm(request.POST or None, instance=vlan_instance) if vlan.is_valid(): - vlan.save() - messages.success(request, "Vlan modifié") + if vlan.changed_data: + vlan.save() + messages.success(request, "Vlan modifié") return redirect(reverse('machines:index-vlan')) return form({'vlanform': vlan, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -823,7 +819,6 @@ def del_vlan(request, instances): @login_required @can_create(Nas) def add_nas(request): - nas = NasForm(request.POST or None) if nas.is_valid(): nas.save() @@ -834,11 +829,11 @@ def add_nas(request): @login_required @can_edit(Nas) def edit_nas(request, nas_instance, nasid): - nas = NasForm(request.POST or None, instance=nas_instance) if nas.is_valid(): - nas.save() - messages.success(request, "Nas modifié") + if nas.changed_data: + nas.save() + messages.success(request, "Nas modifié") return redirect(reverse('machines:index-nas')) return form({'nasform': nas, 'action_name' : 'Editer'}, 'machines/machine.html', request) @@ -951,7 +946,6 @@ def index_portlist(request): @login_required @can_edit(OuverturePortList) def edit_portlist(request, ouvertureportlist_instance, ouvertureportlistid): - port_list = EditOuverturePortListForm(request.POST or None, instance=ouvertureportlist_instance) port_formset = modelformset_factory( OuverturePort, @@ -962,7 +956,10 @@ def edit_portlist(request, ouvertureportlist_instance, ouvertureportlistid): validate_min=True, )(request.POST or None, queryset=ouvertureportlist_instance.ouvertureport_set.all()) if port_list.is_valid() and port_formset.is_valid(): - pl = port_list.save() + if port_list.changed_data: + pl = port_list.save() + else: + pl = ouvertureportlist_instance instances = port_formset.save(commit=False) for to_delete in port_formset.deleted_objects: to_delete.delete() @@ -983,7 +980,6 @@ def del_portlist(request, port_list_instance, ouvertureportlistid): @login_required @can_create(OuverturePortList) def add_portlist(request): - port_list = EditOuverturePortListForm(request.POST or None) port_formset = modelformset_factory( OuverturePort, @@ -1019,8 +1015,9 @@ def configure_ports(request, interface_instance, interfaceid): messages.error(request, "Attention, l'ipv4 n'est pas publique, l'ouverture n'aura pas d'effet en v4") interface = EditOuverturePortConfigForm(request.POST or None, instance=interface_instance) if interface.is_valid(): - interface.save() - messages.success(request, "Configuration des ports mise à jour.") + if interface.changed_data: + interface.save() + messages.success(request, "Configuration des ports mise à jour.") return redirect(reverse('machines:index')) return form({'interfaceform' : interface, 'action_name' : 'Editer la configuration'}, 'machines/machine.html', request) diff --git a/topologie/models.py b/topologie/models.py index 7e08917c..abbdfa7a 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -102,6 +102,9 @@ class AccessPoint(AclMixin, Machine): ("view_accesspoint", "Peut voir une borne"), ) + def __str__(self): + return str(self.interface_set.first()) + class Switch(AclMixin, Machine): """ Definition d'un switch. Contient un nombre de ports (number), diff --git a/users/views.py b/users/views.py index 3374fea9..be87dd06 100644 --- a/users/views.py +++ b/users/views.py @@ -152,8 +152,9 @@ def edit_club_admin_members(request, club_instance, clubid): membres d'un club""" club = ClubAdminandMembersForm(request.POST or None, instance=club_instance) if club.is_valid(): - club.save() - messages.success(request, "Le club a bien été modifié") + if club.changed_data: + club.save() + messages.success(request, "Le club a bien été modifié") return redirect(reverse( 'users:profil', kwargs={'userid':str(club_instance.id)} @@ -180,8 +181,9 @@ def edit_info(request, user, userid): user=request.user ) if user.is_valid(): - user.save() - messages.success(request, "L'user a bien été modifié") + if user.changed_data: + user.save() + messages.success(request, "L'user a bien été modifié") return redirect(reverse( 'users:profil', kwargs={'userid':str(userid)} @@ -202,8 +204,9 @@ def state(request, user, userid): user.unarchive() elif state.cleaned_data['state'] == User.STATE_DISABLED: user.state = User.STATE_DISABLED - user.save() - messages.success(request, "Etat changé avec succès") + if user.changed_data: + user.save() + messages.success(request, "Etat changé avec succès") return redirect(reverse( 'users:profil', kwargs={'userid':str(userid)} @@ -216,8 +219,9 @@ def state(request, user, userid): def groups(request, user, userid): group = GroupForm(request.POST or None, instance=user) if group.is_valid(): - group.save() - messages.success(request, "Groupes changés avec succès") + if group.changed_data: + group.save() + messages.success(request, "Groupes changés avec succès") return redirect(reverse( 'users:profil', kwargs={'userid':str(userid)} @@ -233,8 +237,9 @@ def password(request, user, userid): pour tous si droit bureau """ u_form = PassForm(request.POST or None, instance=user, user=request.user) if u_form.is_valid(): - u_form.save() - messages.success(request, "Le mot de passe a changé") + if u_form.changed_data: + u_form.save() + messages.success(request, "Le mot de passe a changé") return redirect(reverse( 'users:profil', kwargs={'userid':str(user.id)} @@ -277,7 +282,8 @@ def edit_serviceuser(request, user, userid): user_object = user.save(commit=False) if user.cleaned_data['password']: user_object.set_password(user.cleaned_data['password']) - user_object.save() + if user.changed_data: + user_object.save() messages.success(request, "L'user a bien été modifié") return redirect(reverse('users:index-serviceusers')) return form({'userform': user, 'action_name':'Editer un serviceuser'}, 'users/user.html', request) @@ -329,8 +335,9 @@ def edit_ban(request, ban_instance, banid): Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement""" ban = BanForm(request.POST or None, instance=ban_instance) if ban.is_valid(): - ban.save() - messages.success(request, "Bannissement modifié") + if ban.changed_data: + ban.save() + messages.success(request, "Bannissement modifié") return redirect(reverse('users:index')) return form({'userform': ban, 'action_name': 'Editer un ban'}, 'users/user.html', request) @@ -375,8 +382,9 @@ def edit_whitelist(request, whitelist_instance, whitelistid): instance=whitelist_instance ) if whitelist.is_valid(): - whitelist.save() - messages.success(request, "Whitelist modifiée") + if whitelist.changed_data: + whitelist.save() + messages.success(request, "Whitelist modifiée") return redirect(reverse('users:index')) return form({'userform': whitelist, 'action_name': 'Editer une whitelist'}, 'users/user.html', request) @@ -401,8 +409,9 @@ def edit_school(request, school_instance, schoolid): la base de donnée, need cableur""" school = SchoolForm(request.POST or None, instance=school_instance) if school.is_valid(): - school.save() - messages.success(request, "Établissement modifié") + if school.changed_data: + school.save() + messages.success(request, "Établissement modifié") return redirect(reverse('users:index-school')) return form({'userform': school, 'action_name':'Editer'}, 'users/user.html', request) @@ -448,8 +457,9 @@ def edit_shell(request, shell_instance, listshellid): """ Editer un shell à partir du listshellid""" shell = ShellForm(request.POST or None, instance=shell_instance) if shell.is_valid(): - shell.save() - messages.success(request, "Le shell a été modifié") + if shell.changed_data: + shell.save() + messages.success(request, "Le shell a été modifié") return redirect(reverse('users:index-shell')) return form({'userform': shell, 'action_name':'Editer'}, 'users/user.html', request) @@ -492,8 +502,9 @@ def edit_listright(request, listright_instance, listrightid): instance=listright_instance ) if listright.is_valid(): - listright.save() - messages.success(request, "Droit modifié") + if listright.changed_data: + listright.save() + messages.success(request, "Droit modifié") return redirect(reverse('users:index-listright')) return form({'userform': listright, 'action_name': 'Editer'}, 'users/user.html', request) From 513933abbac7f1a92c3edb1c3fe3457644039ad6 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Mon, 2 Apr 2018 03:35:07 +0200 Subject: [PATCH 06/11] Register club et adherent admin pour histo --- users/admin.py | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/users/admin.py b/users/admin.py index f8acb855..a6b70009 100644 --- a/users/admin.py +++ b/users/admin.py @@ -32,11 +32,28 @@ from django.contrib.auth.models import Group from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from reversion.admin import VersionAdmin -from .models import User, ServiceUser, School, ListRight, ListShell -from .models import Ban, Whitelist, Request, LdapUser, LdapServiceUser -from .models import LdapServiceUserGroup, LdapUserGroup -from .forms import UserChangeForm, UserCreationForm -from .forms import ServiceUserChangeForm, ServiceUserCreationForm +from .models import ( + User, + ServiceUser, + School, + ListRight, + ListShell, + Adherent, + Club, + Ban, + Whitelist, + Request, + LdapUser, + LdapServiceUser, + LdapServiceUserGroup, + LdapUserGroup +) +from .forms import ( + UserChangeForm, + UserCreationForm, + ServiceUserChangeForm, + ServiceUserCreationForm +) class UserAdmin(admin.ModelAdmin): @@ -195,6 +212,8 @@ class ServiceUserAdmin(VersionAdmin, BaseUserAdmin): admin.site.register(User, UserAdmin) +admin.site.register(Adherent, UserAdmin) +admin.site.register(Club, UserAdmin) admin.site.register(ServiceUser, ServiceUserAdmin) admin.site.register(LdapUser, LdapUserAdmin) admin.site.register(LdapUserGroup, LdapUserGroupAdmin) From 5596abf4df3db01dfed49a0dffa26dd2be8d905f Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Mon, 2 Apr 2018 03:36:34 +0200 Subject: [PATCH 07/11] =?UTF-8?q?Affichage=20des=20objets=20li=C3=A9s=20qu?= =?UTF-8?q?and=20c'est=20necessaire=20(interfaces-machines-domain)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- re2o/views.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/re2o/views.py b/re2o/views.py index 9965dcda..6aa791f4 100644 --- a/re2o/views.py +++ b/re2o/views.py @@ -40,6 +40,7 @@ from django.conf import settings from contributors import contributeurs import os import time +from itertools import chain import users, preferences, cotisations, topologie, machines def form(ctx, template, request): @@ -146,6 +147,9 @@ def history(request, application, object_name, object_id): )) pagination_number = GeneralOption.get_cached_value('pagination_number') reversions = Version.objects.get_for_object(instance) + if hasattr(instance, 'linked_objects'): + for related_object in chain(instance.linked_objects()): + reversions = reversions | Version.objects.get_for_object(related_object) paginator = Paginator(reversions, pagination_number) page = request.GET.get('page') try: From 88b003d0fe3b948f883a8833af12bb1c97ec40e0 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Mon, 2 Apr 2018 03:37:17 +0200 Subject: [PATCH 08/11] =?UTF-8?q?Remplace=20"cableur"=20par=20effectu?= =?UTF-8?q?=C3=A9=20par?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- re2o/templates/re2o/aff_history.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/re2o/templates/re2o/aff_history.html b/re2o/templates/re2o/aff_history.html index 8f143522..d7be350c 100644 --- a/re2o/templates/re2o/aff_history.html +++ b/re2o/templates/re2o/aff_history.html @@ -41,7 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Date - Cableur + Effectué par Commentaire From 6db387caf8b015e830fa117cf896b2c107e6f4b1 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Mon, 2 Apr 2018 03:52:15 +0200 Subject: [PATCH 09/11] Linked_objects + fix modif formset --- cotisations/models.py | 5 +++++ cotisations/views.py | 3 +-- machines/models.py | 6 ++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cotisations/models.py b/cotisations/models.py index 10aa868e..cdf73a39 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -88,6 +88,11 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): ("change_all_facture", "Superdroit, peut modifier toutes les factures"), ) + def linked_objects(self): + """Return linked objects : machine and domain. + Usefull in history display""" + return self.vente_set.all() + def prix(self): """Renvoie le prix brut sans les quantités. Méthode dépréciée""" diff --git a/cotisations/views.py b/cotisations/views.py index 6bf58994..b78a82f5 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -251,8 +251,7 @@ def edit_facture(request, facture, factureid): if facture_form.is_valid() and vente_form.is_valid(): if facture_form.changed_data: facture_form.save() - if vente_form.changed_data: - vente_form.save() + vente_form.save() messages.success(request, "La facture a bien été modifiée") return redirect(reverse('cotisations:index')) return form({ diff --git a/machines/models.py b/machines/models.py index c7d9fe83..dc2dba65 100644 --- a/machines/models.py +++ b/machines/models.py @@ -27,6 +27,7 @@ from datetime import timedelta import re from netaddr import mac_bare, EUI, IPSet, IPRange, IPNetwork, IPAddress from ipaddress import IPv6Address +from itertools import chain from django.db import models from django.db.models.signals import post_save, post_delete @@ -72,6 +73,11 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): """ return Machine.objects.get(pk=machineid) + def linked_objects(self): + """Return linked objects : machine and domain. + Usefull in history display""" + return chain(self.interface_set.all(), Domain.objects.filter(interface_parent__in=self.interface_set.all())) + @staticmethod def can_change_user(user_request, *args, **kwargs): """Checks if an user is allowed to change the user who owns a From f39ae95b35498b81f028541d7084926e55a398bd Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Mon, 2 Apr 2018 04:31:04 +0200 Subject: [PATCH 10/11] Fix historique alias --- machines/templates/machines/aff_alias.html | 2 +- re2o/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/machines/templates/machines/aff_alias.html b/machines/templates/machines/aff_alias.html index fb3f0486..f19b6482 100644 --- a/machines/templates/machines/aff_alias.html +++ b/machines/templates/machines/aff_alias.html @@ -38,7 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% can_edit alias %} {% include 'buttons/edit.html' with href='machines:edit-alias' id=alias.id %} {% acl_end %} - {% include 'buttons/history.html' with href='machines:history' name='alias' id=alias.id %} + {% include 'buttons/history.html' with href='machines:history' name='domain' id=alias.id %} {% endfor %} diff --git a/re2o/views.py b/re2o/views.py index 6aa791f4..83cd7630 100644 --- a/re2o/views.py +++ b/re2o/views.py @@ -89,7 +89,7 @@ HISTORY_BIND = { 'machines' : { 'machine' : machines.models.Machine, 'interface' : machines.models.Interface, - 'alias' : machines.models.Domain, + 'domain' : machines.models.Domain, 'machinetype' : machines.models.MachineType, 'iptype' : machines.models.IpType, 'extension' : machines.models.Extension, From cf9d37dc541018195e61e573d2e3f50f08e8b60e Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Mon, 2 Apr 2018 20:12:53 +0200 Subject: [PATCH 11/11] affichage des ports plus naturel --- topologie/templates/topologie/aff_port.html | 59 +++++++++++++++++++++ topologie/templates/topologie/index_p.html | 3 +- users/models.py | 55 +++++++++---------- 3 files changed, 87 insertions(+), 30 deletions(-) diff --git a/topologie/templates/topologie/aff_port.html b/topologie/templates/topologie/aff_port.html index 1eef75b8..8a1268a3 100644 --- a/topologie/templates/topologie/aff_port.html +++ b/topologie/templates/topologie/aff_port.html @@ -24,6 +24,64 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load acl %} + +
+ + + + + {% for port in port_list|slice:"::2" %} + + {% endfor %} + + + + + {% for port in port_list|slice:"::2" %} + + {% endfor %} + + + + + + {% for port in port_list|slice:"1::2" %} + + {% endfor %} + + + + + {% for port in port_list|slice:"1::2" %} + + {% endfor %} + + +
{{ port.port }}
+ {% if port.room %} + {{ port.room }} + {% elif port.machine_interface %} + {{ port.machine_interface }} + {% elif port.related%} + {{ port.related }} + {% else %} + Vide + {% endif %} +
{{ port.port }}
+ {% if port.room %} + {{ port.room }} + {% elif port.machine_interface %} + {{ port.machine_interface }} + {% elif port.related%} + {{ port.related }} + {% else %} + Vide + {% endif %} +
+
+ + +
@@ -76,3 +134,4 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endfor %}
+
diff --git a/topologie/templates/topologie/index_p.html b/topologie/templates/topologie/index_p.html index 4fc1a61d..0bd62039 100644 --- a/topologie/templates/topologie/index_p.html +++ b/topologie/templates/topologie/index_p.html @@ -35,7 +35,8 @@ with this program; if not, write to the Free Software Foundation, Inc., Ajouter un port Ajouter des ports {% acl_end %} - {% include "topologie/aff_port.html" with port_list=port_list %} +
+{% include "topologie/aff_port.html" with port_list=port_list %}


diff --git a/users/models.py b/users/models.py index caa03ce3..8b4b11c9 100644 --- a/users/models.py +++ b/users/models.py @@ -548,15 +548,15 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix 'welcome_mail_en': mailmessageoptions.welcome_mail_en, 'pseudo': self.pseudo, }) - send_mail( - 'Bienvenue au %(name)s / Welcome to %(name)s' % { - 'name': AssoOption.get_cached_value('name') - }, - '', - GeneralOption.get_cached_value('email_from'), - [self.email], - html_message=template.render(context) - ) + #send_mail( + # 'Bienvenue au %(name)s / Welcome to %(name)s' % { + # 'name': AssoOption.get_cached_value('name') + # }, + # '', + # GeneralOption.get_cached_value('email_from'), + # [self.email], + # html_message=template.render(context) + #) return def reset_passwd_mail(self, request): @@ -576,14 +576,14 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix reverse('users:process', kwargs={'token': req.token})), 'expire_in': str(GeneralOption.get_cached_value('req_expire_hrs')) + ' heures', } - send_mail( - 'Changement de mot de passe du %(name)s / Password\ - renewal for %(name)s' % {'name': AssoOption.get_cached_value('name')}, - template.render(context), - GeneralOption.get_cached_value('email_from'), - [req.user.email], - fail_silently=False - ) + #send_mail( + # 'Changement de mot de passe du %(name)s / Password\ + # renewal for %(name)s' % {'name': AssoOption.get_cached_value('name')}, + # template.render(context), + # GeneralOption.get_cached_value('email_from'), + # [req.user.email], + # fail_silently=False + #) return def autoregister_machine(self, mac_address, nas_type): @@ -892,8 +892,8 @@ def user_post_save(sender, **kwargs): Synchronise le ldap""" is_created = kwargs['created'] user = kwargs['instance'] - if is_created: - user.notif_inscription() + #if is_created: + #user.notif_inscription() user.ldap_sync(base=True, access_refresh=True, mac_refresh=False, group_refresh=True) regen('mailing') @@ -1127,22 +1127,19 @@ class Ban(RevMixin, AclMixin, models.Model): 'date_end': self.date_end, 'asso_name': AssoOption.get_cached_value('name'), }) - send_mail( - 'Deconnexion disciplinaire', - template.render(context), - GeneralOption.get_cached_value('email_from'), - [self.user.email], - fail_silently=False - ) + #send_mail( + # 'Deconnexion disciplinaire', + # template.render(context), + # GeneralOption.get_cached_value('email_from'), + # [self.user.email], + # fail_silently=False + #) return def is_active(self): """Ce ban est-il actif?""" return self.date_end > timezone.now() - def get_instance(banid, *args, **kwargs): - return Ban.objects.get(pk=banid) - def can_view(self, user_request, *args, **kwargs): """Check if an user can view a Ban object.