8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-11-26 22:52:26 +00:00
This commit is contained in:
lhark 2016-07-06 22:46:46 +02:00
commit bfb07c3a24
49 changed files with 840 additions and 139 deletions

4
.gitignore vendored
View file

@ -1,4 +1,4 @@
settings.py
settings*
settings_local.py
*.swp
*.pyc
__pycache__

View file

@ -3,10 +3,10 @@ from django.contrib import admin
from .models import Facture, Article, Banque, Paiement, Cotisation
class FactureAdmin(admin.ModelAdmin):
list_display = ('user','paiement','name', 'number', 'date')
list_display = ('user','paiement','name', 'number','prix', 'date','valid')
class ArticleAdmin(admin.ModelAdmin):
list_display = ('name','prix','cotisation')
list_display = ('name','prix','cotisation','duration')
class BanqueAdmin(admin.ModelAdmin):
list_display = ('name',)

88
cotisations/forms.py Normal file
View file

@ -0,0 +1,88 @@
from django import forms
from django.forms import ModelForm
from .models import Article, Paiement, Facture, Banque
class NewFactureForm(ModelForm):
article = forms.ModelMultipleChoiceField(queryset=Article.objects.all(), label="Article")
def __init__(self, *args, **kwargs):
super(NewFactureForm, self).__init__(*args, **kwargs)
self.fields['number'].label = 'Quantité'
self.fields['cheque'].required = False
self.fields['banque'].required = False
self.fields['cheque'].label = 'Numero de chèque'
self.fields['banque'].empty_label = "Non renseigné"
self.fields['paiement'].empty_label = "Séléctionner un moyen de paiement"
class Meta:
model = Facture
fields = ['paiement','banque','cheque','number']
def clean(self):
cleaned_data=super(NewFactureForm, self).clean()
paiement = cleaned_data.get("paiement")
cheque = cleaned_data.get("cheque")
banque = cleaned_data.get("banque")
if paiement.moyen=="chèque" and not (cheque and banque):
raise forms.ValidationError("Le numero de chèque et la banque sont obligatoires")
return cleaned_data
class EditFactureForm(NewFactureForm):
class Meta(NewFactureForm.Meta):
fields = '__all__'
def __init__(self, *args, **kwargs):
super(EditFactureForm, self).__init__(*args, **kwargs)
self.fields['user'].label = 'Adherent'
self.fields['name'].label = 'Designation'
self.fields['prix'].label = 'Prix unitaire'
self.fields['user'].empty_label = "Séléctionner l'adhérent propriétaire"
self.fields.pop('article')
class ArticleForm(ModelForm):
class Meta:
model = Article
fields = '__all__'
def __init__(self, *args, **kwargs):
super(ArticleForm, self).__init__(*args, **kwargs)
self.fields['name'].label = "Désignation de l'article"
class DelArticleForm(ModelForm):
articles = forms.ModelMultipleChoiceField(queryset=Article.objects.all(), label="Articles actuels", widget=forms.CheckboxSelectMultiple)
class Meta:
fields = ['articles']
model = Article
class PaiementForm(ModelForm):
class Meta:
model = Paiement
fields = ['moyen']
def __init__(self, *args, **kwargs):
super(PaiementForm, self).__init__(*args, **kwargs)
self.fields['moyen'].label = 'Moyen de paiement à ajouter'
class DelPaiementForm(ModelForm):
paiements = forms.ModelMultipleChoiceField(queryset=Paiement.objects.all(), label="Moyens de paiement actuels", widget=forms.CheckboxSelectMultiple)
class Meta:
exclude = ['moyen']
model = Paiement
class BanqueForm(ModelForm):
class Meta:
model = Banque
fields = ['name']
def __init__(self, *args, **kwargs):
super(BanqueForm, self).__init__(*args, **kwargs)
self.fields['name'].label = 'Banque à ajouter'
class DelBanqueForm(ModelForm):
banques = forms.ModelMultipleChoiceField(queryset=Banque.objects.all(), label="Banques actuelles", widget=forms.CheckboxSelectMultiple)
class Meta:
exclude = ['name']
model = Banque

View file

@ -1,8 +1,5 @@
from django.db import models
from django import forms
from django.forms import ModelForm
from users.models import User
class Facture(models.Model):
user = models.ForeignKey('users.User', on_delete=models.PROTECT)
@ -47,31 +44,3 @@ class Cotisation(models.Model):
def __str__(self):
return str(self.facture)
class NewFactureForm(ModelForm):
article = forms.ModelMultipleChoiceField(queryset=Article.objects.all(), label="Article")
def __init__(self, *args, **kwargs):
super(NewFactureForm, self).__init__(*args, **kwargs)
self.fields['number'].label = 'Quantité'
self.fields['cheque'].required = False
self.fields['banque'].required = False
self.fields['cheque'].label = 'Numero de chèque'
class Meta:
model = Facture
exclude = ['user', 'prix', 'name', 'valid']
class EditFactureForm(ModelForm):
def __init__(self, *args, **kwargs):
super(EditFactureForm, self).__init__(*args, **kwargs)
self.fields['user'].label = 'Adherent'
self.fields['number'].label = 'Quantité'
self.fields['cheque'].required = False
self.fields['banque'].required = False
self.fields['cheque'].label = 'Numero de chèque'
self.fields['name'].label = 'Designation'
self.fields['prix'].label = 'Prix unitaire'
class Meta:
model = Facture
fields = '__all__'

View file

@ -18,7 +18,7 @@
<td>{{ facture.prix }}</td>
<td>{{ facture.paiement }}</td>
<td>{{ facture.date }}</td>
<td><a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:edit-facture' facture.id %}"><i class="glyphicon glyphicon-tree-conifer"></i> Editer</a></td>
<td><a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:edit-facture' facture.id %}"><i class="glyphicon glyphicon-bitcoin"></i> Editer</a></td>
</tr>
{% endfor %}
</table>

