mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-23 11:53:12 +00:00
Merge branch 'profil_frontend' into 'master'
Profil frontend See merge request federez/re2o!111
This commit is contained in:
commit
39a1c4eb86
29 changed files with 1232 additions and 841 deletions
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
@ -87,6 +88,11 @@ class Facture(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"""
|
||||
|
@ -180,7 +186,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 +331,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 +387,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 +402,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 +432,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"
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@ import os
|
|||
|
||||
from django.urls import reverse
|
||||
from django.shortcuts import render, redirect
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from django.core.validators import MaxValueValidator
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.contrib import messages
|
||||
|
@ -39,14 +38,13 @@ 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 reversion import revisions as reversion
|
||||
from users.models import User
|
||||
from re2o.settings import LOGO_PATH
|
||||
from re2o import settings
|
||||
from re2o.views import form
|
||||
from re2o.utils import SortTable
|
||||
from re2o.utils import SortTable, re2o_paginator
|
||||
from re2o.acl import (
|
||||
can_create,
|
||||
can_edit,
|
||||
|
@ -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")
|
||||
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")
|
||||
if any(art_item.cleaned_data['article'].type_cotisation
|
||||
for art_item in articles if art_item.cleaned_data):
|
||||
messages.success(
|
||||
|
@ -257,13 +249,9 @@ 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():
|
||||
if facture_form.changed_data:
|
||||
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))
|
||||
messages.success(request, "La facture a bien été modifiée")
|
||||
return redirect(reverse('cotisations:index'))
|
||||
return form({
|
||||
|
@ -278,9 +266,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)
|
||||
messages.success(request, "La facture a été détruite")
|
||||
return redirect(reverse('cotisations:index'))
|
||||
return form({
|
||||
|
@ -297,21 +283,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")
|
||||
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")
|
||||
messages.success(request, "Solde modifié")
|
||||
return redirect(reverse('cotisations:index'))
|
||||
return form({'factureform': facture, 'action_name' : 'Créditer'}, 'cotisations/facture.html', request)
|
||||
|
@ -329,10 +309,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")
|
||||
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 +322,8 @@ 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():
|
||||
if article.changed_data:
|
||||
article.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment(
|
||||
"Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in article.changed_data
|
||||
)
|
||||
)
|
||||
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 +336,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)
|
||||
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 +349,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")
|
||||
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 +361,8 @@ 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():
|
||||
if paiement.changed_data:
|
||||
paiement.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment(
|
||||
"Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in paiement.changed_data
|
||||
)
|
||||
)
|
||||
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 +377,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")
|
||||
messages.success(
|
||||
request,
|
||||
"Le moyen de paiement a été supprimé"
|
||||
|
@ -441,10 +398,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")
|
||||
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 +410,8 @@ 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():
|
||||
if banque.changed_data:
|
||||
banque.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment(
|
||||
"Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in banque.changed_data
|
||||
)
|
||||
)
|
||||
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 +426,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")
|
||||
messages.success(request, "La banque a été supprimée")
|
||||
except ProtectedError:
|
||||
messages.error(request, "La banque %s est affectée à au moins\
|
||||
|
@ -509,20 +454,11 @@ def control(request):
|
|||
fields=('control', 'valid'),
|
||||
extra=0
|
||||
)
|
||||
paginator = Paginator(facture_list, pagination_number)
|
||||
page = request.GET.get('page')
|
||||
try:
|
||||
facture_list = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
facture_list = paginator.page(1)
|
||||
except EmptyPage:
|
||||
facture_list = paginator.page(paginator.num.pages)
|
||||
facture_list = re2o_paginator(request, facture_list, pagination_number)
|
||||
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")
|
||||
reversion.set_comment("Controle")
|
||||
return redirect(reverse('cotisations:control'))
|
||||
return render(request, 'cotisations/control.html', {
|
||||
'facture_list': facture_list,
|
||||
|
@ -573,16 +509,7 @@ def index(request):
|
|||
request.GET.get('order'),
|
||||
SortTable.COTISATIONS_INDEX
|
||||
)
|
||||
paginator = Paginator(facture_list, pagination_number)
|
||||
page = request.GET.get('page')
|
||||
try:
|
||||
facture_list = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
# If page is not an integer, deliver first page.
|
||||
facture_list = paginator.page(1)
|
||||
except EmptyPage:
|
||||
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||
facture_list = paginator.page(paginator.num_pages)
|
||||
facture_list = re2o_paginator(request, facture_list, pagination_number)
|
||||
return render(request, 'cotisations/index.html', {
|
||||
'facture_list': facture_list
|
||||
})
|
||||
|
@ -630,10 +557,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")
|
||||
for art_item in articles:
|
||||
if art_item.cleaned_data:
|
||||
article = art_item.cleaned_data['article']
|
||||
|
@ -646,10 +570,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")
|
||||
if any(art_item.cleaned_data['article'].type_cotisation
|
||||
for art_item in articles if art_item.cleaned_data):
|
||||
messages.success(
|
||||
|
|
|
@ -39,7 +39,6 @@ from __future__ import unicode_literals
|
|||
|
||||
from django.urls import reverse
|
||||
from django.shortcuts import render, redirect
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db.models import Count, Max
|
||||
|
@ -100,6 +99,7 @@ from re2o.utils import (
|
|||
all_baned,
|
||||
all_has_access,
|
||||
all_adherent,
|
||||
re2o_paginator,
|
||||
)
|
||||
from re2o.acl import (
|
||||
can_view_all,
|
||||
|
@ -139,17 +139,7 @@ def index(request):
|
|||
request.GET.get('order'),
|
||||
SortTable.LOGS_INDEX
|
||||
)
|
||||
paginator = Paginator(versions, pagination_number)
|
||||
page = request.GET.get('page')
|
||||
try:
|
||||
versions = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
# If page is not an integer, deliver first page.
|
||||
versions = paginator.page(1)
|
||||
except EmptyPage:
|
||||
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||
versions = paginator.page(paginator.num_pages)
|
||||
|
||||
versions = re2o_paginator(request, versions, pagination_number)
|
||||
# Force to have a list instead of QuerySet
|
||||
versions.count(0)
|
||||
# Items to remove later because invalid
|
||||
|
@ -191,16 +181,7 @@ def stats_logs(request):
|
|||
request.GET.get('order'),
|
||||
SortTable.LOGS_STATS_LOGS
|
||||
)
|
||||
paginator = Paginator(revisions, pagination_number)
|
||||
page = request.GET.get('page')
|
||||
try:
|
||||
revisions = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
# If page is not an integer, deliver first page.
|
||||
revisions = paginator.page(1)
|
||||
except EmptyPage:
|
||||
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||
revisions = paginator.page(paginator.num_pages)
|
||||
revisions = re2o_paginator(request, revisions, pagination_number)
|
||||
return render(request, 'logs/stats_logs.html', {
|
||||
'revisions_list': revisions
|
||||
})
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
@ -39,13 +40,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"
|
||||
|
@ -72,6 +73,11 @@ class Machine(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
|
||||
|
@ -163,7 +169,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 +209,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 +339,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 +357,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 +396,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 +473,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 +536,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 +561,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 +582,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 +605,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 +667,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 +914,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 +1018,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 +1176,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 +1208,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 +1262,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 +1293,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 +1352,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.
|
||||
|
||||
|
|
|
@ -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 %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -33,13 +33,11 @@ from django.http import HttpResponse
|
|||
from django.shortcuts import render, redirect
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.template.context_processors import csrf
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from django.template import Context, RequestContext, loader
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.db.models import 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
|
||||
|
||||
|
@ -124,6 +122,7 @@ from re2o.utils import (
|
|||
all_has_access,
|
||||
filter_active_interfaces,
|
||||
SortTable,
|
||||
re2o_paginator,
|
||||
)
|
||||
from re2o.acl import (
|
||||
can_create,
|
||||
|
@ -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_interface.machine = new_machine
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_interface.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Création")
|
||||
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")
|
||||
messages.success(request, "La machine a été créée")
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
|
@ -287,18 +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)
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if machine_form.changed_data:
|
||||
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():
|
||||
if interface_form.changed_data:
|
||||
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():
|
||||
if domain_form.changed_data:
|
||||
new_domain.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in domain_form.changed_data))
|
||||
messages.success(request, "La machine a été modifiée")
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
|
@ -318,9 +302,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)
|
||||
messages.success(request, "La machine a été détruite")
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
|
@ -342,15 +324,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_domain.interface_parent = new_interface
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_domain.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Création")
|
||||
messages.success(request, "L'interface a été ajoutée")
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
|
@ -370,11 +346,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)
|
||||
messages.success(request, "L'interface a été détruite")
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
|
@ -390,10 +364,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")
|
||||
messages.success(request, "Ipv6 ajoutée")
|
||||
return redirect(reverse(
|
||||
'machines:index-ipv6',
|
||||
|
@ -407,10 +378,8 @@ 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():
|
||||
if ipv6.changed_data:
|
||||
ipv6.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in ipv6.changed_data))
|
||||
messages.success(request, "Ipv6 modifiée")
|
||||
return redirect(reverse(
|
||||
'machines:index-ipv6',
|
||||
|
@ -424,9 +393,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)
|
||||
messages.success(request, "L'ipv6 a été détruite")
|
||||
return redirect(reverse(
|
||||
'machines:index-ipv6',
|
||||
|
@ -441,10 +408,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")
|
||||
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 +420,8 @@ 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():
|
||||
if iptype.changed_data:
|
||||
iptype.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in iptype.changed_data))
|
||||
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 +435,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)
|
||||
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 +448,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")
|
||||
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)
|
||||
|
@ -499,13 +456,10 @@ 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():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if machinetype.changed_data:
|
||||
machinetype.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in machinetype.changed_data))
|
||||
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 +472,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)
|
||||
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)
|
||||
|
@ -530,13 +482,9 @@ def del_machinetype(request, instances):
|
|||
@login_required
|
||||
@can_create(Extension)
|
||||
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")
|
||||
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)
|
||||
|
@ -544,13 +492,10 @@ 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():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if extension.changed_data:
|
||||
extension.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in extension.changed_data))
|
||||
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 +508,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)
|
||||
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)
|
||||
|
@ -575,13 +518,9 @@ def del_extension(request, instances):
|
|||
@login_required
|
||||
@can_create(SOA)
|
||||
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")
|
||||
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)
|
||||
|
@ -589,13 +528,10 @@ 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():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if soa.changed_data:
|
||||
soa.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in soa.changed_data))
|
||||
messages.success(request, "SOA modifié")
|
||||
return redirect(reverse('machines:index-extension'))
|
||||
return form({'soaform': soa, 'action_name' : 'Editer'}, 'machines/machine.html', request)
|
||||
|
@ -608,9 +544,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)
|
||||
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)
|
||||
|
@ -620,13 +554,9 @@ def del_soa(request, instances):
|
|||
@login_required
|
||||
@can_create(Mx)
|
||||
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")
|
||||
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)
|
||||
|
@ -634,13 +564,10 @@ 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():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if mx.changed_data:
|
||||
mx.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in mx.changed_data))
|
||||
messages.success(request, "Mx modifié")
|
||||
return redirect(reverse('machines:index-extension'))
|
||||
return form({'mxform': mx, 'action_name' : 'Editer'}, 'machines/machine.html', request)
|
||||
|
@ -653,9 +580,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)
|
||||
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)
|
||||
|
@ -665,13 +590,9 @@ def del_mx(request, instances):
|
|||
@login_required
|
||||
@can_create(Ns)
|
||||
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")
|
||||
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)
|
||||
|
@ -679,13 +600,10 @@ 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():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if ns.changed_data:
|
||||
ns.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in ns.changed_data))
|
||||
messages.success(request, "Ns modifié")
|
||||
return redirect(reverse('machines:index-extension'))
|
||||
return form({'nsform': ns, 'action_name' : 'Editer'}, 'machines/machine.html', request)
|
||||
|
@ -698,9 +616,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)
|
||||
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)
|
||||
|
@ -710,13 +626,9 @@ def del_ns(request, instances):
|
|||
@login_required
|
||||
@can_create(Txt)
|
||||
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")
|
||||
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)
|
||||
|
@ -724,13 +636,10 @@ 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():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if txt.changed_data:
|
||||
txt.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in txt.changed_data))
|
||||
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)
|
||||
messages.success(request, "Le txt a été supprimé")
|
||||
except ProtectedError:
|
||||
messages.error(request, "Erreur le Txt suivant %s ne peut être supprimé" % txt_del)
|
||||
|
@ -755,13 +662,9 @@ def del_txt(request, instances):
|
|||
@login_required
|
||||
@can_create(Srv)
|
||||
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")
|
||||
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)
|
||||
|
@ -769,13 +672,10 @@ 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():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if srv.changed_data:
|
||||
srv.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in srv.changed_data))
|
||||
messages.success(request, "Srv modifié")
|
||||
return redirect(reverse('machines:index-extension'))
|
||||
return form({'srvform': srv, 'action_name' : 'Editer'}, 'machines/machine.html', request)
|
||||
|
@ -788,9 +688,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)
|
||||
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)
|
||||
|
@ -801,15 +699,11 @@ 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)
|
||||
alias.cname = interface.domain
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
alias.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Création")
|
||||
messages.success(request, "Cet alias a été ajouté")
|
||||
return redirect(reverse(
|
||||
'machines:index-alias',
|
||||
|
@ -820,13 +714,10 @@ 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():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if alias.changed_data:
|
||||
domain_instance = alias.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in alias.changed_data))
|
||||
messages.success(request, "Alias modifié")
|
||||
return redirect(reverse(
|
||||
'machines:index-alias',
|
||||
|
@ -842,9 +733,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)
|
||||
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)
|
||||
|
@ -858,13 +747,9 @@ 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():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
service.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Création")
|
||||
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)
|
||||
|
@ -872,13 +757,10 @@ 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():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if service.changed_data:
|
||||
service.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in service.changed_data))
|
||||
messages.success(request, "Service modifié")
|
||||
return redirect(reverse('machines:index-service'))
|
||||
return form({'serviceform': service, 'action_name' : 'Editer'}, 'machines/machine.html', request)
|
||||
|
@ -891,9 +773,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)
|
||||
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)
|
||||
|
@ -903,13 +783,9 @@ def del_service(request, instances):
|
|||
@login_required
|
||||
@can_create(Vlan)
|
||||
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")
|
||||
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)
|
||||
|
@ -917,13 +793,10 @@ 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():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if vlan.changed_data:
|
||||
vlan.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in vlan.changed_data))
|
||||
messages.success(request, "Vlan modifié")
|
||||
return redirect(reverse('machines:index-vlan'))
|
||||
return form({'vlanform': vlan, 'action_name' : 'Editer'}, 'machines/machine.html', request)
|
||||
|
@ -936,9 +809,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)
|
||||
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)
|
||||
|
@ -948,13 +819,9 @@ def del_vlan(request, instances):
|
|||
@login_required
|
||||
@can_create(Nas)
|
||||
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")
|
||||
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)
|
||||
|
@ -962,13 +829,10 @@ 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():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if nas.changed_data:
|
||||
nas.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in nas.changed_data))
|
||||
messages.success(request, "Nas modifié")
|
||||
return redirect(reverse('machines:index-nas'))
|
||||
return form({'nasform': nas, 'action_name' : 'Editer'}, 'machines/machine.html', request)
|
||||
|
@ -981,9 +845,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)
|
||||
messages.success(request, "Le nas a été supprimé")
|
||||
except ProtectedError:
|
||||
messages.error(request, "Erreur le Nas suivant %s ne peut être supprimé" % nas_del)
|
||||
|
@ -1001,16 +863,7 @@ def index(request):
|
|||
request.GET.get('order'),
|
||||
SortTable.MACHINES_INDEX
|
||||
)
|
||||
paginator = Paginator(machines_list, pagination_large_number)
|
||||
page = request.GET.get('page')
|
||||
try:
|
||||
machines_list = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
# If page is not an integer, deliver first page.
|
||||
machines_list = paginator.page(1)
|
||||
except EmptyPage:
|
||||
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||
machines_list = paginator.page(paginator.num_pages)
|
||||
machines_list = re2o_paginator(request, machines_list, pagination_large_number)
|
||||
return render(request, 'machines/index.html', {'machines_list': machines_list})
|
||||
|
||||
@login_required
|
||||
|
@ -1084,7 +937,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,
|
||||
|
@ -1095,7 +947,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():
|
||||
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()
|
||||
|
@ -1116,7 +971,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,
|
||||
|
@ -1152,6 +1006,7 @@ 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():
|
||||
if interface.changed_data:
|
||||
interface.save()
|
||||
messages.success(request, "Configuration des ports mise à jour.")
|
||||
return redirect(reverse('machines:index'))
|
||||
|
|
|
@ -32,7 +32,6 @@ from __future__ import unicode_literals
|
|||
|
||||
from django.urls import reverse
|
||||
from django.shortcuts import render, redirect
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.db.models import ProtectedError
|
||||
|
|
|
@ -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:
|
||||
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
|
||||
|
|
86
re2o/script_utils.py
Normal file
86
re2o/script_utils.py
Normal file
|
@ -0,0 +1,86 @@
|
|||
# ⁻*- mode: python; coding: utf-8 -*-
|
||||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||
# quelques clics.
|
||||
#
|
||||
# Copyright © 2018 Lev-Arcady Sellem
|
||||
#
|
||||
# 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
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import os, sys, pwd
|
||||
|
||||
proj_path="/var/www/re2o"
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE","re2o.settings")
|
||||
sys.path.append(proj_path)
|
||||
os.chdir(proj_path)
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
application = get_wsgi_application()
|
||||
|
||||
|
||||
from django.core.management.base import CommandError
|
||||
from users.models import User
|
||||
|
||||
from django.utils.html import strip_tags
|
||||
from reversion import revisions as reversion
|
||||
from django.db import transaction
|
||||
from getpass import getpass
|
||||
|
||||
|
||||
def get_user(pseudo):
|
||||
"""Cherche un utilisateur re2o à partir de son pseudo"""
|
||||
user = User.objects.filter(pseudo=pseudo)
|
||||
if len(user)==0:
|
||||
raise CommandError("Utilisateur invalide")
|
||||
if len(user)>1:
|
||||
raise CommandError("Plusieurs utilisateurs correspondant à ce pseudo. Ceci NE DEVRAIT PAS arriver")
|
||||
return user[0]
|
||||
|
||||
|
||||
def get_system_user():
|
||||
"""Retourne l'utilisateur système ayant lancé la commande"""
|
||||
return pwd.getpwuid(int(os.getenv("SUDO_UID") or os.getuid())).pw_name
|
||||
|
||||
|
||||
def form_cli(Form,user,action,*args,**kwargs):
|
||||
"""
|
||||
Remplit un formulaire à partir de la ligne de commande
|
||||
Form : le formulaire (sous forme de classe) à remplir
|
||||
user : l'utilisateur re2o faisant la modification
|
||||
action : l'action réalisée par le formulaire (pour les logs)
|
||||
Les arguments suivants sont transmis tels quels au formulaire.
|
||||
"""
|
||||
data={}
|
||||
dumb_form = Form(user=user,*args,**kwargs)
|
||||
for key in dumb_form.fields:
|
||||
if not dumb_form.fields[key].widget.input_type=='hidden':
|
||||
if dumb_form.fields[key].widget.input_type=='password':
|
||||
data[key]=getpass("%s : " % dumb_form.fields[key].label)
|
||||
else:
|
||||
data[key]=input("%s : " % dumb_form.fields[key].label)
|
||||
|
||||
form = Form(data,user=user,*args,**kwargs)
|
||||
if not form.is_valid():
|
||||
sys.stderr.write("Erreurs : \n")
|
||||
for err in form.errors:
|
||||
#Oui, oui, on gère du HTML là où d'autres ont eu la lumineuse idée de le mettre
|
||||
sys.stderr.write("\t%s : %s\n" % (err,strip_tags(form.errors[err])))
|
||||
raise CommandError("Formulaire invalide")
|
||||
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
form.save()
|
||||
reversion.set_user(user)
|
||||
reversion.set_comment(action)
|
||||
|
||||
sys.stdout.write("%s : effectué. La modification peut prendre quelques minutes pour s'appliquer.\n" % action)
|
|
@ -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'
|
||||
|
|
|
@ -41,7 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Cableur</th>
|
||||
<th>Effectué par</th>
|
||||
<th>Commentaire</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
|
@ -42,6 +42,7 @@ from django.db.models import Q
|
|||
from django.contrib import messages
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
|
||||
from cotisations.models import Cotisation, Facture, Paiement, Vente
|
||||
from machines.models import Domain, Interface, Machine
|
||||
|
@ -280,6 +281,22 @@ class SortTable:
|
|||
else:
|
||||
return request
|
||||
|
||||
def re2o_paginator(request, query_set, pagination_number):
|
||||
"""Paginator script for list display in re2o.
|
||||
:request:
|
||||
:query_set: Query_set to paginate
|
||||
:pagination_number: Number of entries to display"""
|
||||
paginator = Paginator(query_set, pagination_number)
|
||||
page = request.GET.get('page')
|
||||
try:
|
||||
results = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
# If page is not an integer, deliver first page.
|
||||
results = paginator.page(1)
|
||||
except EmptyPage:
|
||||
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||
results = paginator.page(paginator.num_pages)
|
||||
return results
|
||||
|
||||
def remove_user_room(room):
|
||||
""" Déménage de force l'ancien locataire de la chambre """
|
||||
|
|
|
@ -31,7 +31,6 @@ from django.urls import reverse
|
|||
from django.shortcuts import render, redirect
|
||||
from django.template.context_processors import csrf
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from reversion.models import Version
|
||||
from django.contrib import messages
|
||||
from preferences.models import Service
|
||||
|
@ -40,7 +39,9 @@ from django.conf import settings
|
|||
from contributors import contributeurs
|
||||
import os
|
||||
import time
|
||||
from itertools import chain
|
||||
import users, preferences, cotisations, topologie, machines
|
||||
from .utils import re2o_paginator
|
||||
|
||||
def form(ctx, template, request):
|
||||
"""Form générique, raccourci importé par les fonctions views du site"""
|
||||
|
@ -88,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,
|
||||
|
@ -146,16 +147,10 @@ def history(request, application, object_name, object_id):
|
|||
))
|
||||
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
||||
reversions = Version.objects.get_for_object(instance)
|
||||
paginator = Paginator(reversions, pagination_number)
|
||||
page = request.GET.get('page')
|
||||
try:
|
||||
reversions = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
# If page is not an integer, deliver first page.
|
||||
reversions = paginator.page(1)
|
||||
except EmptyPage:
|
||||
# If page is out of range (e.g. 9999), deliver last page of result
|
||||
reversions = paginator.page(paginator.num_pages)
|
||||
if hasattr(instance, 'linked_objects'):
|
||||
for related_object in chain(instance.linked_objects()):
|
||||
reversions = reversions | Version.objects.get_for_object(related_object)
|
||||
reversions = re2o_paginator(request, reversions, pagination_number)
|
||||
return render(
|
||||
request,
|
||||
're2o/history.html',
|
||||
|
|
|
@ -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:
|
||||
|
@ -64,7 +64,7 @@ class PortForm(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:
|
||||
|
@ -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
|
||||
|
@ -166,7 +166,7 @@ class CreatePortsForm(forms.Form):
|
|||
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
|
||||
|
|
228
topologie/management/commands/graph_topo.py
Normal file
228
topologie/management/commands/graph_topo.py
Normal file
|
@ -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)
|
405
topologie/management/commands/modelviz.py
Normal file
405
topologie/management/commands/modelviz.py
Normal file
|
@ -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 <antonio@cavedoni.org>
|
||||
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 <v.oostveen@gmail.com>",
|
||||
__contributors__ = [
|
||||
"Antonio Cavedoni <http://cavedoni.com/>"
|
||||
"Stefano J. Attardi <http://attardi.org/>",
|
||||
"limodou <http://www.donews.net/limodou/>",
|
||||
"Carlo C8E Miron",
|
||||
"Andre Campos <cahenan@gmail.com>",
|
||||
"Justin Findlay <jfindlay@gmail.com>",
|
||||
"Alexander Houben <alexander@houben.ch>",
|
||||
"Joern Hees <gitdev@joernhees.de>",
|
||||
"Kevin Cherepski <cherepski@gmail.com>",
|
||||
"Jose Tomas Tocino <theom3ga@gmail.com>",
|
||||
"Adam Dobrawy <naczelnik@jawnosc.tk>",
|
||||
"Mikkel Munch Mortensen <https://www.detfalskested.dk/>",
|
||||
"Andrzej Bistram <andrzej.bistram@gmail.com>",
|
||||
]
|
||||
|
||||
|
||||
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)
|
||||
|
|
@ -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"""
|
||||
|
@ -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),
|
||||
|
@ -187,7 +190,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 +208,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 +222,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 +338,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"
|
||||
|
||||
|
|
|
@ -24,6 +24,64 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
{% load acl %}
|
||||
|
||||
|
||||
<div class="table-responsive" style="font-size: 12px">
|
||||
<table class="table table-bordered text-center text-nowrap">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
{% for port in port_list|slice:"::2" %}
|
||||
<td class="bg-primary text-white">{{ port.port }}</td>
|
||||
{% endfor %}
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
{% for port in port_list|slice:"::2" %}
|
||||
<td class="p-3 mb-2 bg-success text-dark">
|
||||
{% if port.room %}
|
||||
{{ port.room }}
|
||||
{% elif port.machine_interface %}
|
||||
<a href="{% url 'users:profil' userid=port.machine_interface.machine.user.id %}">{{ port.machine_interface }}</a>
|
||||
{% elif port.related%}
|
||||
<a href="{% url 'topologie:index-port' switchid=port.related.switch.id %}">{{ port.related }}</a>
|
||||
{% else %}
|
||||
Vide
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
|
||||
{% for port in port_list|slice:"1::2" %}
|
||||
<td class="bg-primary text-white">{{ port.port }}</td>
|
||||
{% endfor %}
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
{% for port in port_list|slice:"1::2" %}
|
||||
<td class="p-3 mb-2 bg-success text-dark">
|
||||
{% if port.room %}
|
||||
{{ port.room }}
|
||||
{% elif port.machine_interface %}
|
||||
<a href="{% url 'users:profil' userid=port.machine_interface.machine.user.id %}">{{ port.machine_interface }}</a>
|
||||
{% elif port.related%}
|
||||
<a href="{% url 'topologie:index-port' switchid=port.related.switch.id %}">{{ port.related }}</a>
|
||||
{% else %}
|
||||
Vide
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -76,3 +134,4 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:new-port' id_switch %}"><i class="fa fa-plus"></i> Ajouter un port</a>
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:create-ports' id_switch %}"><i class="fa fa-plus"></i> Ajouter des ports</a>
|
||||
{% acl_end %}
|
||||
<hr>
|
||||
{% include "topologie/aff_port.html" with port_list=port_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -43,9 +43,6 @@ from django.db import IntegrityError
|
|||
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,
|
||||
|
@ -68,7 +65,7 @@ from topologie.forms import (
|
|||
EditAccessPointForm
|
||||
)
|
||||
from users.views import form
|
||||
from re2o.utils import SortTable
|
||||
from re2o.utils import re2o_paginator, SortTable
|
||||
from re2o.acl import (
|
||||
can_create,
|
||||
can_edit,
|
||||
|
@ -105,16 +102,7 @@ def index(request):
|
|||
SortTable.TOPOLOGIE_INDEX
|
||||
)
|
||||
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
||||
paginator = Paginator(switch_list, pagination_number)
|
||||
page = request.GET.get('page')
|
||||
try:
|
||||
switch_list = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
# If page is not an integer, deliver first page.
|
||||
switch_list = paginator.page(1)
|
||||
except EmptyPage:
|
||||
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||
switch_list = paginator.page(paginator.num_pages)
|
||||
switch_list = re2o_paginator(request, switch_list, pagination_number)
|
||||
return render(request, 'topologie/index.html', {
|
||||
'switch_list': switch_list
|
||||
})
|
||||
|
@ -160,16 +148,7 @@ def index_room(request):
|
|||
SortTable.TOPOLOGIE_INDEX_ROOM
|
||||
)
|
||||
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
||||
paginator = Paginator(room_list, pagination_number)
|
||||
page = request.GET.get('page')
|
||||
try:
|
||||
room_list = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
# If page is not an integer, deliver first page.
|
||||
room_list = paginator.page(1)
|
||||
except EmptyPage:
|
||||
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||
room_list = paginator.page(paginator.num_pages)
|
||||
room_list = re2o_paginator(request, room_list, pagination_number)
|
||||
return render(request, 'topologie/index_room.html', {
|
||||
'room_list': room_list
|
||||
})
|
||||
|
@ -191,16 +170,7 @@ def index_ap(request):
|
|||
SortTable.TOPOLOGIE_INDEX_BORNE
|
||||
)
|
||||
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
||||
paginator = Paginator(ap_list, pagination_number)
|
||||
page = request.GET.get('page')
|
||||
try:
|
||||
ap_list = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
# If page is not an integer, deliver first page.
|
||||
ap_list = paginator.page(1)
|
||||
except EmptyPage:
|
||||
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||
ap_list = paginator.page(paginator.num_pages)
|
||||
ap_list = re2o_paginator(request, ap_list, pagination_number)
|
||||
return render(request, 'topologie/index_ap.html', {
|
||||
'ap_list': ap_list
|
||||
})
|
||||
|
@ -262,10 +232,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")
|
||||
messages.success(request, "Port ajouté")
|
||||
except IntegrityError:
|
||||
messages.error(request, "Ce port existe déjà")
|
||||
|
@ -284,12 +251,8 @@ 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():
|
||||
if port.changed_data:
|
||||
port.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in port.changed_data
|
||||
))
|
||||
messages.success(request, "Le port a bien été modifié")
|
||||
return redirect(reverse(
|
||||
'topologie:index-port',
|
||||
|
@ -304,10 +267,7 @@ 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")
|
||||
except ProtectedError:
|
||||
messages.error(request, "Le port %s est affecté à un autre objet,\
|
||||
|
@ -325,10 +285,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")
|
||||
messages.success(request, "Stack crée")
|
||||
return form({'topoform': stack, 'action_name' : 'Créer'}, 'topologie/topo.html', request)
|
||||
|
||||
|
@ -337,17 +294,10 @@ 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():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if stack.changed_data:
|
||||
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'))
|
||||
return form({'topoform': stack, 'action_name' : 'Editer'}, 'topologie/topo.html', request)
|
||||
|
||||
|
@ -358,10 +308,7 @@ 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")
|
||||
except ProtectedError:
|
||||
messages.error(request, "La stack %s est affectée à un autre\
|
||||
|
@ -412,20 +359,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_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_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")
|
||||
messages.success(request, "Le switch a été créé")
|
||||
return redirect(reverse('topologie:index'))
|
||||
i_mbf_param = generate_ipv4_mbf_param(interface, False)
|
||||
|
@ -468,7 +406,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}
|
||||
|
@ -500,26 +437,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)
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if switch_form.changed_data:
|
||||
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():
|
||||
if interface_form.changed_data:
|
||||
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():
|
||||
if domain_form.changed_data:
|
||||
new_domain.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in domain_form.changed_data)
|
||||
)
|
||||
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 +485,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_interface.machine = new_ap
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_interface.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Création")
|
||||
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")
|
||||
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 +530,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)
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if ap_form.changed_data:
|
||||
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():
|
||||
if interface_form.changed_data:
|
||||
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():
|
||||
if domain_form.changed_data:
|
||||
new_domain.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in domain_form.changed_data)
|
||||
)
|
||||
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 +554,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")
|
||||
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)
|
||||
|
@ -667,15 +564,10 @@ 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():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if room.changed_data:
|
||||
room.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in room.changed_data)
|
||||
)
|
||||
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,10 +579,7 @@ 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")
|
||||
except ProtectedError:
|
||||
messages.error(request, "La chambre %s est affectée à un autre objet,\
|
||||
|
@ -708,10 +597,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")
|
||||
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 +610,8 @@ 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():
|
||||
if model_switch.changed_data:
|
||||
model_switch.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in model_switch.changed_data)
|
||||
)
|
||||
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,10 +623,7 @@ 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")
|
||||
except ProtectedError:
|
||||
messages.error(request, "Le modèle %s est affectée à un autre objet,\
|
||||
|
@ -762,10 +641,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")
|
||||
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 +654,8 @@ 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():
|
||||
if constructor_switch.changed_data:
|
||||
constructor_switch.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in constructor_switch.changed_data)
|
||||
)
|
||||
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,10 +667,7 @@ 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")
|
||||
except ProtectedError:
|
||||
messages.error(request, "Le constructeur %s est affecté à un autre objet,\
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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__)
|
||||
|
|
47
users/management/commands/chgpass.py
Normal file
47
users/management/commands/chgpass.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
# ⁻*- mode: python; coding: utf-8 -*-
|
||||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||
# quelques clics.
|
||||
#
|
||||
# Copyright © 2018 Lev-Arcady Sellem
|
||||
#
|
||||
# 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
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import os, pwd
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from users.forms import PassForm
|
||||
from re2o.script_utils import get_user, get_system_user, form_cli
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Changer le mot de passe d'un utilisateur"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('target_username', nargs='?')
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
|
||||
current_username = get_system_user()
|
||||
current_user = get_user(current_username)
|
||||
target_username = kwargs["target_username"] or current_username
|
||||
target_user = get_user(target_username)
|
||||
|
||||
ok, msg = target_user.can_change_password(current_user)
|
||||
if not ok:
|
||||
raise CommandError(msg)
|
||||
|
||||
self.stdout.write("Changement du mot de passe de %s" % target_user.pseudo)
|
||||
|
||||
form_cli(PassForm,current_user,"Changement du mot de passe",instance=target_user)
|
|
@ -26,6 +26,7 @@ from django.db import transaction
|
|||
from reversion import revisions as reversion
|
||||
|
||||
from users.models import User, ListShell
|
||||
from re2o.script_utils import get_user, get_system_user
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Change the default shell of a user'
|
||||
|
@ -35,14 +36,7 @@ class Command(BaseCommand):
|
|||
|
||||
def handle(self, *args, **options):
|
||||
|
||||
def get_user(user_pseudo):
|
||||
"""Return the user queried by pseudo, and exit the script if not found."""
|
||||
user = User.objects.filter(pseudo=user_pseudo)
|
||||
if not user:
|
||||
raise CommandError("Utilisateur invalide")
|
||||
return user[0]
|
||||
|
||||
current_username = pwd.getpwuid(int(os.getenv("SUDO_UID") or os.getuid())).pw_name
|
||||
current_username = get_system_user()
|
||||
current_user = get_user(current_username)
|
||||
|
||||
target_username = options["target_username"] or current_username
|
||||
|
|
|
@ -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"""
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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"
|
||||
|
@ -1140,9 +1140,6 @@ class Ban(AclMixin, models.Model):
|
|||
"""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.
|
||||
|
||||
|
@ -1189,7 +1186,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"""
|
||||
|
|
|
@ -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 %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
171
users/views.py
171
users/views.py
|
@ -37,7 +37,6 @@ from __future__ import unicode_literals
|
|||
|
||||
from django.urls import reverse
|
||||
from django.shortcuts import get_object_or_404, render, redirect
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.db.models import ProtectedError, Q
|
||||
|
@ -95,6 +94,7 @@ from re2o.views import form
|
|||
from re2o.utils import (
|
||||
all_has_access,
|
||||
SortTable,
|
||||
re2o_paginator
|
||||
)
|
||||
from re2o.acl import (
|
||||
can_create,
|
||||
|
@ -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.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.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,8 @@ 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():
|
||||
if club.changed_data:
|
||||
club.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in club.changed_data
|
||||
))
|
||||
messages.success(request, "Le club a bien été modifié")
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
|
@ -191,12 +181,8 @@ def edit_info(request, user, userid):
|
|||
user=request.user
|
||||
)
|
||||
if user.is_valid():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if user.changed_data:
|
||||
user.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in user.changed_data
|
||||
))
|
||||
messages.success(request, "L'user a bien été modifié")
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
|
@ -212,17 +198,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
|
||||
))
|
||||
if user.changed_data:
|
||||
user.save()
|
||||
messages.success(request, "Etat changé avec succès")
|
||||
return redirect(reverse(
|
||||
|
@ -237,11 +219,7 @@ 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
|
||||
))
|
||||
if group.changed_data:
|
||||
group.save()
|
||||
messages.success(request, "Groupes changés avec succès")
|
||||
return redirect(reverse(
|
||||
|
@ -259,10 +237,8 @@ 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():
|
||||
if u_form.changed_data:
|
||||
u_form.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Changement du mot de passe")
|
||||
messages.success(request, "Le mot de passe a changé")
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
|
@ -274,11 +250,8 @@ 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)
|
||||
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
|
||||
|
||||
|
@ -290,11 +263,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")
|
||||
messages.success(
|
||||
request,
|
||||
"L'utilisateur %s a été crée" % user_object.pseudo
|
||||
|
@ -307,17 +277,13 @@ def new_serviceuser(request):
|
|||
@can_edit(ServiceUser)
|
||||
def edit_serviceuser(request, serviceuser, serviceuserid):
|
||||
""" Edit a ServiceUser """
|
||||
serviceuser = EditServiceUserForm(request.POST or None, instance=serviceuser)
|
||||
if serviceuser.is_valid():
|
||||
user_object = serviceuser.save(commit=False)
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if serviceuser.cleaned_data['password']:
|
||||
user_object.set_password(serviceuser.cleaned_data['password'])
|
||||
user = EditServiceUserForm(request.POST or None, instance=user)
|
||||
if user.is_valid():
|
||||
user_object = user.save(commit=False)
|
||||
if user.cleaned_data['password']:
|
||||
user_object.set_password(user.cleaned_data['password'])
|
||||
if user.changed_data:
|
||||
user_object.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in serviceuser.changed_data
|
||||
))
|
||||
messages.success(request, "L'user a bien été modifié")
|
||||
return redirect(reverse('users:index-serviceusers'))
|
||||
return form({'userform': serviceuser, 'action_name':'Editer un serviceuser'}, 'users/user.html', request)
|
||||
|
@ -328,9 +294,7 @@ def edit_serviceuser(request, serviceuser, serviceuserid):
|
|||
def del_serviceuser(request, serviceuser, serviceuserid):
|
||||
"""Suppression d'un ou plusieurs serviceusers"""
|
||||
if request.method == "POST":
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
serviceuser.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 +314,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")
|
||||
messages.success(request, "Bannissement ajouté")
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
|
@ -374,12 +335,8 @@ 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():
|
||||
if ban.changed_data:
|
||||
ban.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in ban.changed_data
|
||||
))
|
||||
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 +356,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")
|
||||
messages.success(request, "Accès à titre gracieux accordé")
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
|
@ -428,12 +382,8 @@ def edit_whitelist(request, whitelist_instance, whitelistid):
|
|||
instance=whitelist_instance
|
||||
)
|
||||
if whitelist.is_valid():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if whitelist.changed_data:
|
||||
whitelist.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in whitelist.changed_data
|
||||
))
|
||||
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 +396,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")
|
||||
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 +409,8 @@ 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():
|
||||
if school.changed_data:
|
||||
school.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in school.changed_data
|
||||
))
|
||||
messages.success(request, "Établissement modifié")
|
||||
return redirect(reverse('users:index-school'))
|
||||
return form({'userform': school, 'action_name':'Editer'}, 'users/user.html', request)
|
||||
|
@ -485,9 +428,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")
|
||||
messages.success(request, "L'établissement a été supprimé")
|
||||
except ProtectedError:
|
||||
messages.error(
|
||||
|
@ -504,10 +445,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")
|
||||
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 +457,8 @@ 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():
|
||||
if shell.changed_data:
|
||||
shell.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in shell.changed_data
|
||||
))
|
||||
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 +469,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)
|
||||
messages.success(request, "Le shell a été détruit")
|
||||
return redirect(reverse('users:index-shell'))
|
||||
return form(
|
||||
|
@ -554,10 +486,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")
|
||||
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 +502,8 @@ def edit_listright(request, listright_instance, listrightid):
|
|||
instance=listright_instance
|
||||
)
|
||||
if listright.is_valid():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
if listright.changed_data:
|
||||
listright.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in listright.changed_data
|
||||
))
|
||||
messages.success(request, "Droit modifié")
|
||||
return redirect(reverse('users:index-listright'))
|
||||
return form({'userform': listright, 'action_name': 'Editer'}, 'users/user.html', request)
|
||||
|
@ -594,9 +519,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")
|
||||
messages.success(request, "Le droit/groupe a été supprimé")
|
||||
except ProtectedError:
|
||||
messages.error(
|
||||
|
@ -625,7 +548,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
|
||||
|
@ -650,16 +572,7 @@ def index(request):
|
|||
request.GET.get('order'),
|
||||
SortTable.USERS_INDEX
|
||||
)
|
||||
paginator = Paginator(users_list, pagination_number)
|
||||
page = request.GET.get('page')
|
||||
try:
|
||||
users_list = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
# If page is not an integer, deliver first page.
|
||||
users_list = paginator.page(1)
|
||||
except EmptyPage:
|
||||
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||
users_list = paginator.page(paginator.num_pages)
|
||||
users_list = re2o_paginator(request, users_list, pagination_number)
|
||||
return render(request, 'users/index.html', {'users_list': users_list})
|
||||
|
||||
|
||||
|
@ -675,16 +588,7 @@ def index_clubs(request):
|
|||
request.GET.get('order'),
|
||||
SortTable.USERS_INDEX
|
||||
)
|
||||
paginator = Paginator(clubs_list, pagination_number)
|
||||
page = request.GET.get('page')
|
||||
try:
|
||||
clubs_list = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
# If page is not an integer, deliver first page.
|
||||
clubs_list = paginator.page(1)
|
||||
except EmptyPage:
|
||||
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||
clubs_list = paginator.page(paginator.num_pages)
|
||||
clubs_list = re2o_paginator(request, clubs_list, pagination_number)
|
||||
return render(request, 'users/index_clubs.html', {'clubs_list': clubs_list})
|
||||
|
||||
|
||||
|
@ -700,16 +604,7 @@ def index_ban(request):
|
|||
request.GET.get('order'),
|
||||
SortTable.USERS_INDEX_BAN
|
||||
)
|
||||
paginator = Paginator(ban_list, pagination_number)
|
||||
page = request.GET.get('page')
|
||||
try:
|
||||
ban_list = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
# If page isn't an integer, deliver first page
|
||||
ban_list = paginator.page(1)
|
||||
except EmptyPage:
|
||||
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||
ban_list = paginator.page(paginator.num_pages)
|
||||
ban_list = re2o_paginator(request, ban_list, pagination_number)
|
||||
return render(request, 'users/index_ban.html', {'ban_list': ban_list})
|
||||
|
||||
|
||||
|
@ -725,16 +620,7 @@ def index_white(request):
|
|||
request.GET.get('order'),
|
||||
SortTable.USERS_INDEX_BAN
|
||||
)
|
||||
paginator = Paginator(white_list, pagination_number)
|
||||
page = request.GET.get('page')
|
||||
try:
|
||||
white_list = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
# If page isn't an integer, deliver first page
|
||||
white_list = paginator.page(1)
|
||||
except EmptyPage:
|
||||
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||
white_list = paginator.page(paginator.num_pages)
|
||||
white_list = re2o_paginator(request, white_list, pagination_number)
|
||||
return render(
|
||||
request,
|
||||
'users/index_whitelist.html',
|
||||
|
@ -754,16 +640,7 @@ def index_school(request):
|
|||
request.GET.get('order'),
|
||||
SortTable.USERS_INDEX_SCHOOL
|
||||
)
|
||||
paginator = Paginator(school_list, pagination_number)
|
||||
page = request.GET.get('page')
|
||||
try:
|
||||
school_list = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
# If page isn't an integer, deliver first page
|
||||
school_list = paginator.page(1)
|
||||
except EmptyPage:
|
||||
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||
school_list = paginator.page(paginator.num_pages)
|
||||
school_list = re2o_paginator(request, school_list, pagination_number)
|
||||
return render(
|
||||
request,
|
||||
'users/index_schools.html',
|
||||
|
@ -832,6 +709,8 @@ def profil(request, users, userid):
|
|||
request.GET.get('order'),
|
||||
SortTable.MACHINES_INDEX
|
||||
)
|
||||
pagination_large_number = GeneralOption.get_cached_value('pagination_large_number')
|
||||
machines = re2o_paginator(request, machines, pagination_large_number)
|
||||
factures = Facture.objects.filter(user=users)
|
||||
factures = SortTable.sort(
|
||||
factures,
|
||||
|
|
Loading…
Reference in a new issue