View file

@ -1,7 +1,11 @@
{% extends "base.html" %}
{% block sidebar %}
<p><a href="{% url "search:search" %}">Créer une facture</a></p>
<p><a href="{% url "search:search" %}">Editer une facture</a></p>
<p><a href="{% url "cotisations:index" %}">Liste des factures</a></p>
<p><a href="{% url "cotisations:add-article" %}">Ajouter un article</a></p>
<p><a href="{% url "cotisations:del-article" %}">Retirer un article</a></p>
<p><a href="{% url "cotisations:add-paiement" %}">Ajouter un moyen de paiement</a></p>
<p><a href="{% url "cotisations:del-paiement" %}">Retirer un moyen de paiement</a></p>
<p><a href="{% url "cotisations:add-banque" %}">Ajouter une banque</a></p>
<p><a href="{% url "cotisations:del-banque" %}">Retirer une banque</a></p>
{% endblock %}

View file

@ -5,6 +5,12 @@ from . import views
urlpatterns = [
url(r'^new_facture/(?P<userid>[0-9]+)$', views.new_facture, name='new-facture'),
url(r'^edit_facture/(?P<factureid>[0-9]+)$', views.edit_facture, name='edit-facture'),
url(r'^add_article/$', views.add_article, name='add-article'),
url(r'^del_article/$', views.del_article, name='del-article'),
url(r'^add_paiement/$', views.add_paiement, name='add-paiement'),
url(r'^del_paiement/$', views.del_paiement, name='del-paiement'),
url(r'^add_banque/$', views.add_banque, name='add-banque'),
url(r'^del_banque/$', views.del_banque, name='del-banque'),
url(r'^$', views.index, name='index'),
]

View file

@ -6,9 +6,10 @@ from django.shortcuts import render_to_response, get_object_or_404
from django.core.context_processors import csrf
from django.template import Context, RequestContext, loader
from django.contrib import messages
from django.db.models import Max
from django.db.models import Max, ProtectedError
from cotisations.models import NewFactureForm, EditFactureForm, Facture, Article, Cotisation
from .models import Facture, Article, Cotisation, Article
from .forms import NewFactureForm, EditFactureForm, ArticleForm, DelArticleForm, PaiementForm, DelPaiementForm, BanqueForm, DelBanqueForm
from users.models import User
from dateutil.relativedelta import relativedelta
@ -34,14 +35,14 @@ def is_adherent(user):
else:
return True
def create_cotis(facture, user, article):
def create_cotis(facture, user, duration):
""" Update et crée l'objet cotisation associé à une facture, prend en argument l'user, la facture pour la quantitéi, et l'article pour la durée"""
cotisation=Cotisation(facture=facture)
date_max = end_adhesion(user) or timezone.now()
if date_max < timezone.now():
datemax = timezone.now()
cotisation.date_start=date_max
cotisation.date_end = cotisation.date_start + relativedelta(months=article[0].duration*facture.number)
cotisation.date_end = cotisation.date_start + relativedelta(months=duration)
cotisation.save()
return
@ -56,11 +57,12 @@ def new_facture(request, userid):
if facture_form.is_valid():
new_facture = facture_form.save(commit=False)
article = facture_form.cleaned_data['article']
new_facture.prix = article[0].prix
new_facture.name = article[0].name
new_facture.prix = sum(art.prix for art in article)
new_facture.name = ' - '.join(art.name for art in article)
new_facture.save()
if article[0].cotisation == True:
create_cotis(new_facture, user, article)
if any(art.cotisation for art in article):
duration = sum(art.duration*facture.number for art in article if art.cotisation)
create_cotis(new_facture, user, duration)
messages.success(request, "La cotisation a été prolongée pour l'adhérent %s " % user.name )
else:
messages.success(request, "La facture a été crée")
@ -80,6 +82,65 @@ def edit_facture(request, factureid):
return redirect("/cotisations/")
return form({'factureform': facture_form}, 'cotisations/facture.html', request)
def add_article(request):
article = ArticleForm(request.POST or None)
if article.is_valid():
article.save()
messages.success(request, "L'article a été ajouté")
return redirect("/cotisations/")
return form({'factureform': article}, 'cotisations/facture.html', request)
def del_article(request):
article = DelArticleForm(request.POST or None)
if article.is_valid():
article_del = article.cleaned_data['articles']
article_del.delete()
messages.success(request, "Le/les articles ont été supprimé")
return redirect("/cotisations/")
return form({'factureform': article}, 'cotisations/facture.html', request)
def add_paiement(request):
paiement = PaiementForm(request.POST or None)
if paiement.is_valid():
paiement.save()
messages.success(request, "Le moyen de paiement a été ajouté")
return redirect("/cotisations/")
return form({'factureform': paiement}, 'cotisations/facture.html', request)
def del_paiement(request):
paiement = DelPaiementForm(request.POST or None)
if paiement.is_valid():
paiement_dels = paiement.cleaned_data['paiements']
for paiement_del in paiement_dels:
try:
paiement_del.delete()
messages.success(request, "Le moyen de paiement a été supprimé")
except ProtectedError:
messages.error(request, "Le moyen de paiement %s est affecté à au moins une facture, vous ne pouvez pas le supprimer" % paiement_del)
return redirect("/cotisations/")
return form({'factureform': paiement}, 'cotisations/facture.html', request)
def add_banque(request):
banque = BanqueForm(request.POST or None)
if banque.is_valid():
banque.save()
messages.success(request, "La banque a été ajoutée")
return redirect("/cotisations/")
return form({'factureform': banque}, 'cotisations/facture.html', request)
def del_banque(request):
banque = DelBanqueForm(request.POST or None)
if banque.is_valid():
banque_dels = banque.cleaned_data['banques']
for banque_del in banque_dels:
try:
banque_del.delete()
messages.success(request, "La banque a été supprimée")
except ProtectedError:
messages.error(request, "La banque %s est affectée à au moins une facture, vous ne pouvez pas la supprimer" % banque_del)
return redirect("/cotisations/")
return form({'factureform': banque}, 'cotisations/facture.html', request)
def index(request):
facture_list = Facture.objects.order_by('pk')
facture_list = Facture.objects.order_by('date').reverse()
return render(request, 'cotisations/index.html', {'facture_list': facture_list})

View file

@ -3,7 +3,7 @@ from django.contrib import admin
from .models import Machine, MachineType, IpList, Interface
class MachineAdmin(admin.ModelAdmin):
list_display = ('user','name','type')
list_display = ('user','name','type','active')
class MachineTypeAdmin(admin.ModelAdmin):
list_display = ('type',)

View file

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('machines', '0012_auto_20160704_0118'),
]
operations = [
migrations.AddField(
model_name='machine',
name='active',
field=models.BooleanField(default=True),
),
migrations.AlterField(
model_name='interface',
name='dns',
field=models.CharField(max_length=255, unique=True, help_text='Obligatoire et unique, doit se terminer en .rez et ne pas comporter de points'),
),
]

View file

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import machines.models
class Migration(migrations.Migration):
dependencies = [
('machines', '0013_auto_20160705_1014'),
]
operations = [
migrations.AlterField(
model_name='interface',
name='dns',
field=models.CharField(unique=True, validators=[machines.models.full_domain_validator], max_length=255, help_text="Obligatoire et unique, doit se terminer en .rez et ne pas comporter d'autres points"),
),
]

View file

@ -1,13 +1,38 @@
from django.db import models
from django.forms import ModelForm, Form
from django.forms import ModelForm, Form, ValidationError
from macaddress.fields import MACAddressField
from users.models import User
from django.conf import settings
import re
def full_domain_validator(hostname):
""" Validation du nom de domaine, extensions dans settings, prefixe pas plus long que 63 caractères """
HOSTNAME_LABEL_PATTERN = re.compile("(?!-)[A-Z\d-]+(?<!-)$", re.IGNORECASE)
if not any(ext in hostname for ext in settings.ALLOWED_EXTENSIONS):
raise ValidationError(
", le nom de domaine '%(label)s' doit comporter une extension valide",
params={'label': hostname},
)
for extension in settings.ALLOWED_EXTENSIONS:
if hostname.endswith(extension):
hostname=re.sub('%s$' % extension, '', hostname)
break
if len(hostname) > 63:
raise ValidationError(
", le nom de domaine '%(label)s' est trop long (maximum de 63 caractères).",
params={'label': hostname},
)
if not HOSTNAME_LABEL_PATTERN.match(hostname):
raise ValidationError(
", ce nom de domaine '%(label)s' contient des carractères interdits.",
params={'label': hostname},
)
class Machine(models.Model):
user = models.ForeignKey('users.User', on_delete=models.PROTECT)
type = models.ForeignKey('MachineType', on_delete=models.PROTECT)
name = models.CharField(max_length=255, help_text="Optionnel", blank=True, null=True)
active = models.BooleanField(default=True)
def __str__(self):
return str(self.user) + ' - ' + str(self.id) + ' - ' + str(self.name)
@ -25,11 +50,14 @@ class Interface(models.Model):
mac_address = MACAddressField(integer=False, unique=True)
machine = models.ForeignKey('Machine', on_delete=models.PROTECT)
details = models.CharField(max_length=255, blank=True)
dns = models.CharField(help_text="Obligatoire et unique", max_length=255, unique=True)
dns = models.CharField(help_text="Obligatoire et unique, doit se terminer en %s et ne pas comporter d'autres points" % ", ".join(settings.ALLOWED_EXTENSIONS), max_length=255, unique=True, validators=[full_domain_validator])
def __str__(self):
return self.dns
def clean(self):
self.dns=self.dns.lower()
class IpList(models.Model):
ipv4 = models.GenericIPAddressField(protocol='IPv4', unique=True)
@ -45,6 +73,7 @@ class EditMachineForm(ModelForm):
super(EditMachineForm, self).__init__(*args, **kwargs)
self.fields['name'].label = 'Nom de la machine'
self.fields['type'].label = 'Type de machine'
self.fields['type'].empty_label = "Séléctionner un type de machine"
class NewMachineForm(EditMachineForm):
class Meta(EditMachineForm.Meta):

View file

@ -16,7 +16,7 @@
<td>{{ machine.machine.type }}</td>
<td>{{ machine.mac_address }}</td>
<td>{{ machine.ipv4 }}</td>
<td><a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:edit-machine' machine.id %}"><i class="glyphicon glyphicon-tree-conifer"></i> Editer</a></td>
<td><a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:edit-machine' machine.id %}"><i class="glyphicon glyphicon-hdd"></i> Editer</a></td>
</tr>
{% endfor %}
</table>

View file

@ -1,5 +1,4 @@
{% extends "base.html" %}
{% block sidebar %}
<p><a href="{% url "search:search" %}">Nouvelle machine</a></p>
{% endblock %}

108
re2o/settings.py Normal file
View file

@ -0,0 +1,108 @@
"""
Django settings for re2o project.
Generated by 'django-admin startproject' using Django 1.8.13.
For more information on this file, see
https://docs.djangoproject.com/en/1.8/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.8/ref/settings/
"""
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
from .settings_local import SECRET_KEY, DATABASES, DEBUG, ALLOWED_HOSTS, ALLOWED_EXTENSIONS
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
# Application definition
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'bootstrap3',
'users',
'machines',
'cotisations',
'topologie',
'search',
'logs',
)
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
)
ROOT_URLCONF = 're2o.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, 'templates').replace('\\', '/'),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 're2o.wsgi.application'
# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/
LANGUAGE_CODE = 'fr-fr'
TIME_ZONE = 'Europe/Paris'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# django-bootstrap3 config dictionnary
BOOTSTRAP3 = {
'jquery_url': '/static/js/jquery-2.2.4.min.js',
'base_url': '/static/bootstrap/',
'include_jquery': True,
}
BOOTSTRAP_BASE_URL = '/static/bootstrap/'
STATICFILES_DIRS = (
# Put strings here, like "/home/html/static" or "C:/www/django/static".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
os.path.join(
BASE_DIR,
'static',
),
)
STATIC_URL = '/static/'

View file

@ -0,0 +1,20 @@
SECRET_KEY = 'SUPER_SECRET'
DB_PASSWORD = 'SUPER_SECRET'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
ALLOWED_HOSTS = []
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 're2o',
'USER': 're2o',
'PASSWORD': DB_PASSWORD,
'HOST': 'localhost',
}
}
ALLOWED_EXTENSIONS = ['.example']

View file

@ -24,5 +24,6 @@ urlpatterns = [
url(r'^search/', include('search.urls', namespace='search')),
url(r'^cotisations/', include('cotisations.urls', namespace='cotisations')),
url(r'^machines/', include('machines.urls', namespace='machines')),
url(r'^topologie/', include('topologie.urls', namespace='topologie')),
#url(r'^logs/', include('logs.urls', namespace='logs')),
]

View file

@ -3,9 +3,6 @@ from django import forms
from django.forms import Form
from django.forms import ModelForm
from users.models import User
# Create your models here.
CHOICES = (
('0', 'Actifs'),
('1', 'Désactivés'),

View file

@ -12,6 +12,7 @@ from machines.models import Machine, Interface
from cotisations.models import Facture
from search.models import SearchForm, SearchFormPlus
from users.views import has_access
from cotisations.views import end_adhesion
def form(ctx, template, request):
c = ctx
@ -23,7 +24,7 @@ def search_result(search, type):
date_fin = None
states=[]
co=[]
aff=[0,1,2,3,4]
aff=['0','1','2','3','4']
if(type):
aff = search.cleaned_data['affichage']
co = search.cleaned_data['connexion']
@ -32,7 +33,7 @@ def search_result(search, type):
date_fin = search.cleaned_data['date_fin']
date_query = Q()
if aff==[]:
aff = [0,1,2,3,4]
aff = ['0','1','2','3','4']
if date_deb != None:
date_query = date_query & Q(date__gte=date_deb)
if date_fin != None:
@ -54,9 +55,13 @@ def search_result(search, type):
users = User.objects.filter((Q(pseudo__icontains = search) | Q(name__icontains = search) | Q(surname__icontains = search)) & query)
connexion = []
for user in users:
end=end_adhesion(user)
access=has_access(user)
if(len(co)==0 or (len(co)==1 and bool(co[0])==access) or (len(co)==2 and (bool(co[0])==access or bool(co[1])==access))):
connexion.append([user, access])
if(end!=None):
connexion.append([user, access, end])
else:
connexion.append([user, access, "Non adhérent"])
query = Q(user__pseudo__icontains = search) | Q(user__name__icontains = search) | Q(user__surname__icontains = search)
if i == '1':
machines = Interface.objects.filter(machine=Machine.objects.filter(query)) | Interface.objects.filter(Q(dns__icontains = search))

View file

@ -30,7 +30,7 @@
<li><a href="{% url "users:index" %}">Adhérents</a></li>
<li><a href="{% url "machines:index" %}">Machines</a></li>
<li><a href="{% url "cotisations:index" %}">Cotisations</a></li>
<li><a href="#">Topologie</a></li>
<li><a href="{% url "topologie:index" %}">Topologie</a></li>
<li><a href="#">Statistiques</a></li>
</ul>
<div class="col-sm-3 col-md-3 navbar-right">

View file

@ -7,7 +7,7 @@ class SwitchAdmin(admin.ModelAdmin):
list_display = ('building','number','details')
class PortAdmin(admin.ModelAdmin):
list_display = ('switch', 'port','room','details')
list_display = ('switch', 'port','room','machine_interface','details')
class RoomAdmin(admin.ModelAdmin):
list_display = ('name',)

11
topologie/forms.py Normal file
View file

@ -0,0 +1,11 @@
from .models import Port
from django.forms import ModelForm, Form
class PortForm(ModelForm):
class Meta:
model = Port
fields = '__all__'
class EditPortForm(ModelForm):
class Meta(PortForm.Meta):
fields = ['room', 'machine_interface', 'related', 'details']

View file

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('machines', '0014_auto_20160706_1220'),
('topologie', '0011_auto_20160704_2153'),
]
operations = [
migrations.AddField(
model_name='port',
name='machine_interface',
field=models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, null=True, blank=True, to='machines.Interface'),
),
]

View file

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('topologie', '0012_port_machine_interface'),
]
operations = [
migrations.AddField(
model_name='port',
name='related',
field=models.OneToOneField(null=True, to='topologie.Port', blank=True, related_name='related_port'),
),
]

View file

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('topologie', '0013_port_related'),
]
operations = [
migrations.AlterUniqueTogether(
name='port',
unique_together=set([('switch', 'port')]),
),
migrations.RemoveField(
model_name='port',
name='_content_type',
),
migrations.RemoveField(
model_name='port',
name='_object_id',
),
]

View file

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('topologie', '0014_auto_20160706_1238'),
]
operations = [
migrations.RemoveField(
model_name='port',
name='related',
),
migrations.AddField(
model_name='port',
name='related',
field=models.ManyToManyField(related_name='_port_related_+', to='topologie.Port', blank=True),
),
]

View file

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('topologie', '0015_auto_20160706_1452'),
]
operations = [
migrations.RemoveField(
model_name='port',
name='related',
),
migrations.AddField(
model_name='port',
name='related',
field=models.OneToOneField(blank=True, to='topologie.Port', related_name='related_port', null=True),
),
]

View file

@ -1,7 +1,18 @@
from django.db import models
from django.forms import ModelForm, Form
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
from django.core.exceptions import ValidationError
def make_port_related(port):
related_port = port.related
related_port.related = port
related_port.save()
def clean_port_related(port):
related_port = port.related_port
related_port.related = None
related_port.save()
class Switch(models.Model):
building = models.CharField(max_length=10)
@ -17,25 +28,26 @@ class Switch(models.Model):
class Port(models.Model):
switch = models.ForeignKey(Switch, related_name="ports")
port = models.IntegerField()
details = models.CharField(max_length=255, blank=True)
room = models.ForeignKey('Room', on_delete=models.PROTECT, blank=True, null=True)
# machine_interface = models.OneToOneField('machines.Interface', on_delete=models.PROTECT, blank=True, null=True)
machine_interface = models.OneToOneField('machines.Interface', on_delete=models.PROTECT, blank=True, null=True)
related = models.OneToOneField('self', null=True, blank=True, related_name='related_port')
details = models.CharField(max_length=255, blank=True)
class Meta:
unique_together = ('_content_type', '_object_id')
unique_together = ('switch', 'port')
_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, blank=True, null=True)
_object_id = models.PositiveIntegerField(blank=True, null=True)
goto = GenericForeignKey('_content_type', '_object_id')
@property
def comefrom(self):
ctype = ContentType.objects.get_for_model(self.__class__)
try:
event = Port.objects.get(_content_type__pk=ctype.id, _object_id=self.id)
except:
return None
return event
def clean(self):
if self.room and self.machine_interface or self.room and self.related or self.machine_interface and self.related:
raise ValidationError("Chambre, interface et related_port sont mutuellement exclusifs")
if self.related==self:
raise ValidationError("On ne peut relier un port à lui même")
if self.related and not self.related.related:
if self.related.machine_interface or self.related.room:
raise ValidationError("Le port relié est déjà occupé, veuillez le libérer avant de créer une relation")
else:
make_port_related(self)
elif hasattr(self, 'related_port'):
clean_port_related(self)
def __str__(self):
return str(self.switch) + " - " + str(self.port)

View file

@ -0,0 +1,23 @@
<h2>Switch {% if port_list.0 %}{{ port_list.0.switch }}{% endif %}</h2>
<table class="table table-striped">
<thead>
<tr>
<th>Port</th>
<th>Room</th>
<th>Interface machine</th>
<th>Related</th>
<th>Détails</th>
<th></th>
</tr>
</thead>
{% for port in port_list %}
<tr>
<td>{{ port.port }}</td>
<td>{{ port.room }}</td>
<td>{{ port.machine_interface }}</td>
<td>{{ port.related }}</td>
<td>{{ port.details }}</td>
<td><a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:edit-port' port.id %}"><i class="glyphicon glyphicon-random"></i> Editer</a></td>
</tr>
{% endfor %}
</table>

View file

@ -0,0 +1,18 @@
<table class="table table-striped">
<thead>
<tr>
<th>Bâtiment</th>
<th>Numero</th>
<th>Détails</th>
<th></th>
</tr>
</thead>
{% for switch in switch_list %}
<tr>
<td>{{switch.building}}</td>
<td>{{switch.number}}</td>
<td>{{switch.details}}</td>
<td><a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:index-port' switch.pk %}"><i class="glyphicon glyphicon-list-alt"></i> Editer</a></td>
</tr>
{% endfor %}
</table>

View file

@ -0,0 +1,11 @@
{% extends "topologie/sidebar.html" %}
{% load bootstrap3 %}
{% block title %}Switchs{% endblock %}
{% block content %}
{% include "topologie/aff_switch.html" with switch_list=switch_list %}
<br />
<br />
<br />
{% endblock %}

View file

@ -0,0 +1,11 @@
{% extends "topologie/sidebar.html" %}
{% load bootstrap3 %}
{% block title %}Ports du switch{% endblock %}
{% block content %}
{% include "topologie/aff_port.html" with port_list=port_list %}
<br />
<br />
<br />
{% endblock %}

View file

@ -0,0 +1,17 @@
{% extends "topologie/sidebar.html" %}
{% load bootstrap3 %}
{% block title %}Création et modificationd 'utilisateur{% endblock %}
{% block content %}
{% bootstrap_form_errors topoform %}
<form class="form" method="post">
{% csrf_token %}
{% bootstrap_form topoform %}
{%bootstrap_button "Créer ou modifier" button_type="submit" icon="ok" %}
</form>
<br />
<br />
<br />
{% endblock %}

View file

@ -0,0 +1,5 @@
{% extends "base.html" %}
{% block sidebar %}
<p><a href="{% url "topologie:index" %}">Liste des switchs</a></p>
{% endblock %}

10
topologie/urls.py Normal file
View file

@ -0,0 +1,10 @@
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^switch/(?P<switch_id>[0-9]+)$', views.index_port, name='index-port'),
url(r'^edit_port/(?P<port_id>[0-9]+)$', views.edit_port, name='edit-port'),
]

View file

@ -1,3 +1,35 @@
from django.shortcuts import render
from django.shortcuts import render, redirect
from django.contrib import messages
# Create your views here.
from topologie.models import Switch, Port
from topologie.forms import EditPortForm
from users.views import form
def index(request):
switch_list = Switch.objects.order_by('building', 'number')
return render(request, 'topologie/index.html', {'switch_list': switch_list})
def index_port(request, switch_id):
try:
switch = Switch.objects.get(pk=switch_id)
except Switch.DoesNotExist:
messages.error(request, u"Switch inexistant")
return redirect("/topologie/")
port_list = Port.objects.filter(switch = switch).order_by('port')
return render(request, 'topologie/index_p.html', {'port_list':port_list})
def edit_port(request, port_id):
try:
port = Port.objects.get(pk=port_id)
except Port.DoesNotExist:
messages.error(request, u"Port inexistant")
return redirect("/topologie/")
port = EditPortForm(request.POST or None, instance=port)
if port.is_valid():
port.save()
messages.success(request, "Le port a bien été modifié")
return redirect("/topologie")
return form({'topoform':port}, 'topologie/port.html', request)

View file

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import users.models
class Migration(migrations.Migration):
dependencies = [
('users', '0015_whitelist'),
]
operations = [
migrations.AlterField(
model_name='ban',
name='date_end',
field=models.DateTimeField(help_text='%d/%m/%y %H:%M:%S'),
),
migrations.AlterField(
model_name='user',
name='pseudo',
field=models.CharField(unique=True, validators=[users.models.linux_user_validator], max_length=32, help_text='Doit contenir uniquement des lettres, chiffres, ou tirets'),
),
migrations.AlterField(
model_name='whitelist',
name='date_end',
field=models.DateTimeField(help_text='%d/%m/%y %H:%M:%S'),
),
]

View file

@ -1,6 +1,7 @@
from django.db import models
from django.forms import ModelForm, Form
from django import forms
import re
from django.utils import timezone
@ -15,6 +16,15 @@ def remove_user_room(room):
user.room = None
user.save()
def linux_user_validator(login):
""" Validation du pseudo pour respecter les contraintes unix"""
UNIX_LOGIN_PATTERN = re.compile("^[a-z_][a-z0-9_-]*[$]?$")
if not UNIX_LOGIN_PATTERN.match(login):
raise forms.ValidationError(
", ce pseudo ('%(label)s') contient des carractères interdits",
params={'label': login},
)
class User(models.Model):
STATE_ACTIVE = 0
STATE_DEACTIVATED = 1
@ -27,9 +37,9 @@ class User(models.Model):
name = models.CharField(max_length=255)
surname = models.CharField(max_length=255)
pseudo = models.CharField(max_length=255, unique=True)
pseudo = models.CharField(max_length=32, unique=True, help_text="Doit contenir uniquement des lettres, chiffres, ou tirets", validators=[linux_user_validator])
email = models.EmailField()
school = models.ForeignKey('School', on_delete=models.PROTECT)
school = models.ForeignKey('School', on_delete=models.PROTECT, null=False, blank=False)
comment = models.CharField(help_text="Commentaire, promo", max_length=255, blank=True)
room = models.OneToOneField('topologie.Room', on_delete=models.PROTECT, blank=True, null=True)
pwd_ssha = models.CharField(max_length=255)
@ -66,7 +76,7 @@ class Ban(models.Model):
user = models.ForeignKey('User', on_delete=models.PROTECT)
raison = models.CharField(max_length=255)
date_start = models.DateTimeField(auto_now_add=True)
date_end = models.DateTimeField(help_text='%m/%d/%y %H:%M:%S')
date_end = models.DateTimeField(help_text='%d/%m/%y %H:%M:%S')
def __str__(self):
return str(self.user) + ' ' + str(self.raison)
@ -75,24 +85,11 @@ class Whitelist(models.Model):
user = models.ForeignKey('User', on_delete=models.PROTECT)
raison = models.CharField(max_length=255)
date_start = models.DateTimeField(auto_now_add=True)
date_end = models.DateTimeField(help_text='%m/%d/%y %H:%M:%S')
date_end = models.DateTimeField(help_text='%d/%m/%y %H:%M:%S')
def __str__(self):
return str(self.user) + ' ' + str(self.raison)
class UserForm(ModelForm):
def __init__(self, *args, **kwargs):
super(InfoForm, self).__init__(*args, **kwargs)
self.fields['name'].label = 'Nom'
self.fields['surname'].label = 'Prénom'
self.fields['school'].label = 'Établissement'
self.fields['comment'].label = 'Commentaire'
self.fields['room'].label = 'Chambre'
class Meta:
model = User
fields = '__all__'
class InfoForm(ModelForm):
force = forms.BooleanField(label="Forcer le déménagement ?", initial=False, required=False)
@ -103,6 +100,8 @@ class InfoForm(ModelForm):
self.fields['school'].label = 'Établissement'
self.fields['comment'].label = 'Commentaire'
self.fields['room'].label = 'Chambre'
self.fields['room'].empty_label = "Pas de chambre"
self.fields['school'].empty_label = "Séléctionner un établissement"
def clean_force(self):
if self.cleaned_data.get('force', False):
@ -113,6 +112,10 @@ class InfoForm(ModelForm):
model = User
fields = ['name','surname','pseudo','email', 'school', 'comment', 'room']
class UserForm(InfoForm):
class Meta(InfoForm.Meta):
fields = '__all__'
class PasswordForm(ModelForm):
class Meta:
model = User
@ -128,10 +131,22 @@ class SchoolForm(ModelForm):
model = School
fields = ['name']
def __init__(self, *args, **kwargs):
super(SchoolForm, self).__init__(*args, **kwargs)
self.fields['name'].label = 'Établissement à ajouter'
class DelSchoolForm(ModelForm):
schools = forms.ModelMultipleChoiceField(queryset=School.objects.all(), label="Etablissements actuels", widget=forms.CheckboxSelectMultiple)
class Meta:
exclude = ['name']
model = School
class RightForm(ModelForm):
def __init__(self, *args, **kwargs):
super(RightForm, self).__init__(*args, **kwargs)
self.fields['right'].label = 'Droit'
self.fields['right'].empty_label = "Choisir un nouveau droit"
class Meta:
model = Right

View file

@ -14,7 +14,7 @@
<td>{{ ban.raison }}</td>
<td>{{ ban.date_start }}</td>
<td>{{ ban.date_end }}</td>
<td><a class="btn btn-primary btn-sm" role="button" href="{% url 'users:edit-ban' ban.id %}"><i class="glyphicon glyphicon-grain"></i> Editer</a></td>
<td><a class="btn btn-primary btn-sm" role="button" href="{% url 'users:edit-ban' ban.id %}"><i class="glyphicon glyphicon-pushpin"></i> Editer</a></td>
</tr>
{% endfor %}
</table>

View file

@ -4,7 +4,7 @@
<th>Prénom</th>
<th>Nom</th>
<th>Pseudo</th>
<th>Inscrit le</th>
<th>Fin de cotisation le</th>
<th>Connexion</th>
<th>Profil</th>
</tr>
@ -14,19 +14,15 @@
<td>{{ donnee.0.name }}</td>
<td>{{ donnee.0.surname }}</td>
<td>{{ donnee.0.pseudo }}</td>
<td>{{ donnee.0.registered }}</td>
<td>{{ donnee.2 }}</td>
<td>{% if donnee.1 == True %}
<font color="green">Active</font>
{% else %}
<font color="red">Désactivée</font>
{% endif %}
</td>
<td><form method="POST" action="{% url "users:profil"%}">
{% csrf_token %}
<input type="hidden" name="user" id="user" value="{{ donnee.0.pseudo }}"></input>
<button class="btn btn-primary btn-sm" type="submit"><i class="glyphicon glyphicon-tree-deciduous"></i></button>
</form>
</td>
<td><a href="{% url "users:profil" donnee.0.id%}" class="btn btn-primary btn-sm" role="button"><i class="glyphicon glyphicon-user"></i></a>
</td>
</tr>
{% endfor %}
</table>

View file

@ -14,7 +14,7 @@
<td>{{ whitelist.raison }}</td>
<td>{{ whitelist.date_start }}</td>
<td>{{ whitelist.date_end }}</td>
<td><a class="btn btn-primary btn-sm" role="button" href="{% url 'users:edit-whitelist' whitelist.id %}"><i class="glyphicon glyphicon-grain"></i> Editer</a></td>
<td><a class="btn btn-primary btn-sm" role="button" href="{% url 'users:edit-whitelist' whitelist.id %}"><i class="glyphicon glyphicon-flag"></i> Editer</a></td>
</tr>
{% endfor %}
</table>

View file

@ -4,6 +4,7 @@
{% block title %}Utilisateurs{% endblock %}
{% block content %}
<h2>Adhérents</h2>
{% include "users/aff_users.html" with users_list=users_list %}
<br />
<br />

View file

@ -0,0 +1,13 @@
{% extends "users/sidebar.html" %}
{% load bootstrap3 %}
{% block title %}Utilisateurs{% endblock %}
{% block content %}
<h2>Bannissements</h2>
{% include "users/aff_bans.html" with ban_list=ban_list %}
<br />
<br />
<br />
{% endblock %}

View file

@ -0,0 +1,13 @@
{% extends "users/sidebar.html" %}
{% load bootstrap3 %}
{% block title %}Utilisateurs{% endblock %}
{% block content %}
<h2>Accès à titre gracieux</h2>
{% include "users/aff_whitelists.html" with white_list=white_list %}
<br />
<br />
<br />
{% endblock %}

View file

@ -5,7 +5,7 @@
{% block content %}
<h2>Adhérent</h2>
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:edit-info' user.id %}"><i class="glyphicon glyphicon-fire"></i> Editer</a>
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:edit-info' user.id %}"><i class="glyphicon glyphicon-edit"></i> Editer</a>
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:password' user.id %}"><i class="glyphicon glyphicon-lock"></i> Changer le mot de passe</a>
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:state' user.id %}"><i class="glyphicon glyphicon-flash"></i> Changer le statut</a>
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:add-right' user.id %}"><i class="glyphicon glyphicon-ok"></i> Ajouter un droit</a>
@ -74,7 +74,7 @@
{% endif %}
<th>Droits</th>
{% if list_droits %}
<td>{% for droit in list_droits %}{{ droit.right }} - {% endfor %}</td>
<td>{% for droit in list_droits %}{{ droit.right }}{% if list_droits|length != forloop.counter %} - {% endif %} {% endfor %}</td>
{% else %}
<td>Aucun</td>
{% endif %}
@ -101,7 +101,7 @@
<p>Aucun bannissement</p>
{% endif %}
<h2>Accès à titre gracieux :</h2>
<h4><a class="btn btn-primary btn-sm" role="button" href="{% url 'users:add-whitelist' user.id %}"><i class="glyphicon glyphicon-pushpin"></i> Accorder un accès à titre gracieux</a></h4>
<h4><a class="btn btn-primary btn-sm" role="button" href="{% url 'users:add-whitelist' user.id %}"><i class="glyphicon glyphicon-flag"></i> Accorder un accès à titre gracieux</a></h4>
{% if white_list %}
{% include "users/aff_whitelists.html" with white_list=white_list %}
{% else %}

View file

@ -2,9 +2,10 @@
{% block sidebar %}
<p><a href="{% url "users:new-user" %}">Créer un adhérent</a></p>
<p><a href="{% url "search:search" %}">Editer un adhérent</a></p>
<p><a href="{% url "users:index" %}">Liste des adhérents</a></p>
<p><a href="{% url "search:search" %}">Ajouter un bannissement</a></p>
<p><a href="{% url "search:search" %}">Gérer les bannissements</a></p>
<p><a href="{% url "users:index-ban" %}">Liste des bannissements</a></p>
<p><a href="{% url "users:index-white" %}">Liste des accès à titre gracieux</a></p>
<p><a href="{% url "users:add-school" %}">Ajouter un établissement</a></p>
<p><a href="{% url "users:del-school" %}">Supprimer un établissement</a></p>
<p><a href="{% url "users:del-right" %}">Retirer un droit rezo</a></p>
{% endblock %}

View file

@ -13,7 +13,11 @@ urlpatterns = [
url(r'^edit_whitelist/(?P<whitelistid>[0-9]+)$', views.edit_whitelist, name='edit-whitelist'),
url(r'^add_right/(?P<userid>[0-9]+)$', views.add_right, name='add-right'),
url(r'^del_right/$', views.del_right, name='del-right'),
url(r'^profil/$', views.profil, name='profil'),
url(r'^add_school/$', views.add_school, name='add-school'),
url(r'^del_school/$', views.del_school, name='del-school'),
url(r'^profil/(?P<userid>[0-9]+)$', views.profil, name='profil'),
url(r'^index_ban/$', views.index_ban, name='index-ban'),
url(r'^index_white/$', views.index_white, name='index-white'),
url(r'^$', views.index, name='index'),
]

View file

@ -6,11 +6,11 @@ from django.shortcuts import render_to_response, get_object_or_404
from django.core.context_processors import csrf
from django.template import Context, RequestContext, loader
from django.contrib import messages
from django.db.models import Max
from django.db.models import Max, ProtectedError
from django.db import IntegrityError
from django.utils import timezone
from users.models import User, Right, Ban, DelRightForm, UserForm, InfoForm, PasswordForm, StateForm, RightForm, BanForm, ProfilForm, Whitelist, WhitelistForm
from users.models import User, Right, Ban, DelRightForm, UserForm, InfoForm, PasswordForm, StateForm, RightForm, BanForm, ProfilForm, Whitelist, WhitelistForm, DelSchoolForm, SchoolForm
from cotisations.models import Facture
from machines.models import Machine, Interface
from users.forms import PassForm
@ -62,10 +62,13 @@ def is_whitelisted(user):
def has_access(user):
""" Renvoie si un utilisateur a accès à internet"""
if user.state == User.STATE_ACTIVE and not is_ban(user) and ( is_adherent(user) or is_whitelisted(user)):
return True
else:
return False
return user.state == User.STATE_ACTIVE and not is_ban(user) and ( is_adherent(user) or is_whitelisted(user))
def is_active(interface):
""" Renvoie si une interface doit avoir accès ou non """
machine = interface.machine
user = machine.user
return machine.active and has_access(user)
def form(ctx, template, request):
c = ctx
@ -214,31 +217,63 @@ def edit_whitelist(request, whitelistid):
return redirect("/users/")
return form({'userform': whitelist}, 'users/user.html', request)
def add_school(request):
school = SchoolForm(request.POST or None)
if school.is_valid():
school.save()
messages.success(request, "L'établissement a été ajouté")
return redirect("/users/")
return form({'userform': school}, 'users/user.html', request)
def del_school(request):
school = DelSchoolForm(request.POST or None)
if school.is_valid():
school_dels = school.cleaned_data['schools']
for school_del in school_dels:
try:
school_del.delete()
messages.success(request, "L'établissement a été supprimé")
except ProtectedError:
messages.error(request, "L'établissement %s est affecté à au moins un user, vous ne pouvez pas le supprimer" % school_del)
return redirect("/users/")
return form({'userform': school}, 'users/user.html', request)
def index(request):
users_list = User.objects.order_by('pk')
connexion = []
for user in users_list:
connexion.append([user, has_access(user)])
end = end_adhesion(user)
access = has_access(user)
if(end!=None):
connexion.append([user, access, end])
else:
connexion.append([user, access, "Non adhérent"])
return render(request, 'users/index.html', {'users_list': connexion})
def profil(request):
if request.method == 'POST':
profil = ProfilForm(request.POST or None)
if profil.is_valid():
profils = profil.cleaned_data['user']
users = User.objects.get(pseudo = profils)
machines = Interface.objects.filter(machine=Machine.objects.filter(user__pseudo = users))
factures = Facture.objects.filter(user__pseudo = users)
bans = Ban.objects.filter(user__pseudo = users)
whitelists = Whitelist.objects.filter(user__pseudo = users)
end_bans = None
end_whitelists = None
if(is_ban(users)):
end_bans=end_ban(users)
if(is_whitelisted(users)):
end_whitelists=end_whitelist(users)
list_droits = Right.objects.filter(user=users)
return render(request, 'users/profil.html', {'user': users, 'machine_list' :machines, 'facture_list':factures, 'ban_list':bans, 'white_list':whitelists,'end_ban':end_bans,'end_whitelist':end_whitelists, 'end_adhesion':end_adhesion(users), 'actif':has_access(users), 'list_droits': list_droits})
return redirect("/users/")
return redirect("/users/")
def index_ban(request):
ban_list = Ban.objects.order_by('date_start')
return render(request, 'users/index_ban.html', {'ban_list':ban_list})
def index_white(request):
white_list = Whitelist.objects.order_by('date_start')
return render(request, 'users/index_whitelist.html', {'white_list':white_list})
def profil(request, userid):
try:
users = User.objects.get(pk=userid)
except User.DoesNotExist:
messages.error(request, u"Utilisateur inexistant" )
return redirect("/users/")
machines = Interface.objects.filter(machine=Machine.objects.filter(user__pseudo = users))
factures = Facture.objects.filter(user__pseudo = users)
bans = Ban.objects.filter(user__pseudo = users)
whitelists = Whitelist.objects.filter(user__pseudo = users)
end_bans = None
end_whitelists = None
if(is_ban(users)):
end_bans=end_ban(users)
if(is_whitelisted(users)):
end_whitelists=end_whitelist(users)
list_droits = Right.objects.filter(user=users)
return render(request, 'users/profil.html', {'user': users, 'machine_list' :machines, 'facture_list':factures, 'ban_list':bans, 'white_list':whitelists,'end_ban':end_bans,'end_whitelist':end_whitelists, 'end_adhesion':end_adhesion(users), 'actif':has_access(users), 'list_droits': list_droits})