mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-12-23 15:33:45 +00:00
Merge branch 'master' into documentation
This commit is contained in:
commit
309cc60ef1
190 changed files with 9749 additions and 2660 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,3 +6,4 @@ __pycache__/*
|
|||
static_files/*
|
||||
static/logo/*
|
||||
doc/build/
|
||||
media/*
|
||||
|
|
|
@ -171,10 +171,9 @@ re2o/wsgi.py permet de fonctionner avec apache2 en production
|
|||
Une fois démaré, le site web devrait être accessible.
|
||||
Pour créer un premier user, faire '''python3 manage.py createsuperuser'''
|
||||
qui va alors créer un user admin.
|
||||
Il est conseillé de créer alors les droits cableur, bureau, trésorier et infra,
|
||||
qui n'existent pas par défaut dans le menu adhérents.
|
||||
Il est également conseillé de créer un user portant le nom de
|
||||
l'association/organisation, qui possedera l'ensemble des machines.
|
||||
Il est conseillé de créer un user portant le nom de
|
||||
l'association/organisation, qui possedera l'ensemble des machines, à indiquer
|
||||
dans le menu reglages sur l'interface.
|
||||
|
||||
## Installations Optionnelles
|
||||
### Générer le schéma des dépendances
|
||||
|
|
360
api/serializers.py
Normal file
360
api/serializers.py
Normal file
|
@ -0,0 +1,360 @@
|
|||
# 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 Mael Kervella
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Serializers for the API app
|
||||
"""
|
||||
|
||||
from rest_framework import serializers
|
||||
from users.models import Club, Adherent
|
||||
from machines.models import (
|
||||
Interface,
|
||||
IpType,
|
||||
Extension,
|
||||
IpList,
|
||||
MachineType,
|
||||
Domain,
|
||||
Txt,
|
||||
Mx,
|
||||
Srv,
|
||||
Service_link,
|
||||
Ns,
|
||||
OuverturePortList,
|
||||
OuverturePort,
|
||||
Ipv6List
|
||||
)
|
||||
|
||||
|
||||
class ServiceLinkSerializer(serializers.ModelSerializer):
|
||||
name = serializers.CharField(source='service.service_type')
|
||||
|
||||
class Meta:
|
||||
model = Service_link
|
||||
fields = ('name',)
|
||||
|
||||
|
||||
class MailingSerializer(serializers.ModelSerializer):
|
||||
name = serializers.CharField(source='pseudo')
|
||||
|
||||
class Meta:
|
||||
model = Club
|
||||
fields = ('name',)
|
||||
|
||||
|
||||
class MailingMemberSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Adherent
|
||||
fields = ('email', 'name', 'surname', 'pseudo',)
|
||||
|
||||
|
||||
class IpTypeField(serializers.RelatedField):
|
||||
"""Serialisation d'une iptype, renvoie son evaluation str"""
|
||||
def to_representation(self, value):
|
||||
return value.type
|
||||
|
||||
|
||||
class IpListSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation d'une iplist, ip_type etant une foreign_key,
|
||||
on evalue sa methode str"""
|
||||
ip_type = IpTypeField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = IpList
|
||||
fields = ('ipv4', 'ip_type')
|
||||
|
||||
|
||||
class Ipv6ListSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Ipv6List
|
||||
fields = ('ipv6', 'slaac_ip')
|
||||
|
||||
|
||||
class InterfaceSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation d'une interface, ipv4, domain et extension sont
|
||||
des foreign_key, on les override et on les evalue avec des fonctions
|
||||
get_..."""
|
||||
ipv4 = IpListSerializer(read_only=True)
|
||||
mac_address = serializers.SerializerMethodField('get_macaddress')
|
||||
domain = serializers.SerializerMethodField('get_dns')
|
||||
extension = serializers.SerializerMethodField('get_interface_extension')
|
||||
|
||||
class Meta:
|
||||
model = Interface
|
||||
fields = ('ipv4', 'mac_address', 'domain', 'extension')
|
||||
|
||||
def get_dns(self, obj):
|
||||
return obj.domain.name
|
||||
|
||||
def get_interface_extension(self, obj):
|
||||
return obj.domain.extension.name
|
||||
|
||||
def get_macaddress(self, obj):
|
||||
return str(obj.mac_address)
|
||||
|
||||
|
||||
class FullInterfaceSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation complete d'une interface avec les ipv6 en plus"""
|
||||
ipv4 = IpListSerializer(read_only=True)
|
||||
ipv6 = Ipv6ListSerializer(read_only=True, many=True)
|
||||
mac_address = serializers.SerializerMethodField('get_macaddress')
|
||||
domain = serializers.SerializerMethodField('get_dns')
|
||||
extension = serializers.SerializerMethodField('get_interface_extension')
|
||||
|
||||
class Meta:
|
||||
model = Interface
|
||||
fields = ('ipv4', 'ipv6', 'mac_address', 'domain', 'extension')
|
||||
|
||||
def get_dns(self, obj):
|
||||
return obj.domain.name
|
||||
|
||||
def get_interface_extension(self, obj):
|
||||
return obj.domain.extension.name
|
||||
|
||||
def get_macaddress(self, obj):
|
||||
return str(obj.mac_address)
|
||||
|
||||
|
||||
class ExtensionNameField(serializers.RelatedField):
|
||||
"""Evaluation str d'un objet extension (.example.org)"""
|
||||
def to_representation(self, value):
|
||||
return value.name
|
||||
|
||||
|
||||
class TypeSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation d'un iptype : extension et la liste des
|
||||
ouvertures de port son evalués en get_... etant des
|
||||
foreign_key ou des relations manytomany"""
|
||||
extension = ExtensionNameField(read_only=True)
|
||||
ouverture_ports_tcp_in = serializers\
|
||||
.SerializerMethodField('get_port_policy_input_tcp')
|
||||
ouverture_ports_tcp_out = serializers\
|
||||
.SerializerMethodField('get_port_policy_output_tcp')
|
||||
ouverture_ports_udp_in = serializers\
|
||||
.SerializerMethodField('get_port_policy_input_udp')
|
||||
ouverture_ports_udp_out = serializers\
|
||||
.SerializerMethodField('get_port_policy_output_udp')
|
||||
|
||||
class Meta:
|
||||
model = IpType
|
||||
fields = ('type', 'extension', 'domaine_ip_start', 'domaine_ip_stop',
|
||||
'prefix_v6',
|
||||
'ouverture_ports_tcp_in', 'ouverture_ports_tcp_out',
|
||||
'ouverture_ports_udp_in', 'ouverture_ports_udp_out',)
|
||||
|
||||
def get_port_policy(self, obj, protocole, io):
|
||||
if obj.ouverture_ports is None:
|
||||
return []
|
||||
return map(
|
||||
str,
|
||||
obj.ouverture_ports.ouvertureport_set.filter(
|
||||
protocole=protocole
|
||||
).filter(io=io)
|
||||
)
|
||||
|
||||
def get_port_policy_input_tcp(self, obj):
|
||||
"""Renvoie la liste des ports ouverts en entrée tcp"""
|
||||
return self.get_port_policy(obj, OuverturePort.TCP, OuverturePort.IN)
|
||||
|
||||
def get_port_policy_output_tcp(self, obj):
|
||||
"""Renvoie la liste des ports ouverts en sortie tcp"""
|
||||
return self.get_port_policy(obj, OuverturePort.TCP, OuverturePort.OUT)
|
||||
|
||||
def get_port_policy_input_udp(self, obj):
|
||||
"""Renvoie la liste des ports ouverts en entrée udp"""
|
||||
return self.get_port_policy(obj, OuverturePort.UDP, OuverturePort.IN)
|
||||
|
||||
def get_port_policy_output_udp(self, obj):
|
||||
"""Renvoie la liste des ports ouverts en sortie udp"""
|
||||
return self.get_port_policy(obj, OuverturePort.UDP, OuverturePort.OUT)
|
||||
|
||||
|
||||
class ExtensionSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation d'une extension : origin_ip et la zone sont
|
||||
des foreign_key donc evalués en get_..."""
|
||||
origin = serializers.SerializerMethodField('get_origin_ip')
|
||||
zone_entry = serializers.SerializerMethodField('get_zone_name')
|
||||
soa = serializers.SerializerMethodField('get_soa_data')
|
||||
|
||||
class Meta:
|
||||
model = Extension
|
||||
fields = ('name', 'origin', 'origin_v6', 'zone_entry', 'soa')
|
||||
|
||||
def get_origin_ip(self, obj):
|
||||
return obj.origin.ipv4
|
||||
|
||||
def get_zone_name(self, obj):
|
||||
return str(obj.dns_entry)
|
||||
|
||||
def get_soa_data(self, obj):
|
||||
return { 'mail': obj.soa.dns_soa_mail, 'param': obj.soa.dns_soa_param }
|
||||
|
||||
|
||||
class MxSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation d'un MX, evaluation du nom, de la zone
|
||||
et du serveur cible, etant des foreign_key"""
|
||||
name = serializers.SerializerMethodField('get_entry_name')
|
||||
zone = serializers.SerializerMethodField('get_zone_name')
|
||||
mx_entry = serializers.SerializerMethodField('get_mx_name')
|
||||
|
||||
class Meta:
|
||||
model = Mx
|
||||
fields = ('zone', 'priority', 'name', 'mx_entry')
|
||||
|
||||
def get_entry_name(self, obj):
|
||||
return str(obj.name)
|
||||
|
||||
def get_zone_name(self, obj):
|
||||
return obj.zone.name
|
||||
|
||||
def get_mx_name(self, obj):
|
||||
return str(obj.dns_entry)
|
||||
|
||||
|
||||
class TxtSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation d'un txt : zone cible et l'entrée txt
|
||||
sont evaluées à part"""
|
||||
zone = serializers.SerializerMethodField('get_zone_name')
|
||||
txt_entry = serializers.SerializerMethodField('get_txt_name')
|
||||
|
||||
class Meta:
|
||||
model = Txt
|
||||
fields = ('zone', 'txt_entry', 'field1', 'field2')
|
||||
|
||||
def get_zone_name(self, obj):
|
||||
return str(obj.zone.name)
|
||||
|
||||
def get_txt_name(self, obj):
|
||||
return str(obj.dns_entry)
|
||||
|
||||
|
||||
class SrvSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation d'un srv : zone cible et l'entrée txt"""
|
||||
extension = serializers.SerializerMethodField('get_extension_name')
|
||||
srv_entry = serializers.SerializerMethodField('get_srv_name')
|
||||
|
||||
class Meta:
|
||||
model = Srv
|
||||
fields = (
|
||||
'service',
|
||||
'protocole',
|
||||
'extension',
|
||||
'ttl',
|
||||
'priority',
|
||||
'weight',
|
||||
'port',
|
||||
'target',
|
||||
'srv_entry'
|
||||
)
|
||||
|
||||
def get_extension_name(self, obj):
|
||||
return str(obj.extension.name)
|
||||
|
||||
def get_srv_name(self, obj):
|
||||
return str(obj.dns_entry)
|
||||
|
||||
|
||||
class NsSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation d'un NS : la zone, l'entrée ns complète et le serveur
|
||||
ns sont évalués à part"""
|
||||
zone = serializers.SerializerMethodField('get_zone_name')
|
||||
ns = serializers.SerializerMethodField('get_domain_name')
|
||||
ns_entry = serializers.SerializerMethodField('get_text_name')
|
||||
|
||||
class Meta:
|
||||
model = Ns
|
||||
fields = ('zone', 'ns', 'ns_entry')
|
||||
|
||||
def get_zone_name(self, obj):
|
||||
return obj.zone.name
|
||||
|
||||
def get_domain_name(self, obj):
|
||||
return str(obj.ns)
|
||||
|
||||
def get_text_name(self, obj):
|
||||
return str(obj.dns_entry)
|
||||
|
||||
|
||||
class DomainSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation d'un domain, extension, cname sont des foreign_key,
|
||||
et l'entrée complète, sont évalués à part"""
|
||||
extension = serializers.SerializerMethodField('get_zone_name')
|
||||
cname = serializers.SerializerMethodField('get_alias_name')
|
||||
cname_entry = serializers.SerializerMethodField('get_cname_name')
|
||||
|
||||
class Meta:
|
||||
model = Domain
|
||||
fields = ('name', 'extension', 'cname', 'cname_entry')
|
||||
|
||||
def get_zone_name(self, obj):
|
||||
return obj.extension.name
|
||||
|
||||
def get_alias_name(self, obj):
|
||||
return str(obj.cname)
|
||||
|
||||
def get_cname_name(self, obj):
|
||||
return str(obj.dns_entry)
|
||||
|
||||
|
||||
class ServicesSerializer(serializers.ModelSerializer):
|
||||
"""Evaluation d'un Service, et serialisation"""
|
||||
server = serializers.SerializerMethodField('get_server_name')
|
||||
service = serializers.SerializerMethodField('get_service_name')
|
||||
need_regen = serializers.SerializerMethodField('get_regen_status')
|
||||
|
||||
class Meta:
|
||||
model = Service_link
|
||||
fields = ('server', 'service', 'need_regen')
|
||||
|
||||
def get_server_name(self, obj):
|
||||
return str(obj.server.domain.name)
|
||||
|
||||
def get_service_name(self, obj):
|
||||
return str(obj.service)
|
||||
|
||||
def get_regen_status(self, obj):
|
||||
return obj.need_regen()
|
||||
|
||||
|
||||
class OuverturePortsSerializer(serializers.Serializer):
|
||||
"""Serialisation de l'ouverture des ports"""
|
||||
ipv4 = serializers.SerializerMethodField()
|
||||
ipv6 = serializers.SerializerMethodField()
|
||||
|
||||
def get_ipv4():
|
||||
return {i.ipv4.ipv4:
|
||||
{
|
||||
"tcp_in":[j.tcp_ports_in() for j in i.port_lists.all()],
|
||||
"tcp_out":[j.tcp_ports_out()for j in i.port_lists.all()],
|
||||
"udp_in":[j.udp_ports_in() for j in i.port_lists.all()],
|
||||
"udp_out":[j.udp_ports_out() for j in i.port_lists.all()],
|
||||
}
|
||||
for i in Interface.objects.all() if i.ipv4
|
||||
}
|
||||
|
||||
def get_ipv6():
|
||||
return {i.ipv6:
|
||||
{
|
||||
"tcp_in":[j.tcp_ports_in() for j in i.port_lists.all()],
|
||||
"tcp_out":[j.tcp_ports_out()for j in i.port_lists.all()],
|
||||
"udp_in":[j.udp_ports_in() for j in i.port_lists.all()],
|
||||
"udp_out":[j.udp_ports_out() for j in i.port_lists.all()],
|
||||
}
|
||||
for i in Interface.objects.all() if i.ipv6
|
||||
}
|
25
api/tests.py
Normal file
25
api/tests.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
# 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 © 2017 Gabriel Détraz
|
||||
# Copyright © 2017 Goulven Kermarec
|
||||
# Copyright © 2017 Augustin Lemesle
|
||||
#
|
||||
# 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.
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
59
api/urls.py
Normal file
59
api/urls.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
# 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 Mael Kervella
|
||||
#
|
||||
# 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.
|
||||
"""api.urls
|
||||
|
||||
Urls de l'api, pointant vers les fonctions de views
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
# Services
|
||||
url(r'^services/$', views.services),
|
||||
url(r'^services/(?P<server_name>\w+)/(?P<service_name>\w+)/regen/$', views.services_server_service_regen),
|
||||
url(r'^services/(?P<server_name>\w+)/$', views.services_server),
|
||||
|
||||
# DNS
|
||||
url(r'^dns/mac-ip-dns/$', views.dns_mac_ip_dns),
|
||||
url(r'^dns/alias/$', views.dns_alias),
|
||||
url(r'^dns/corresp/$', views.dns_corresp),
|
||||
url(r'^dns/mx/$', views.dns_mx),
|
||||
url(r'^dns/ns/$', views.dns_ns),
|
||||
url(r'^dns/txt/$', views.dns_txt),
|
||||
url(r'^dns/srv/$', views.dns_srv),
|
||||
url(r'^dns/zones/$', views.dns_zones),
|
||||
|
||||
# Firewall
|
||||
url(r'^firewall/ouverture_ports/$', views.firewall_ouverture_ports),
|
||||
|
||||
# DHCP
|
||||
url(r'^dhcp/mac-ip/$', views.dhcp_mac_ip),
|
||||
|
||||
# Mailings
|
||||
url(r'^mailing/standard/$', views.mailing_standard),
|
||||
url(r'^mailing/standard/(?P<ml_name>\w+)/members/$', views.mailing_standard_ml_members),
|
||||
url(r'^mailing/club/$', views.mailing_club),
|
||||
url(r'^mailing/club/(?P<ml_name>\w+)/members/$', views.mailing_club_ml_members),
|
||||
]
|
114
api/utils.py
Normal file
114
api/utils.py
Normal file
|
@ -0,0 +1,114 @@
|
|||
# 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 Maël Kervella
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""api.utils.
|
||||
|
||||
Set of various and usefull functions for the API app
|
||||
"""
|
||||
|
||||
from rest_framework.renderers import JSONRenderer
|
||||
from django.http import HttpResponse
|
||||
|
||||
class JSONResponse(HttpResponse):
|
||||
"""A JSON response that can be send as an HTTP response.
|
||||
Usefull in case of REST API.
|
||||
"""
|
||||
|
||||
def __init__(self, data, **kwargs):
|
||||
"""Initialisz a JSONResponse object.
|
||||
|
||||
Args:
|
||||
data: the data to render as JSON (often made of lists, dicts,
|
||||
strings, boolean and numbers). See `JSONRenderer.render(data)` for
|
||||
further details.
|
||||
|
||||
Creates:
|
||||
An HTTPResponse containing the data in JSON format.
|
||||
"""
|
||||
|
||||
content = JSONRenderer().render(data)
|
||||
kwargs['content_type'] = 'application/json'
|
||||
super(JSONResponse, self).__init__(content, **kwargs)
|
||||
|
||||
|
||||
class JSONError(JSONResponse):
|
||||
"""A JSON response when the request failed.
|
||||
"""
|
||||
|
||||
def __init__(self, error_msg, data=None, **kwargs):
|
||||
"""Initialise a JSONError object.
|
||||
|
||||
Args:
|
||||
error_msg: A message explaining where the error is.
|
||||
data: An optional field for further data to send along.
|
||||
|
||||
Creates:
|
||||
A JSONResponse containing a field `status` set to `error` and a field
|
||||
`reason` containing `error_msg`. If `data` argument has been given,
|
||||
a field `data` containing it is added to the JSON response.
|
||||
"""
|
||||
|
||||
response = {
|
||||
'status' : 'error',
|
||||
'reason' : error_msg
|
||||
}
|
||||
if data is not None:
|
||||
response['data'] = data
|
||||
super(JSONError, self).__init__(response, **kwargs)
|
||||
|
||||
|
||||
class JSONSuccess(JSONResponse):
|
||||
"""A JSON response when the request suceeded.
|
||||
"""
|
||||
|
||||
def __init__(self, data=None, **kwargs):
|
||||
"""Initialise a JSONSucess object.
|
||||
|
||||
Args:
|
||||
error_msg: A message explaining where the error is.
|
||||
data: An optional field for further data to send along.
|
||||
|
||||
Creates:
|
||||
A JSONResponse containing a field `status` set to `sucess`. If `data`
|
||||
argument has been given, a field `data` containing it is added to the
|
||||
JSON response.
|
||||
"""
|
||||
|
||||
response = {
|
||||
'status' : 'success',
|
||||
}
|
||||
if data is not None:
|
||||
response['data'] = data
|
||||
super(JSONSuccess, self).__init__(response, **kwargs)
|
||||
|
||||
|
||||
def accept_method(methods):
|
||||
"""Decorator to set a list of accepted request method.
|
||||
Check if the method used is accepted. If not, send a NotAllowed response.
|
||||
"""
|
||||
def decorator(view):
|
||||
def wrapper(request, *args, **kwargs):
|
||||
if request.method in methods:
|
||||
return view(request, *args, **kwargs)
|
||||
else:
|
||||
return JSONError('Invalid request method. Request methods authorize are '+str(methods))
|
||||
return view(request, *args, **kwargs)
|
||||
return wrapper
|
||||
return decorator
|
464
api/views.py
Normal file
464
api/views.py
Normal file
|
@ -0,0 +1,464 @@
|
|||
# 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 Maël Kervella
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""api.views
|
||||
|
||||
The views for the API app. They should all return JSON data and not fallback on
|
||||
HTML pages such as the login and index pages for a better integration.
|
||||
"""
|
||||
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
from re2o.utils import all_has_access, all_active_assigned_interfaces
|
||||
|
||||
from users.models import Club
|
||||
from machines.models import (Service_link, Service, Interface, Domain,
|
||||
OuverturePortList)
|
||||
|
||||
from .serializers import *
|
||||
from .utils import JSONError, JSONSuccess, accept_method
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('machines.serveur')
|
||||
@accept_method(['GET'])
|
||||
def services(request):
|
||||
"""The list of the different services and servers couples
|
||||
|
||||
Return:
|
||||
GET:
|
||||
A JSONSuccess response with a field `data` containing:
|
||||
* a list of dictionnaries (one for each service-server couple) containing:
|
||||
* a field `server`: the server name
|
||||
* a field `service`: the service name
|
||||
* a field `need_regen`: does the service need a regeneration ?
|
||||
"""
|
||||
service_link = Service_link.objects.all().select_related('server__domain').select_related('service')
|
||||
seria = ServicesSerializer(service_link, many=True)
|
||||
return JSONSuccess(seria.data)
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('machines.serveur')
|
||||
@accept_method(['GET', 'POST'])
|
||||
def services_server_service_regen(request, server_name, service_name):
|
||||
"""The status of a particular service linked to a particular server.
|
||||
Mark the service as regenerated if POST used.
|
||||
|
||||
Returns:
|
||||
GET:
|
||||
A JSONSucess response with a field `data` containing:
|
||||
* a field `need_regen`: does the service need a regeneration ?
|
||||
|
||||
POST:
|
||||
An empty JSONSuccess response.
|
||||
"""
|
||||
query = Service_link.objects.filter(
|
||||
service__in=Service.objects.filter(service_type=service_name),
|
||||
server__in=Interface.objects.filter(
|
||||
domain__in=Domain.objects.filter(name=server_name)
|
||||
)
|
||||
)
|
||||
if not query:
|
||||
return JSONError("This service is not active for this server")
|
||||
|
||||
service = query.first()
|
||||
if request.method == 'GET':
|
||||
return JSONSuccess({'need_regen': service.need_regen()})
|
||||
else:
|
||||
service.done_regen()
|
||||
return JSONSuccess()
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('machines.serveur')
|
||||
@accept_method(['GET'])
|
||||
def services_server(request, server_name):
|
||||
"""The list of services attached to a specific server
|
||||
|
||||
Returns:
|
||||
GET:
|
||||
A JSONSuccess response with a field `data` containing:
|
||||
* a list of dictionnaries (one for each service) containing:
|
||||
* a field `name`: the name of a service
|
||||
"""
|
||||
query = Service_link.objects.filter(
|
||||
server__in=Interface.objects.filter(
|
||||
domain__in=Domain.objects.filter(name=server_name)
|
||||
)
|
||||
)
|
||||
if not query:
|
||||
return JSONError("This service is not active for this server")
|
||||
|
||||
services = query.all()
|
||||
seria = ServiceLinkSerializer(services, many=True)
|
||||
return JSONSuccess(seria.data)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('machines.serveur')
|
||||
@accept_method(['GET'])
|
||||
def dns_mac_ip_dns(request):
|
||||
"""The list of all active interfaces with all the associated infos
|
||||
(MAC, IP, IpType, DNS name and associated zone extension)
|
||||
|
||||
Returns:
|
||||
GET:
|
||||
A JSON Success response with a field `data` containing:
|
||||
* a list of dictionnaries (one for each interface) containing:
|
||||
* a field `ipv4` containing:
|
||||
* a field `ipv4`: the ip for this interface
|
||||
* a field `ip_type`: the name of the IpType of this interface
|
||||
* a field `ipv6` containing `null` if ipv6 is deactivated else:
|
||||
* a field `ipv6`: the ip for this interface
|
||||
* a field `ip_type`: the name of the IpType of this interface
|
||||
* a field `mac_address`: the MAC of this interface
|
||||
* a field `domain`: the DNS name for this interface
|
||||
* a field `extension`: the extension for the DNS zone of this interface
|
||||
"""
|
||||
interfaces = all_active_assigned_interfaces(full=True)
|
||||
seria = FullInterfaceSerializer(interfaces, many=True)
|
||||
return JSONSuccess(seria.data)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('machines.serveur')
|
||||
@accept_method(['GET'])
|
||||
def dns_alias(request):
|
||||
"""The list of all the alias used and the DNS info associated
|
||||
|
||||
Returns:
|
||||
GET:
|
||||
A JSON Success response with a field `data` containing:
|
||||
* a list of dictionnaries (one for each alias) containing:
|
||||
* a field `name`: the alias used
|
||||
* a field `cname`: the target of the alias (real name of the interface)
|
||||
* a field `cname_entry`: the entry to write in the DNS to have the alias
|
||||
* a field `extension`: the extension for the DNS zone of this interface
|
||||
"""
|
||||
alias = Domain.objects.filter(interface_parent=None).filter(cname__in=Domain.objects.filter(interface_parent__in=Interface.objects.exclude(ipv4=None))).select_related('extension').select_related('cname__extension')
|
||||
seria = DomainSerializer(alias, many=True)
|
||||
return JSONSuccess(seria.data)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('machines.serveur')
|
||||
@accept_method(['GET'])
|
||||
def dns_corresp(request):
|
||||
"""The list of the IpTypes possible with the infos about each
|
||||
|
||||
Returns:
|
||||
GET:
|
||||
A JSON Success response with a field `data` containing:
|
||||
* a list of dictionnaries (one for each IpType) containing:
|
||||
* a field `type`: the name of the type
|
||||
* a field `extension`: the DNS extension associated
|
||||
* a field `domain_ip_start`: the first ip to use for this type
|
||||
* a field `domain_ip_stop`: the last ip to use for this type
|
||||
* a field `prefix_v6`: `null` if IPv6 is deactivated else the prefix to use
|
||||
* a field `ouverture_ports_tcp_in`: the policy for TCP IN ports
|
||||
* a field `ouverture_ports_tcp_out`: the policy for TCP OUT ports
|
||||
* a field `ouverture_ports_udp_in`: the policy for UDP IN ports
|
||||
* a field `ouverture_ports_udp_out`: the policy for UDP OUT ports
|
||||
"""
|
||||
ip_type = IpType.objects.all().select_related('extension')
|
||||
seria = TypeSerializer(ip_type, many=True)
|
||||
return JSONSuccess(seria.data)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('machines.serveur')
|
||||
@accept_method(['GET'])
|
||||
def dns_mx(request):
|
||||
"""The list of MX record to add to the DNS
|
||||
|
||||
Returns:
|
||||
GET:
|
||||
A JSON Success response with a field `data` containing:
|
||||
* a list of dictionnaries (one for each MX record) containing:
|
||||
* a field `zone`: the extension for the concerned zone
|
||||
* a field `priority`: the priority to use
|
||||
* a field `name`: the name of the target
|
||||
* a field `mx_entry`: the full entry to add in the DNS for this MX record
|
||||
"""
|
||||
mx = Mx.objects.all().select_related('zone').select_related('name__extension')
|
||||
seria = MxSerializer(mx, many=True)
|
||||
return JSONSuccess(seria.data)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('machines.serveur')
|
||||
@accept_method(['GET'])
|
||||
def dns_ns(request):
|
||||
"""The list of NS record to add to the DNS
|
||||
|
||||
Returns:
|
||||
GET:
|
||||
A JSON Success response with a field `data` containing:
|
||||
* a list of dictionnaries (one for each NS record) containing:
|
||||
* a field `zone`: the extension for the concerned zone
|
||||
* a field `ns`: the DNS name for the NS server targeted
|
||||
* a field `ns_entry`: the full entry to add in the DNS for this NS record
|
||||
"""
|
||||
ns = Ns.objects.exclude(ns__in=Domain.objects.filter(interface_parent__in=Interface.objects.filter(ipv4=None))).select_related('zone').select_related('ns__extension')
|
||||
seria = NsSerializer(ns, many=True)
|
||||
return JSONSuccess(seria.data)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('machines.serveur')
|
||||
@accept_method(['GET'])
|
||||
def dns_txt(request):
|
||||
"""The list of TXT record to add to the DNS
|
||||
|
||||
Returns:
|
||||
GET:
|
||||
A JSON Success response with a field `data` containing:
|
||||
* a list of dictionnaries (one for each TXT record) containing:
|
||||
* a field `zone`: the extension for the concerned zone
|
||||
* a field `field1`: the first field in the record (target)
|
||||
* a field `field2`: the second field in the record (value)
|
||||
* a field `txt_entry`: the full entry to add in the DNS for this TXT record
|
||||
"""
|
||||
txt = Txt.objects.all().select_related('zone')
|
||||
seria = TxtSerializer(txt, many=True)
|
||||
return JSONSuccess(seria.data)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('machines.serveur')
|
||||
@accept_method(['GET'])
|
||||
def dns_srv(request):
|
||||
"""The list of SRV record to add to the DNS
|
||||
|
||||
Returns:
|
||||
GET:
|
||||
A JSON Success response with a field `data` containing:
|
||||
* a list of dictionnaries (one for each SRV record) containing:
|
||||
* a field `extension`: the extension for the concerned zone
|
||||
* a field `service`: the name of the service concerned
|
||||
* a field `protocole`: the name of the protocol to use
|
||||
* a field `ttl`: the Time To Live to use
|
||||
* a field `priority`: the priority for this service
|
||||
* a field `weight`: the weight for same priority entries
|
||||
* a field `port`: the port targeted
|
||||
* a field `target`: the interface targeted by this service
|
||||
* a field `srv_entry`: the full entry to add in the DNS for this SRV record
|
||||
"""
|
||||
srv = Srv.objects.all().select_related('extension').select_related('target__extension')
|
||||
seria = SrvSerializer(srv, many=True)
|
||||
return JSONSuccess(seria.data)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('machines.serveur')
|
||||
@accept_method(['GET'])
|
||||
def dns_zones(request):
|
||||
"""The list of the zones managed
|
||||
|
||||
Returns:
|
||||
GET:
|
||||
A JSON Success response with a field `data` containing:
|
||||
* a list of dictionnaries (one for each zone) containing:
|
||||
* a field `name`: the extension for the zone
|
||||
* a field `origin`: the server IPv4 for the orgin of the zone
|
||||
* a field `origin_v6`: `null` if ipv6 is deactivated else the server IPv6 for the origin of the zone
|
||||
* a field `soa` containing:
|
||||
* a field `mail` containing the mail to contact in case of problem with the zone
|
||||
* a field `param` containing the full soa paramters to use in the DNS for this zone
|
||||
* a field `zone_entry`: the full entry to add in the DNS for the origin of the zone
|
||||
"""
|
||||
zones = Extension.objects.all().select_related('origin')
|
||||
seria = ExtensionSerializer(zones, many=True)
|
||||
return JSONSuccess(seria.data)
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('machines.serveur')
|
||||
@accept_method(['GET'])
|
||||
def firewall_ouverture_ports(request):
|
||||
"""The list of the ports authorized to be openned by the firewall
|
||||
|
||||
Returns:
|
||||
GET:
|
||||
A JSONSuccess response with a `data` field containing:
|
||||
* a field `ipv4` containing:
|
||||
* a field `tcp_in` containing:
|
||||
* a list of port number where ipv4 tcp in should be ok
|
||||
* a field `tcp_out` containing:
|
||||
* a list of port number where ipv4 tcp ou should be ok
|
||||
* a field `udp_in` containing:
|
||||
* a list of port number where ipv4 udp in should be ok
|
||||
* a field `udp_out` containing:
|
||||
* a list of port number where ipv4 udp out should be ok
|
||||
* a field `ipv6` containing:
|
||||
* a field `tcp_in` containing:
|
||||
* a list of port number where ipv6 tcp in should be ok
|
||||
* a field `tcp_out` containing:
|
||||
* a list of port number where ipv6 tcp ou should be ok
|
||||
* a field `udp_in` containing:
|
||||
* a list of port number where ipv6 udp in should be ok
|
||||
* a field `udp_out` containing:
|
||||
* a list of port number where ipv6 udp out should be ok
|
||||
"""
|
||||
r = {'ipv4':{}, 'ipv6':{}}
|
||||
for o in OuverturePortList.objects.all().prefetch_related('ouvertureport_set').prefetch_related('interface_set', 'interface_set__ipv4'):
|
||||
pl = {
|
||||
"tcp_in":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.TCP, io=OuverturePort.IN))),
|
||||
"tcp_out":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.TCP, io=OuverturePort.OUT))),
|
||||
"udp_in":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.UDP, io=OuverturePort.IN))),
|
||||
"udp_out":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.UDP, io=OuverturePort.OUT))),
|
||||
}
|
||||
for i in filter_active_interfaces(o.interface_set):
|
||||
if i.may_have_port_open():
|
||||
d = r['ipv4'].get(i.ipv4.ipv4, {})
|
||||
d["tcp_in"] = d.get("tcp_in",set()).union(pl["tcp_in"])
|
||||
d["tcp_out"] = d.get("tcp_out",set()).union(pl["tcp_out"])
|
||||
d["udp_in"] = d.get("udp_in",set()).union(pl["udp_in"])
|
||||
d["udp_out"] = d.get("udp_out",set()).union(pl["udp_out"])
|
||||
r['ipv4'][i.ipv4.ipv4] = d
|
||||
if i.ipv6():
|
||||
for ipv6 in i.ipv6():
|
||||
d = r['ipv6'].get(ipv6.ipv6, {})
|
||||
d["tcp_in"] = d.get("tcp_in",set()).union(pl["tcp_in"])
|
||||
d["tcp_out"] = d.get("tcp_out",set()).union(pl["tcp_out"])
|
||||
d["udp_in"] = d.get("udp_in",set()).union(pl["udp_in"])
|
||||
d["udp_out"] = d.get("udp_out",set()).union(pl["udp_out"])
|
||||
r['ipv6'][ipv6.ipv6] = d
|
||||
return JSONSuccess(r)
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('machines.serveur')
|
||||
@accept_method(['GET'])
|
||||
def dhcp_mac_ip(request):
|
||||
"""The list of all active interfaces with all the associated infos
|
||||
(MAC, IP, IpType, DNS name and associated zone extension)
|
||||
|
||||
Returns:
|
||||
GET:
|
||||
A JSON Success response with a field `data` containing:
|
||||
* a list of dictionnaries (one for each interface) containing:
|
||||
* a field `ipv4` containing:
|
||||
* a field `ipv4`: the ip for this interface
|
||||
* a field `ip_type`: the name of the IpType of this interface
|
||||
* a field `mac_address`: the MAC of this interface
|
||||
* a field `domain`: the DNS name for this interface
|
||||
* a field `extension`: the extension for the DNS zone of this interface
|
||||
"""
|
||||
interfaces = all_active_assigned_interfaces()
|
||||
seria = InterfaceSerializer(interfaces, many=True)
|
||||
return JSONSuccess(seria.data)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('machines.serveur')
|
||||
@accept_method(['GET'])
|
||||
def mailing_standard(request):
|
||||
"""All the available standard mailings.
|
||||
|
||||
Returns:
|
||||
GET:
|
||||
A JSONSucess response with a field `data` containing:
|
||||
* a list of dictionnaries (one for each mailing) containing:
|
||||
* a field `name`: the name of a mailing
|
||||
"""
|
||||
return JSONSuccess([
|
||||
{'name': 'adherents'}
|
||||
])
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('machines.serveur')
|
||||
@accept_method(['GET'])
|
||||
def mailing_standard_ml_members(request):
|
||||
"""All the members of a specific standard mailing
|
||||
|
||||
Returns:
|
||||
GET:
|
||||
A JSONSucess response with a field `data` containing:
|
||||
* a list if dictionnaries (one for each member) containing:
|
||||
* a field `email`: the email of the member
|
||||
* a field `name`: the name of the member
|
||||
* a field `surname`: the surname of the member
|
||||
* a field `pseudo`: the pseudo of the member
|
||||
"""
|
||||
# All with active connextion
|
||||
if ml_name == 'adherents':
|
||||
members = all_has_access().values('email').distinct()
|
||||
# Unknown mailing
|
||||
else:
|
||||
return JSONError("This mailing does not exist")
|
||||
seria = MailingMemberSerializer(members, many=True)
|
||||
return JSONSuccess(seria.data)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('machines.serveur')
|
||||
@accept_method(['GET'])
|
||||
def mailing_club(request):
|
||||
"""All the available club mailings.
|
||||
|
||||
Returns:
|
||||
GET:
|
||||
A JSONSucess response with a field `data` containing:
|
||||
* a list of dictionnaries (one for each mailing) containing:
|
||||
* a field `name` indicating the name of a mailing
|
||||
"""
|
||||
clubs = Club.objects.filter(mailing=True).values('pseudo')
|
||||
seria = MailingSerializer(clubs, many=True)
|
||||
return JSONSuccess(seria.data)
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('machines.serveur')
|
||||
@accept_method(['GET'])
|
||||
def mailing_club_ml_members(request):
|
||||
"""All the members of a specific club mailing
|
||||
|
||||
Returns:
|
||||
GET:
|
||||
A JSONSucess response with a field `data` containing:
|
||||
* a list if dictionnaries (one for each member) containing:
|
||||
* a field `email`: the email of the member
|
||||
* a field `name`: the name of the member
|
||||
* a field `surname`: the surname of the member
|
||||
* a field `pseudo`: the pseudo of the member
|
||||
"""
|
||||
try:
|
||||
club = Club.objects.get(mailing=True, pseudo=ml_name)
|
||||
except Club.DoesNotExist:
|
||||
return JSONError("This mailing does not exist")
|
||||
members = club.administrators.all().values('email').distinct()
|
||||
seria = MailingMemberSerializer(members, many=True)
|
||||
return JSONSuccess(seria.data)
|
3
contributors.py
Normal file
3
contributors.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
contributeurs = ['Gabriel Detraz', 'chirac', 'Maël Kervella', 'LEVY-FALK Hugo', 'Dalahro', 'lhark', 'root', 'Hugo LEVY-FALK', 'Chirac', 'guimoz', 'Mael Kervella', 'klafyvel', 'matthieu', 'Yoann Pietri', 'Simon Brélivet', 'chibrac', 'David Sinquin', 'Pierre Cadart', 'moamoak', 'Éloi Alain', 'FERNET Laouen', 'Hugo Levy-Falk', 'Joanne Steiner', 'Matthieu Michelet', 'Yoann PIETRI', 'B', 'Daniel STAN', 'Eloi Alain', 'Guimoz', 'Hugo Hervieux', 'Laouen Fernet', 'Lemesle', 'MICHELET matthieu', 'Nymous', 'Thibault de BOUTRAY', 'Tipunchetrhum', 'Éloi ALAIN']
|
|
@ -21,3 +21,4 @@
|
|||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
from .acl import *
|
||||
|
|
40
cotisations/acl.py
Normal file
40
cotisations/acl.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
# -*- 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 © 2017 Gabriel Détraz
|
||||
# Copyright © 2017 Goulven Kermarec
|
||||
# Copyright © 2017 Augustin Lemesle
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""cotisations.acl
|
||||
|
||||
Here are defined some functions to check acl on the application.
|
||||
"""
|
||||
|
||||
def can_view(user):
|
||||
"""Check if an user can view the application.
|
||||
|
||||
Args:
|
||||
user: The user who wants to view the application.
|
||||
|
||||
Returns:
|
||||
A couple (allowed, msg) where allowed is a boolean which is True if
|
||||
viewing is granted and msg is a message (can be None).
|
||||
"""
|
||||
can = user.has_module_perms('cotisations')
|
||||
return can, None if can else "Vous ne pouvez pas voir cette application."
|
|
@ -26,9 +26,8 @@ importé par les views.
|
|||
Permet de créer une nouvelle facture pour un user (NewFactureForm),
|
||||
et de l'editer (soit l'user avec EditFactureForm,
|
||||
soit le trésorier avec TrezEdit qui a plus de possibilités que self
|
||||
notamment sur le controle trésorier)
|
||||
|
||||
SelectArticleForm est utilisée lors de la creation d'une facture en
|
||||
notamment sur le controle trésorier SelectArticleForm est utilisée
|
||||
lors de la creation d'une facture en
|
||||
parrallèle de NewFacture pour le choix des articles désirés.
|
||||
(la vue correspondante est unique)
|
||||
|
||||
|
@ -40,8 +39,12 @@ from __future__ import unicode_literals
|
|||
from django import forms
|
||||
from django.db.models import Q
|
||||
from django.forms import ModelForm, Form
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.core.validators import MinValueValidator,MaxValueValidator
|
||||
from .models import Article, Paiement, Facture, Banque
|
||||
from preferences.models import OptionalUser
|
||||
from users.models import User
|
||||
|
||||
from re2o.field_permissions import FieldPermissionFormMixin
|
||||
|
||||
|
||||
class NewFactureForm(ModelForm):
|
||||
|
@ -141,27 +144,18 @@ class NewFactureFormPdf(Form):
|
|||
)
|
||||
|
||||
|
||||
class EditFactureForm(NewFactureForm):
|
||||
class EditFactureForm(FieldPermissionFormMixin, NewFactureForm):
|
||||
"""Edition d'une facture : moyen de paiement, banque, user parent"""
|
||||
class Meta(NewFactureForm.Meta):
|
||||
fields = ['paiement', 'banque', 'cheque', 'user']
|
||||
model = Facture
|
||||
fields = '__all__'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(EditFactureForm, self).__init__(*args, **kwargs)
|
||||
self.fields['user'].label = 'Adherent'
|
||||
self.fields['user'].empty_label = "Séléctionner\
|
||||
l'adhérent propriétaire"
|
||||
|
||||
|
||||
class TrezEditFactureForm(EditFactureForm):
|
||||
"""Vue pour édition controle trésorier"""
|
||||
class Meta(EditFactureForm.Meta):
|
||||
fields = '__all__'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TrezEditFactureForm, self).__init__(*args, **kwargs)
|
||||
self.fields['valid'].label = 'Validité de la facture'
|
||||
self.fields['control'].label = 'Contrôle de la facture'
|
||||
|
||||
|
||||
class ArticleForm(ModelForm):
|
||||
|
@ -180,11 +174,19 @@ class DelArticleForm(Form):
|
|||
"""Suppression d'un ou plusieurs articles en vente. Choix
|
||||
parmis les modèles"""
|
||||
articles = forms.ModelMultipleChoiceField(
|
||||
queryset=Article.objects.all(),
|
||||
queryset=Article.objects.none(),
|
||||
label="Articles actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop('instances', None)
|
||||
super(DelArticleForm, self).__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields['articles'].queryset = instances
|
||||
else:
|
||||
self.fields['articles'].queryset = Article.objects.all()
|
||||
|
||||
|
||||
class PaiementForm(ModelForm):
|
||||
"""Creation d'un moyen de paiement, champ text moyen et type
|
||||
|
@ -204,11 +206,19 @@ class DelPaiementForm(Form):
|
|||
"""Suppression d'un ou plusieurs moyens de paiements, selection
|
||||
parmis les models"""
|
||||
paiements = forms.ModelMultipleChoiceField(
|
||||
queryset=Paiement.objects.all(),
|
||||
queryset=Paiement.objects.none(),
|
||||
label="Moyens de paiement actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop('instances', None)
|
||||
super(DelPaiementForm, self).__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields['paiements'].queryset = instances
|
||||
else:
|
||||
self.fields['paiements'].queryset = Paiement.objects.all()
|
||||
|
||||
|
||||
class BanqueForm(ModelForm):
|
||||
"""Creation d'une banque, field name"""
|
||||
|
@ -225,7 +235,69 @@ class BanqueForm(ModelForm):
|
|||
class DelBanqueForm(Form):
|
||||
"""Selection d'une ou plusieurs banques, pour suppression"""
|
||||
banques = forms.ModelMultipleChoiceField(
|
||||
queryset=Banque.objects.all(),
|
||||
queryset=Banque.objects.none(),
|
||||
label="Banques actuelles",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop('instances', None)
|
||||
super(DelBanqueForm, self).__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields['banques'].queryset = instances
|
||||
else:
|
||||
self.fields['banques'].queryset = Banque.objects.all()
|
||||
|
||||
|
||||
class NewFactureSoldeForm(NewFactureForm):
|
||||
"""Creation d'une facture, moyen de paiement, banque et numero
|
||||
de cheque"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
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\
|
||||
une bite de paiement"
|
||||
paiement_list = Paiement.objects.filter(type_paiement=1)
|
||||
if paiement_list:
|
||||
self.fields['paiement'].widget\
|
||||
.attrs['data-cheque'] = paiement_list.first().id
|
||||
|
||||
class Meta:
|
||||
model = Facture
|
||||
fields = ['paiement', 'banque']
|
||||
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(NewFactureSoldeForm, self).clean()
|
||||
paiement = cleaned_data.get("paiement")
|
||||
cheque = cleaned_data.get("cheque")
|
||||
banque = cleaned_data.get("banque")
|
||||
if not paiement:
|
||||
raise forms.ValidationError("Le moyen de paiement est obligatoire")
|
||||
elif paiement.type_paiement == "check" and not (cheque and banque):
|
||||
raise forms.ValidationError("Le numéro de chèque et\
|
||||
la banque sont obligatoires.")
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class RechargeForm(Form):
|
||||
value = forms.FloatField(
|
||||
label='Valeur',
|
||||
min_value=0.01,
|
||||
validators = []
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user = kwargs.pop('user')
|
||||
super(RechargeForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def clean_value(self):
|
||||
value = self.cleaned_data['value']
|
||||
if value < OptionalUser.get_cached_value('min_online_payment'):
|
||||
raise forms.ValidationError("Montant inférieur au montant minimal de paiement en ligne (%s) €" % OptionalUser.get_cached_value('min_online_payment'))
|
||||
if value + self.user.solde > OptionalUser.get_cached_value('max_solde'):
|
||||
raise forms.ValidationError("Le solde ne peux excéder %s " % OptionalUser.get_cached_value('max_solde'))
|
||||
return value
|
||||
|
|
|
@ -59,7 +59,7 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='cotisation',
|
||||
name='type_cotisation',
|
||||
field=models.CharField(choices=[('Connexion', 'Connexion'), ('Adhesion', 'Adhesion'), ('All', 'All')], max_length=255),
|
||||
field=models.CharField(choices=[('Connexion', 'Connexion'), ('Adhesion', 'Adhesion'), ('All', 'All')], max_length=255, default='All'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='vente',
|
||||
|
|
39
cotisations/migrations/0028_auto_20171231_0007.py
Normal file
39
cotisations/migrations/0028_auto_20171231_0007.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-12-30 23:07
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cotisations', '0027_auto_20171029_1156'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='article',
|
||||
options={'permissions': (('view_article', 'Peut voir un objet article'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='banque',
|
||||
options={'permissions': (('view_banque', 'Peut voir un objet banque'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='cotisation',
|
||||
options={'permissions': (('view_cotisation', 'Peut voir un objet cotisation'), ('change_all_cotisation', 'Superdroit, peut modifier toutes les cotisations'))},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='facture',
|
||||
options={'permissions': (('change_facture_control', "Peut changer l'etat de controle"), ('change_facture_pdf', 'Peut éditer une facture pdf'), ('view_facture', 'Peut voir un objet facture'), ('change_all_facture', 'Superdroit, peut modifier toutes les factures'))},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='paiement',
|
||||
options={'permissions': (('view_paiement', 'Peut voir un objet paiement'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='vente',
|
||||
options={'permissions': (('view_vente', 'Peut voir un objet vente'), ('change_all_vente', 'Superdroit, peut modifier toutes les ventes'))},
|
||||
),
|
||||
]
|
|
@ -56,8 +56,10 @@ from django.db.models import Max
|
|||
from django.utils import timezone
|
||||
from machines.models import regen
|
||||
|
||||
from re2o.field_permissions import FieldPermissionModelMixin
|
||||
|
||||
class Facture(models.Model):
|
||||
|
||||
class Facture(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
|
||||
|
@ -76,6 +78,15 @@ class Facture(models.Model):
|
|||
valid = models.BooleanField(default=True)
|
||||
control = models.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
abstract = False
|
||||
permissions = (
|
||||
("change_facture_control", "Peut changer l'etat de controle"),
|
||||
("change_facture_pdf", "Peut éditer une facture pdf"),
|
||||
("view_facture", "Peut voir un objet facture"),
|
||||
("change_all_facture", "Superdroit, peut modifier toutes les factures"),
|
||||
)
|
||||
|
||||
def prix(self):
|
||||
"""Renvoie le prix brut sans les quantités. Méthode
|
||||
dépréciée"""
|
||||
|
@ -103,6 +114,65 @@ class Facture(models.Model):
|
|||
).values_list('name', flat=True))
|
||||
return name
|
||||
|
||||
def get_instance(factureid, *args, **kwargs):
|
||||
return Facture.objects.get(pk=factureid)
|
||||
|
||||
def can_create(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.add_facture'), u"Vous n'avez pas le\
|
||||
droit de créer des factures"
|
||||
|
||||
def can_edit(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.change_facture'):
|
||||
return False, u"Vous n'avez pas le droit d'éditer les factures"
|
||||
elif not user_request.has_perm('cotisations.change_all_facture') and not self.user.can_edit(user_request, *args, **kwargs)[0]:
|
||||
return False, u"Vous ne pouvez pas éditer les factures de cet user protégé"
|
||||
elif not user_request.has_perm('cotisations.change_all_facture') and\
|
||||
(self.control or not self.valid):
|
||||
return False, u"Vous n'avez pas le droit d'éditer une facture\
|
||||
controlée ou invalidée par un trésorier"
|
||||
else:
|
||||
return True, None
|
||||
|
||||
def can_delete(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.delete_facture'):
|
||||
return False, u"Vous n'avez pas le droit de supprimer une facture"
|
||||
if not self.user.can_edit(user_request, *args, **kwargs)[0]:
|
||||
return False, u"Vous ne pouvez pas éditer les factures de cet user protégé"
|
||||
if self.control or not self.valid:
|
||||
return False, u"Vous ne pouvez pas supprimer une facture\
|
||||
contrôlée ou invalidée par un trésorier"
|
||||
else:
|
||||
return True, None
|
||||
|
||||
def can_view_all(user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.view_facture'):
|
||||
return False, u"Vous n'avez pas le droit de voir les factures"
|
||||
return True, None
|
||||
|
||||
def can_view(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.view_facture') and\
|
||||
self.user != user_request:
|
||||
return False, u"Vous ne pouvez pas afficher l'historique d'une\
|
||||
facture d'un autre user que vous sans droit cableur"
|
||||
elif not self.valid:
|
||||
return False, u"La facture est invalidée et ne peut être affichée"
|
||||
else:
|
||||
return True, None
|
||||
|
||||
@staticmethod
|
||||
def can_change_control(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.change_facture_control'), "Vous ne pouvez pas éditer le controle sans droit trésorier"
|
||||
|
||||
@staticmethod
|
||||
def can_change_pdf(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.change_facture_pdf'), "Vous ne pouvez pas éditer une facture sans droit trésorier"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Facture, self).__init__(*args, **kwargs)
|
||||
self.field_permissions = {
|
||||
'control' : self.can_change_control,
|
||||
}
|
||||
|
||||
def __str__(self):
|
||||
return str(self.user) + ' ' + str(self.date)
|
||||
|
||||
|
@ -149,6 +219,12 @@ class Vente(models.Model):
|
|||
max_length=255
|
||||
)
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("view_vente", "Peut voir un objet vente"),
|
||||
("change_all_vente", "Superdroit, peut modifier toutes les ventes"),
|
||||
)
|
||||
|
||||
def prix_total(self):
|
||||
"""Renvoie le prix_total de self (nombre*prix)"""
|
||||
return self.prix*self.number
|
||||
|
@ -201,6 +277,50 @@ class Vente(models.Model):
|
|||
self.update_cotisation()
|
||||
super(Vente, self).save(*args, **kwargs)
|
||||
|
||||
def get_instance(venteid, *args, **kwargs):
|
||||
return Vente.objects.get(pk=venteid)
|
||||
|
||||
def can_create(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.add_vente'), u"Vous n'avez pas le\
|
||||
droit de créer des ventes"
|
||||
return True, None
|
||||
|
||||
def can_edit(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.change_vente'):
|
||||
return False, u"Vous n'avez pas le droit d'éditer les ventes"
|
||||
elif not user_request.has_perm('cotisations.change_all_facture') and not self.facture.user.can_edit(user_request, *args, **kwargs)[0]:
|
||||
return False, u"Vous ne pouvez pas éditer les factures de cet user protégé"
|
||||
elif not user_request.has_perm('cotisations.change_all_vente') and\
|
||||
(self.facture.control or not self.facture.valid):
|
||||
return False, u"Vous n'avez pas le droit d'éditer une vente\
|
||||
controlée ou invalidée par un trésorier"
|
||||
else:
|
||||
return True, None
|
||||
|
||||
def can_delete(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.delete_vente'):
|
||||
return False, u"Vous n'avez pas le droit de supprimer une vente"
|
||||
if not self.facture.user.can_edit(user_request, *args, **kwargs)[0]:
|
||||
return False, u"Vous ne pouvez pas éditer les factures de cet user protégé"
|
||||
if self.facture.control or not self.facture.valid:
|
||||
return False, u"Vous ne pouvez pas supprimer une vente\
|
||||
contrôlée ou invalidée par un trésorier"
|
||||
else:
|
||||
return True, None
|
||||
|
||||
def can_view_all(user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.view_vente'):
|
||||
return False, u"Vous n'avez pas le droit de voir les ventes"
|
||||
return True, None
|
||||
|
||||
def can_view(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.view_vente') and\
|
||||
self.facture.user != user_request:
|
||||
return False, u"Vous ne pouvez pas afficher l'historique d'une\
|
||||
facture d'un autre user que vous sans droit cableur"
|
||||
else:
|
||||
return True, None
|
||||
|
||||
def __str__(self):
|
||||
return str(self.name) + ' ' + str(self.facture)
|
||||
|
||||
|
@ -269,6 +389,11 @@ class Article(models.Model):
|
|||
|
||||
unique_together = ('name', 'type_user')
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("view_article", "Peut voir un objet article"),
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
if self.name.lower() == "solde":
|
||||
raise ValidationError("Solde est un nom d'article invalide")
|
||||
|
@ -277,6 +402,29 @@ class Article(models.Model):
|
|||
"La durée est obligatoire si il s'agit d'une cotisation"
|
||||
)
|
||||
|
||||
def get_instance(articleid, *args, **kwargs):
|
||||
return Article.objects.get(pk=articleid)
|
||||
|
||||
def can_create(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.add_article'), u"Vous n'avez pas le\
|
||||
droit d'ajouter des articles"
|
||||
|
||||
def can_edit(self, user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.change_article'), u"Vous n'avez pas le\
|
||||
droit d'éditer des articles"
|
||||
|
||||
def can_delete(self, user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.delete_article'), u"Vous n'avez pas le\
|
||||
droit de supprimer des articles"
|
||||
|
||||
def can_view_all(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.view_article'), u"Vous n'avez pas le\
|
||||
droit de voir des articles"
|
||||
|
||||
def can_view(self, user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.view_article'), u"Vous n'avez pas le\
|
||||
droit de voir des articles"
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
@ -287,6 +435,34 @@ class Banque(models.Model):
|
|||
|
||||
name = models.CharField(max_length=255)
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("view_banque", "Peut voir un objet banque"),
|
||||
)
|
||||
|
||||
def get_instance(banqueid, *args, **kwargs):
|
||||
return Banque.objects.get(pk=banqueid)
|
||||
|
||||
def can_create(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.add_banque'), u"Vous n'avez pas le\
|
||||
droit d'ajouter des banques"
|
||||
|
||||
def can_edit(self, user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.change_banque'), u"Vous n'avez pas le\
|
||||
droit d'éditer des banques"
|
||||
|
||||
def can_delete(self, user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.delete_banque'), u"Vous n'avez pas le\
|
||||
droit de supprimer des banques"
|
||||
|
||||
def can_view_all(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.view_banque'), u"Vous n'avez pas le\
|
||||
droit de voir des banques"
|
||||
|
||||
def can_view(self, user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.view_banque'), u"Vous n'avez pas le\
|
||||
droit de voir des banques"
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
@ -302,6 +478,34 @@ class Paiement(models.Model):
|
|||
moyen = models.CharField(max_length=255)
|
||||
type_paiement = models.IntegerField(choices=PAYMENT_TYPES, default=0)
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("view_paiement", "Peut voir un objet paiement"),
|
||||
)
|
||||
|
||||
def get_instance(paiementid, *args, **kwargs):
|
||||
return Paiement.objects.get(pk=paiementid)
|
||||
|
||||
def can_create(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.add_paiement'), u"Vous n'avez pas le\
|
||||
droit d'ajouter des paiements"
|
||||
|
||||
def can_edit(self, user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.change_paiement'), u"Vous n'avez pas le\
|
||||
droit d'éditer des paiements"
|
||||
|
||||
def can_delete(self, user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.delete_paiement'), u"Vous n'avez pas le\
|
||||
droit de supprimer des paiements"
|
||||
|
||||
def can_view_all(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.view_paiement'), u"Vous n'avez pas le\
|
||||
droit de voir des paiements"
|
||||
|
||||
def can_view(self, user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.view_paiement'), u"Vous n'avez pas le\
|
||||
droit de voir des paiements"
|
||||
|
||||
def __str__(self):
|
||||
return self.moyen
|
||||
|
||||
|
@ -330,10 +534,57 @@ class Cotisation(models.Model):
|
|||
type_cotisation = models.CharField(
|
||||
choices=COTISATION_TYPE,
|
||||
max_length=255,
|
||||
default='All',
|
||||
)
|
||||
date_start = models.DateTimeField()
|
||||
date_end = models.DateTimeField()
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("view_cotisation", "Peut voir un objet cotisation"),
|
||||
("change_all_cotisation", "Superdroit, peut modifier toutes les cotisations"),
|
||||
)
|
||||
|
||||
def get_instance(cotisationid, *args, **kwargs):
|
||||
return Cotisations.objects.get(pk=cotisationid)
|
||||
|
||||
def can_create(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('cotisations.add_cotisation'), u"Vous n'avez pas le\
|
||||
droit de créer des cotisations"
|
||||
return True, None
|
||||
|
||||
def can_edit(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.change_cotisation'):
|
||||
return False, u"Vous n'avez pas le droit d'éditer les cotisations"
|
||||
elif not user_request.has_perm('cotisations.change_all_cotisation') and\
|
||||
(self.vente.facture.control or not self.vente.facture.valid):
|
||||
return False, u"Vous n'avez pas le droit d'éditer une cotisation\
|
||||
controlée ou invalidée par un trésorier"
|
||||
else:
|
||||
return True, None
|
||||
|
||||
def can_delete(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.delete_cotisation'):
|
||||
return False, u"Vous n'avez pas le droit de supprimer une cotisations"
|
||||
if self.vente.facture.control or not self.vente.facture.valid:
|
||||
return False, u"Vous ne pouvez pas supprimer une cotisations\
|
||||
contrôlée ou invalidée par un trésorier"
|
||||
else:
|
||||
return True, None
|
||||
|
||||
def can_view_all(user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.view_cotisation'):
|
||||
return False, u"Vous n'avez pas le droit de voir les cotisations"
|
||||
return True, None
|
||||
|
||||
def can_view(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('cotisations.view_cotisation') and\
|
||||
self.vente.facture.user != user_request:
|
||||
return False, u"Vous ne pouvez pas afficher l'historique d'une\
|
||||
cotisation d'un autre user que vous sans droit cableur"
|
||||
else:
|
||||
return True, None
|
||||
|
||||
def __str__(self):
|
||||
return str(self.vente)
|
||||
|
||||
|
|
111
cotisations/payment.py
Normal file
111
cotisations/payment.py
Normal file
|
@ -0,0 +1,111 @@
|
|||
"""Payment
|
||||
|
||||
Here are defined some views dedicated to online payement.
|
||||
"""
|
||||
from django.urls import reverse
|
||||
from django.shortcuts import redirect, get_object_or_404
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib import messages
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.utils.datastructures import MultiValueDictKeyError
|
||||
from django.http import HttpResponse, HttpResponseBadRequest
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from preferences.models import AssoOption
|
||||
from .models import Facture
|
||||
from .payment_utils.comnpay import Payment as ComnpayPayment
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
def accept_payment(request, factureid):
|
||||
facture = get_object_or_404(Facture, id=factureid)
|
||||
messages.success(
|
||||
request,
|
||||
"Le paiement de {} € a été accepté.".format(facture.prix())
|
||||
)
|
||||
return redirect(reverse('users:profil', kwargs={'userid':request.user.id}))
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
def refuse_payment(request):
|
||||
messages.error(
|
||||
request,
|
||||
"Le paiement a été refusé."
|
||||
)
|
||||
return redirect(reverse('users:profil', kwargs={'userid':request.user.id}))
|
||||
|
||||
@csrf_exempt
|
||||
def ipn(request):
|
||||
p = ComnpayPayment()
|
||||
order = ('idTpe', 'idTransaction', 'montant', 'result', 'sec', )
|
||||
try:
|
||||
data = OrderedDict([(f, request.POST[f]) for f in order])
|
||||
except MultiValueDictKeyError:
|
||||
return HttpResponseBadRequest("HTTP/1.1 400 Bad Request")
|
||||
|
||||
if not p.validSec(data, AssoOption.get_cached_value('payment_pass')):
|
||||
return HttpResponseBadRequest("HTTP/1.1 400 Bad Request")
|
||||
|
||||
result = True if (request.POST['result'] == 'OK') else False
|
||||
idTpe = request.POST['idTpe']
|
||||
idTransaction = request.POST['idTransaction']
|
||||
|
||||
# On vérifie que le paiement nous est destiné
|
||||
if not idTpe == AssoOption.get_cached_value('payment_id'):
|
||||
return HttpResponseBadRequest("HTTP/1.1 400 Bad Request")
|
||||
|
||||
try:
|
||||
factureid = int(idTransaction)
|
||||
except ValueError:
|
||||
return HttpResponseBadRequest("HTTP/1.1 400 Bad Request")
|
||||
|
||||
facture = get_object_or_404(Facture, id=factureid)
|
||||
|
||||
# On vérifie que le paiement est valide
|
||||
if not result:
|
||||
# Le paiement a échoué : on effectue les actions nécessaires (On indique qu'elle a échoué)
|
||||
facture.delete()
|
||||
|
||||
# On notifie au serveur ComNPay qu'on a reçu les données pour traitement
|
||||
return HttpResponse("HTTP/1.1 200 OK")
|
||||
|
||||
facture.valid = True
|
||||
facture.save()
|
||||
|
||||
# A nouveau, on notifie au serveur qu'on a bien traité les données
|
||||
return HttpResponse("HTTP/1.0 200 OK")
|
||||
|
||||
|
||||
def comnpay(facture, request):
|
||||
host = request.get_host()
|
||||
p = ComnpayPayment(
|
||||
str(AssoOption.get_cached_value('payment_id')),
|
||||
str(AssoOption.get_cached_value('payment_pass')),
|
||||
'https://' + host + reverse(
|
||||
'cotisations:accept_payment',
|
||||
kwargs={'factureid':facture.id}
|
||||
),
|
||||
'https://' + host + reverse('cotisations:refuse_payment'),
|
||||
'https://' + host + reverse('cotisations:ipn'),
|
||||
"",
|
||||
"D"
|
||||
)
|
||||
r = {
|
||||
'action' : 'https://secure.homologation.comnpay.com',
|
||||
'method' : 'POST',
|
||||
'content' : p.buildSecretHTML(
|
||||
"Rechargement du solde",
|
||||
facture.prix(),
|
||||
idTransaction=str(facture.id)
|
||||
),
|
||||
'amount' : facture.prix,
|
||||
}
|
||||
return r
|
||||
|
||||
|
||||
PAYMENT_SYSTEM = {
|
||||
'COMNPAY' : comnpay,
|
||||
'NONE' : None
|
||||
}
|
0
cotisations/payment_utils/__init__.py
Normal file
0
cotisations/payment_utils/__init__.py
Normal file
68
cotisations/payment_utils/comnpay.py
Normal file
68
cotisations/payment_utils/comnpay.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
import time
|
||||
from random import randrange
|
||||
import base64
|
||||
import hashlib
|
||||
from collections import OrderedDict
|
||||
from itertools import chain
|
||||
|
||||
class Payment():
|
||||
|
||||
vad_number = ""
|
||||
secret_key = ""
|
||||
urlRetourOK = ""
|
||||
urlRetourNOK = ""
|
||||
urlIPN = ""
|
||||
source = ""
|
||||
typeTr = "D"
|
||||
|
||||
def __init__(self, vad_number = "", secret_key = "", urlRetourOK = "", urlRetourNOK = "", urlIPN = "", source="", typeTr="D"):
|
||||
self.vad_number = vad_number
|
||||
self.secret_key = secret_key
|
||||
self.urlRetourOK = urlRetourOK
|
||||
self.urlRetourNOK = urlRetourNOK
|
||||
self.urlIPN = urlIPN
|
||||
self.source = source
|
||||
self.typeTr = typeTr
|
||||
|
||||
def buildSecretHTML(self, produit="Produit", montant="0.00", idTransaction=""):
|
||||
if idTransaction == "":
|
||||
self.idTransaction = str(time.time())+self.vad_number+str(randrange(999))
|
||||
else:
|
||||
self.idTransaction = idTransaction
|
||||
|
||||
array_tpe = OrderedDict(
|
||||
montant= str(montant),
|
||||
idTPE= self.vad_number,
|
||||
idTransaction= self.idTransaction,
|
||||
devise= "EUR",
|
||||
lang= 'fr',
|
||||
nom_produit= produit,
|
||||
source= self.source,
|
||||
urlRetourOK= self.urlRetourOK,
|
||||
urlRetourNOK= self.urlRetourNOK,
|
||||
typeTr= str(self.typeTr)
|
||||
)
|
||||
|
||||
if self.urlIPN!="":
|
||||
array_tpe['urlIPN'] = self.urlIPN
|
||||
|
||||
array_tpe['key'] = self.secret_key;
|
||||
strWithKey = base64.b64encode(bytes('|'.join(array_tpe.values()), 'utf-8'))
|
||||
del array_tpe["key"]
|
||||
array_tpe['sec'] = hashlib.sha512(strWithKey).hexdigest()
|
||||
|
||||
ret = ""
|
||||
for key in array_tpe:
|
||||
ret += '<input type="hidden" name="'+key+'" value="'+array_tpe[key]+'"/>'
|
||||
|
||||
return ret
|
||||
|
||||
def validSec(self, values, secret_key):
|
||||
if "sec" in values:
|
||||
sec = values['sec']
|
||||
del values["sec"]
|
||||
strWithKey = hashlib.sha512(base64.b64encode(bytes('|'.join(values.values()) +"|"+secret_key, 'utf-8'))).hexdigest()
|
||||
return strWithKey.upper() == sec.upper()
|
||||
else:
|
||||
return False
|
||||
|
|
@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -41,13 +43,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ article.duration }}</td>
|
||||
<td>{{ article.type_user }}</td>
|
||||
<td class="text-right">
|
||||
{% if is_trez %}
|
||||
{% can_edit article %}
|
||||
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'cotisations:edit-article' article.id %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
<i class="fa fa-edit"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'cotisations:history' 'article' article.id %}">
|
||||
<i class="glyphicon glyphicon-time"></i>
|
||||
<i class="fa fa-history"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -33,13 +35,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<tr>
|
||||
<td>{{ banque.name }}</td>
|
||||
<td class="text-right">
|
||||
{% if is_trez %}
|
||||
{% can_edit banque %}
|
||||
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'cotisations:edit-banque' banque.id %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
<i class="fa fa-edit"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'cotisations:history' 'banque' banque.id %}">
|
||||
<i class="glyphicon glyphicon-time"></i>
|
||||
<i class="fa fa-history"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
{% if facture_list.paginator %}
|
||||
{% include "pagination.html" with list=facture_list %}
|
||||
{% endif %}
|
||||
|
@ -47,7 +49,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ facture.paiement }}</td>
|
||||
<td>{{ facture.date }}</td>
|
||||
<td>{{ facture.id }}</td>
|
||||
{% if is_cableur %}
|
||||
<td>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-default dropdown-toggle" type="button" id="editionfacture" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
|
@ -55,21 +56,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="editionfacture">
|
||||
{% if facture.valid and not facture.control or is_trez %}
|
||||
<li><a href="{% url 'cotisations:edit-facture' facture.id %}"><i class="glyphicon glyphicon-bitcoin"></i> Modifier</a></li>
|
||||
<li><a href="{% url 'cotisations:del-facture' facture.id %}"><i class="glyphicon glyphicon-trash"></i> Supprimer</a></li>
|
||||
<li><a href="{% url 'cotisations:history' 'facture' facture.id %}"><i class="glyphicon glyphicon-time"></i> Historique</a></li>
|
||||
{% else %}
|
||||
{% can_edit facture %}
|
||||
<li><a href="{% url 'cotisations:edit-facture' facture.id %}"><i class="fa fa-dollar-sign"></i> Modifier</a></li>
|
||||
{% acl_else %}
|
||||
<li>Facture controlée</li>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% can_delete facture %}
|
||||
<li><a href="{% url 'cotisations:del-facture' facture.id %}"><i class="fa fa-trash"></i> Supprimer</a></li>
|
||||
{% acl_end %}
|
||||
<li><a href="{% url 'cotisations:history' 'facture' facture.id %}"><i class="fa fa-history"></i> Historique</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
{% endif %}
|
||||
<td>
|
||||
{% if facture.valid %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:facture-pdf' facture.id %}">
|
||||
<i class="glyphicon glyphicon-save"></i>
|
||||
<i class="fa fa-file-pdf"></i>
|
||||
PDF
|
||||
</a>
|
||||
{% else %}
|
||||
|
|
|
@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -33,13 +35,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<tr>
|
||||
<td>{{ paiement.moyen }}</td>
|
||||
<td class="text-right">
|
||||
{% if is_trez %}
|
||||
{% can_edit paiement %}
|
||||
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'cotisations:edit-paiement' paiement.id %}">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
<i class="fa fa-edit"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'cotisations:history' 'paiement' paiement.id %}">
|
||||
<i class="glyphicon glyphicon-time"></i>
|
||||
<i class="fa fa-history"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -56,7 +56,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% bootstrap_form_errors form %}
|
||||
<tr>
|
||||
|
||||
<td><a href="{% url "users:profil" form.instance.user.id%}" class="btn btn-primary btn-sm" role="button"><i class="glyphicon glyphicon-user"></i></a>
|
||||
<td><a href="{% url "users:profil" form.instance.user.id%}" class="btn btn-primary btn-sm" role="button"><i class="fa fa-user"></i></a>
|
||||
</td>
|
||||
<td>{{ form.instance.user.name }}</td>
|
||||
<td>{{ form.instance.user.surname }}</td>
|
||||
|
|
|
@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form factureform %}
|
||||
{% bootstrap_button "Créer ou modifier" button_type="submit" icon="star" %}
|
||||
{% bootstrap_button action_name button_type="submit" icon="star" %}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -24,15 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endcomment %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
{% load acl %}
|
||||
|
||||
{% block title %}Articles{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Liste des types d'articles</h2>
|
||||
{% if is_trez %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:add-article' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un type d'articles</a>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-article' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs types d'articles</a>
|
||||
{% endif %}
|
||||
{% can_create Article %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:add-article' %}"><i class="fa fa-cart-plus"></i> Ajouter un type d'articles</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-article' %}"><i class="fa fa-trash"></i> Supprimer un ou plusieurs types d'articles</a>
|
||||
{% include "cotisations/aff_article.html" with article_list=article_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -24,15 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endcomment %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
{% load acl %}
|
||||
|
||||
{% block title %}Banques{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Liste des banques</h2>
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:add-banque' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter une banque</a>
|
||||
{% if is_trez %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-banque' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer une ou plusieurs banques</a>
|
||||
{% endif %}
|
||||
{% can_create Banque %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:add-banque' %}"><i class="fa fa-cart-plus"></i> Ajouter une banque</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-banque' %}"><i class="fa fa-trash"></i> Supprimer une ou plusieurs banques</a>
|
||||
{% include "cotisations/aff_banque.html" with banque_list=banque_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -24,15 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endcomment %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
{% load acl %}
|
||||
|
||||
{% block title %}Paiements{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Liste des types de paiements</h2>
|
||||
{% if is_trez %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:add-paiement' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un type de paiement</a>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-paiement' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs types de paiements</a>
|
||||
{% endif %}
|
||||
{% can_create Paiement %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:add-paiement' %}"><i class="fa fa-cart-plus"></i> Ajouter un type de paiement</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-paiement' %}"><i class="fa fa-trash"></i> Supprimer un ou plusieurs types de paiements</a>
|
||||
{% include "cotisations/aff_paiement.html" with paiement_list=paiement_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -34,6 +34,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
<h3>Nouvelle facture</h3>
|
||||
<p>
|
||||
Solde de l'utilisateur : {{ user.solde }} €
|
||||
</p>
|
||||
{% bootstrap_form factureform %}
|
||||
{{ venteform.management_form }}
|
||||
<!-- TODO: FIXME to include data-type="check" for right option in id_cheque select -->
|
||||
|
@ -46,7 +49,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
<button class="btn btn-danger btn-sm"
|
||||
id="id_form-0-article-remove" type="button">
|
||||
<span class="glyphicon glyphicon-remove"></span>
|
||||
<span class="fa fa-times"></span>
|
||||
</button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
@ -55,7 +58,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<p>
|
||||
Prix total : <span id="total_price">0,00</span> €
|
||||
</p>
|
||||
{% bootstrap_button "Créer ou modifier" button_type="submit" icon="star" %}
|
||||
{% bootstrap_button "Créer" button_type="submit" icon="star" %}
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
@ -70,7 +73,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
<button class="btn btn-danger btn-sm"
|
||||
id="id_form-__prefix__-article-remove" type="button">
|
||||
<span class="glyphicon glyphicon-remove"></span>
|
||||
<span class="fa fa-times"></span>
|
||||
</button>`
|
||||
|
||||
function add_article(){
|
||||
|
|
157
cotisations/templates/cotisations/new_facture_solde.html
Normal file
157
cotisations/templates/cotisations/new_facture_solde.html
Normal file
|
@ -0,0 +1,157 @@
|
|||
|
||||
{% extends "cotisations/sidebar.html" %}
|
||||
{% comment %}
|
||||
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 © 2017 Gabriel Détraz
|
||||
Copyright © 2017 Goulven Kermarec
|
||||
Copyright © 2017 Augustin Lemesle
|
||||
|
||||
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.
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
{% load staticfiles%}
|
||||
|
||||
{% block title %}Création et modification de factures{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% bootstrap_form_errors venteform.management_form %}
|
||||
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
<h3>Nouvelle facture</h3>
|
||||
{{ venteform.management_form }}
|
||||
<!-- TODO: FIXME to include data-type="check" for right option in id_cheque select -->
|
||||
<h3>Articles de la facture</h3>
|
||||
<div id="form_set" class="form-group">
|
||||
{% for form in venteform.forms %}
|
||||
<div class='product_to_sell form-inline'>
|
||||
Article :
|
||||
{% bootstrap_form form label_class='sr-only' %}
|
||||
|
||||
<button class="btn btn-danger btn-sm"
|
||||
id="id_form-0-article-remove" type="button">
|
||||
<span class="fa fa-times"></span>
|
||||
</button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<input class="btn btn-primary btn-sm" role="button" value="Ajouter un article" id="add_one">
|
||||
<p>
|
||||
Prix total : <span id="total_price">0,00</span> €
|
||||
</p>
|
||||
{% bootstrap_button "Créer ou modifier" button_type="submit" icon="star" %}
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var prices = {};
|
||||
{% for article in articlelist %}
|
||||
prices[{{ article.id|escapejs }}] = {{ article.prix }};
|
||||
{% endfor %}
|
||||
|
||||
var template = `Article :
|
||||
{% bootstrap_form venteform.empty_form label_class='sr-only' %}
|
||||
|
||||
<button class="btn btn-danger btn-sm"
|
||||
id="id_form-__prefix__-article-remove" type="button">
|
||||
<span class="fa fa-times"></span>
|
||||
</button>`
|
||||
|
||||
function add_article(){
|
||||
// Index start at 0 => new_index = number of items
|
||||
var new_index =
|
||||
document.getElementsByClassName('product_to_sell').length;
|
||||
document.getElementById('id_form-TOTAL_FORMS').value ++;
|
||||
var new_article = document.createElement('div');
|
||||
new_article.className = 'product_to_sell form-inline';
|
||||
new_article.innerHTML = template.replace(/__prefix__/g, new_index);
|
||||
document.getElementById('form_set').appendChild(new_article);
|
||||
add_listenner_for_id(new_index);
|
||||
}
|
||||
|
||||
function update_price(){
|
||||
var price = 0;
|
||||
var product_count =
|
||||
document.getElementsByClassName('product_to_sell').length;
|
||||
var article, article_price, quantity;
|
||||
for (i = 0; i < product_count; ++i){
|
||||
article = document.getElementById(
|
||||
'id_form-' + i.toString() + '-article').value;
|
||||
if (article == '') {
|
||||
continue;
|
||||
}
|
||||
article_price = prices[article];
|
||||
quantity = document.getElementById(
|
||||
'id_form-' + i.toString() + '-quantity').value;
|
||||
price += article_price * quantity;
|
||||
}
|
||||
document.getElementById('total_price').innerHTML =
|
||||
price.toFixed(2).toString().replace('.', ',');
|
||||
}
|
||||
|
||||
function add_listenner_for_id(i){
|
||||
document.getElementById('id_form-' + i.toString() + '-article')
|
||||
.addEventListener("change", update_price, true);
|
||||
document.getElementById('id_form-' + i.toString() + '-article')
|
||||
.addEventListener("onkeypress", update_price, true);
|
||||
document.getElementById('id_form-' + i.toString() + '-quantity')
|
||||
.addEventListener("change", update_price, true);
|
||||
document.getElementById('id_form-' + i.toString() + '-article-remove')
|
||||
.addEventListener("click", function(event) {
|
||||
var article = event.target.parentNode;
|
||||
article.parentNode.removeChild(article);
|
||||
document.getElementById('id_form-TOTAL_FORMS').value --;
|
||||
update_price();
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function set_cheque_info_visibility() {
|
||||
var paiement = document.getElementById("id_Facture-paiement");
|
||||
var visible = paiement.value == paiement.getAttribute('data-cheque');
|
||||
p = document.getElementById("id_Facture-paiement");
|
||||
var display = 'none';
|
||||
if (visible) {
|
||||
display = 'block';
|
||||
}
|
||||
document.getElementById("id_Facture-cheque")
|
||||
.parentNode.style.display = display;
|
||||
document.getElementById("id_Facture-banque")
|
||||
.parentNode.style.display = display;
|
||||
}
|
||||
|
||||
// Add events manager when DOM is fully loaded
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
document.getElementById("add_one")
|
||||
.addEventListener("click", add_article, true);
|
||||
var product_count =
|
||||
document.getElementsByClassName('product_to_sell').length;
|
||||
for (i = 0; i < product_count; ++i){
|
||||
add_listenner_for_id(i);
|
||||
}
|
||||
document.getElementById("id_Facture-paiement")
|
||||
.addEventListener("change", set_cheque_info_visibility, true);
|
||||
set_cheque_info_visibility();
|
||||
update_price();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
37
cotisations/templates/cotisations/payment.html
Normal file
37
cotisations/templates/cotisations/payment.html
Normal file
|
@ -0,0 +1,37 @@
|
|||
{% extends "cotisations/sidebar.html" %}
|
||||
{% comment %}
|
||||
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 © 2017 Gabriel Détraz
|
||||
Copyright © 2017 Goulven Kermarec
|
||||
Copyright © 2017 Augustin Lemesle
|
||||
|
||||
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.
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
{% load staticfiles%}
|
||||
|
||||
{% block title %}Rechargement du solde{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h3>Recharger de {{ amount }} €</h3>
|
||||
<form class="form" method="{{ method }}" action="{{action}}">
|
||||
{{ content | safe }}
|
||||
{% bootstrap_button "Payer" button_type="submit" icon="piggy-bank" %}
|
||||
</form>
|
||||
{% endblock %}
|
39
cotisations/templates/cotisations/recharge.html
Normal file
39
cotisations/templates/cotisations/recharge.html
Normal file
|
@ -0,0 +1,39 @@
|
|||
{% extends "cotisations/sidebar.html" %}
|
||||
{% comment %}
|
||||
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 © 2017 Gabriel Détraz
|
||||
Copyright © 2017 Goulven Kermarec
|
||||
Copyright © 2017 Augustin Lemesle
|
||||
|
||||
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.
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
{% load staticfiles%}
|
||||
|
||||
{% block title %}Rechargement du solde{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Rechargement du solde</h2>
|
||||
<h3>Solde : <span class="label label-default">{{ request.user.solde }} €</span></h3>
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form rechargeform %}
|
||||
{% bootstrap_button "Valider" button_type="submit" icon="piggy-bank" %}
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -23,32 +23,41 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
{% block sidebar %}
|
||||
{% if is_trez %}
|
||||
{% can_change Facture pdf %}
|
||||
<a class="list-group-item list-group-item-success" href="{% url "cotisations:new-facture-pdf" %}">
|
||||
<i class="glyphicon glyphicon-plus"></i>
|
||||
<i class="fa fa-plus"></i>
|
||||
Créer une facture
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-warning" href="{% url "cotisations:control" %}">
|
||||
<i class="glyphicon glyphicon-eye-open"></i>
|
||||
<i class="fa fa-eye"></i>
|
||||
Contrôler les factures
|
||||
</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% can_view_all Facture %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "cotisations:index" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
<i class="fa fa-list-ul"></i>
|
||||
Factures
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all Article %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "cotisations:index-article" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
<i class="fa fa-list-ul"></i>
|
||||
Articles en vente
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all Banque %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "cotisations:index-banque" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
<i class="fa fa-list-ul"></i>
|
||||
Banques
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all Paiement %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "cotisations:index-paiement" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
<i class="fa fa-list-ul"></i>
|
||||
Moyens de paiement
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -24,7 +24,9 @@ from __future__ import unicode_literals
|
|||
|
||||
from django.conf.urls import url
|
||||
|
||||
import re2o
|
||||
from . import views
|
||||
from . import payment
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^new_facture/(?P<userid>[0-9]+)$',
|
||||
|
@ -99,24 +101,35 @@ urlpatterns = [
|
|||
views.index_paiement,
|
||||
name='index-paiement'
|
||||
),
|
||||
url(r'^history/(?P<object_name>facture)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'
|
||||
),
|
||||
url(r'^history/(?P<object_name>article)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'
|
||||
),
|
||||
url(r'^history/(?P<object_name>paiement)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'),
|
||||
url(r'^history/(?P<object_name>banque)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'
|
||||
url(
|
||||
r'history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
|
||||
re2o.views.history,
|
||||
name='history',
|
||||
kwargs={'application':'cotisations'},
|
||||
),
|
||||
url(r'^control/$',
|
||||
views.control,
|
||||
name='control'
|
||||
),
|
||||
url(r'^new_facture_solde/(?P<userid>[0-9]+)$',
|
||||
views.new_facture_solde,
|
||||
name='new_facture_solde'
|
||||
),
|
||||
url(r'^recharge/$',
|
||||
views.recharge,
|
||||
name='recharge'
|
||||
),
|
||||
url(r'^payment/accept/(?P<factureid>[0-9]+)$',
|
||||
payment.accept_payment,
|
||||
name='accept_payment'
|
||||
),
|
||||
url(r'^payment/refuse/$',
|
||||
payment.refuse_payment,
|
||||
name='refuse_payment'
|
||||
),
|
||||
url(r'^payment/ipn/$',
|
||||
payment.ipn,
|
||||
name='ipn'
|
||||
),
|
||||
url(r'^$', views.index, name='index'),
|
||||
]
|
||||
|
|
|
@ -29,6 +29,7 @@ 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
|
||||
from django.db.models import ProtectedError
|
||||
|
@ -36,6 +37,8 @@ from django.db import transaction
|
|||
from django.db.models import Q
|
||||
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
|
||||
|
@ -44,11 +47,19 @@ from re2o.settings import LOGO_PATH
|
|||
from re2o import settings
|
||||
from re2o.views import form
|
||||
from re2o.utils import SortTable
|
||||
from re2o.acl import (
|
||||
can_create,
|
||||
can_edit,
|
||||
can_delete,
|
||||
can_view,
|
||||
can_view_all,
|
||||
can_delete_set,
|
||||
can_change,
|
||||
)
|
||||
from preferences.models import OptionalUser, AssoOption, GeneralOption
|
||||
from .models import Facture, Article, Vente, Paiement, Banque
|
||||
from .forms import (
|
||||
NewFactureForm,
|
||||
TrezEditFactureForm,
|
||||
EditFactureForm,
|
||||
ArticleForm,
|
||||
DelArticleForm,
|
||||
|
@ -59,14 +70,19 @@ from .forms import (
|
|||
NewFactureFormPdf,
|
||||
SelectUserArticleForm,
|
||||
SelectClubArticleForm,
|
||||
CreditSoldeForm
|
||||
CreditSoldeForm,
|
||||
NewFactureSoldeForm,
|
||||
RechargeForm
|
||||
)
|
||||
from . import payment
|
||||
from .tex import render_invoice
|
||||
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('cableur')
|
||||
def new_facture(request, userid):
|
||||
@can_create(Facture)
|
||||
@can_edit(User)
|
||||
def new_facture(request, user, userid):
|
||||
"""Creation d'une facture pour un user. Renvoie la liste des articles
|
||||
et crée des factures dans un formset. Utilise un peu de js coté template
|
||||
pour ajouter des articles.
|
||||
|
@ -74,11 +90,6 @@ def new_facture(request, userid):
|
|||
enfin sauve la facture parente.
|
||||
TODO : simplifier cette fonction, déplacer l'intelligence coté models
|
||||
Facture et Vente."""
|
||||
try:
|
||||
user = User.objects.get(pk=userid)
|
||||
except User.DoesNotExist:
|
||||
messages.error(request, u"Utilisateur inexistant")
|
||||
return redirect(reverse('cotisations:index'))
|
||||
facture = Facture(user=user)
|
||||
# Le template a besoin de connaitre les articles pour le js
|
||||
article_list = Article.objects.filter(
|
||||
|
@ -95,9 +106,8 @@ def new_facture(request, userid):
|
|||
articles = article_formset
|
||||
# Si au moins un article est rempli
|
||||
if any(art.cleaned_data for art in articles):
|
||||
options, _created = OptionalUser.objects.get_or_create()
|
||||
user_solde = options.user_solde
|
||||
solde_negatif = options.solde_negatif
|
||||
user_solde = OptionalUser.get_cached_value('user_solde')
|
||||
solde_negatif = OptionalUser.get_cached_value('solde_negatif')
|
||||
# Si on paye par solde, que l'option est activée,
|
||||
# on vérifie que le négatif n'est pas atteint
|
||||
if user_solde:
|
||||
|
@ -163,14 +173,13 @@ def new_facture(request, userid):
|
|||
|
||||
|
||||
@login_required
|
||||
@permission_required('tresorier')
|
||||
@can_change(Facture, 'pdf')
|
||||
def new_facture_pdf(request):
|
||||
"""Permet de générer un pdf d'une facture. Réservée
|
||||
au trésorier, permet d'emettre des factures sans objet
|
||||
Vente ou Facture correspondant en bdd"""
|
||||
facture_form = NewFactureFormPdf(request.POST or None)
|
||||
if facture_form.is_valid():
|
||||
options, _created = AssoOption.objects.get_or_create()
|
||||
tbl = []
|
||||
article = facture_form.cleaned_data['article']
|
||||
quantite = facture_form.cleaned_data['number']
|
||||
|
@ -189,48 +198,30 @@ def new_facture_pdf(request):
|
|||
'article': tbl,
|
||||
'total': prix_total,
|
||||
'paid': paid,
|
||||
'asso_name': options.name,
|
||||
'line1': options.adresse1,
|
||||
'line2': options.adresse2,
|
||||
'siret': options.siret,
|
||||
'email': options.contact,
|
||||
'phone': options.telephone,
|
||||
'asso_name': AssoOption.get_cached_value('name'),
|
||||
'line1': AssoOption.get_cached_value('adresse1'),
|
||||
'line2': AssoOption.get_cached_value('adresse2'),
|
||||
'siret': AssoOption.get_cached_value('siret'),
|
||||
'email': AssoOption.get_cached_value('contact'),
|
||||
'phone': AssoOption.get_cached_value('telephone'),
|
||||
'tpl_path': os.path.join(settings.BASE_DIR, LOGO_PATH)
|
||||
})
|
||||
return form({
|
||||
'factureform': facture_form
|
||||
'factureform': facture_form,
|
||||
'action_name' : 'Editer'
|
||||
}, 'cotisations/facture.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
def facture_pdf(request, factureid):
|
||||
@can_view(Facture)
|
||||
def facture_pdf(request, facture, factureid):
|
||||
"""Affiche en pdf une facture. Cree une ligne par Vente de la facture,
|
||||
et génére une facture avec le total, le moyen de paiement, l'adresse
|
||||
de l'adhérent, etc. Réservée à self pour un user sans droits,
|
||||
les droits cableurs permettent d'afficher toute facture"""
|
||||
try:
|
||||
facture = Facture.objects.get(pk=factureid)
|
||||
except Facture.DoesNotExist:
|
||||
messages.error(request, u"Facture inexistante")
|
||||
return redirect(reverse('cotisations:index'))
|
||||
if not request.user.has_perms(('cableur',))\
|
||||
and facture.user != request.user:
|
||||
messages.error(request, "Vous ne pouvez pas afficher une facture ne vous\
|
||||
appartenant pas sans droit cableur")
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
kwargs={'userid': str(request.user.id)}
|
||||
))
|
||||
if not facture.valid:
|
||||
messages.error(request, "Vous ne pouvez pas afficher\
|
||||
une facture non valide")
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
kwargs={'userid': str(request.user.id)}
|
||||
))
|
||||
|
||||
ventes_objects = Vente.objects.all().filter(facture=facture)
|
||||
ventes = []
|
||||
options, _created = AssoOption.objects.get_or_create()
|
||||
for vente in ventes_objects:
|
||||
ventes.append([vente, vente.number, vente.prix_total])
|
||||
return render_invoice(request, {
|
||||
|
@ -240,38 +231,23 @@ def facture_pdf(request, factureid):
|
|||
'dest': facture.user,
|
||||
'article': ventes,
|
||||
'total': facture.prix_total(),
|
||||
'asso_name': options.name,
|
||||
'line1': options.adresse1,
|
||||
'line2': options.adresse2,
|
||||
'siret': options.siret,
|
||||
'email': options.contact,
|
||||
'phone': options.telephone,
|
||||
'asso_name': AssoOption.get_cached_value('name'),
|
||||
'line1': AssoOption.get_cached_value('adresse1'),
|
||||
'line2': AssoOption.get_cached_value('adresse2'),
|
||||
'siret': AssoOption.get_cached_value('siret'),
|
||||
'email': AssoOption.get_cached_value('contact'),
|
||||
'phone': AssoOption.get_cached_value('telephone'),
|
||||
'tpl_path': os.path.join(settings.BASE_DIR, LOGO_PATH)
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('cableur')
|
||||
def edit_facture(request, factureid):
|
||||
@can_edit(Facture)
|
||||
def edit_facture(request, facture, factureid):
|
||||
"""Permet l'édition d'une facture. On peut y éditer les ventes
|
||||
déjà effectuer, ou rendre une facture invalide (non payées, chèque
|
||||
en bois etc). Mets à jour les durée de cotisation attenantes"""
|
||||
try:
|
||||
facture = Facture.objects.get(pk=factureid)
|
||||
except Facture.DoesNotExist:
|
||||
messages.error(request, u"Facture inexistante")
|
||||
return redirect(reverse('cotisations:index'))
|
||||
if request.user.has_perms(['tresorier']):
|
||||
facture_form = TrezEditFactureForm(
|
||||
request.POST or None,
|
||||
instance=facture
|
||||
)
|
||||
elif facture.control or not facture.valid:
|
||||
messages.error(request, "Vous ne pouvez pas editer une facture\
|
||||
controlée ou invalidée par le trésorier")
|
||||
return redirect(reverse('cotisations:index'))
|
||||
else:
|
||||
facture_form = EditFactureForm(request.POST or None, instance=facture)
|
||||
facture_form = EditFactureForm(request.POST or None, instance=facture, user=request.user)
|
||||
ventes_objects = Vente.objects.filter(facture=facture)
|
||||
vente_form_set = modelformset_factory(
|
||||
Vente,
|
||||
|
@ -297,19 +273,10 @@ def edit_facture(request, factureid):
|
|||
|
||||
|
||||
@login_required
|
||||
@permission_required('cableur')
|
||||
def del_facture(request, factureid):
|
||||
@can_delete(Facture)
|
||||
def del_facture(request, facture, factureid):
|
||||
"""Suppression d'une facture. Supprime en cascade les ventes
|
||||
et cotisations filles"""
|
||||
try:
|
||||
facture = Facture.objects.get(pk=factureid)
|
||||
except Facture.DoesNotExist:
|
||||
messages.error(request, u"Facture inexistante")
|
||||
return redirect(reverse('cotisations:index'))
|
||||
if facture.control or not facture.valid:
|
||||
messages.error(request, "Vous ne pouvez pas editer une facture\
|
||||
controlée ou invalidée par le trésorier")
|
||||
return redirect(reverse('cotisations:index'))
|
||||
if request.method == "POST":
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
facture.delete()
|
||||
|
@ -323,14 +290,10 @@ def del_facture(request, factureid):
|
|||
|
||||
|
||||
@login_required
|
||||
@permission_required('cableur')
|
||||
def credit_solde(request, userid):
|
||||
@can_create(Facture)
|
||||
@can_edit(User)
|
||||
def credit_solde(request, user, userid):
|
||||
""" Credit ou débit de solde """
|
||||
try:
|
||||
user = User.objects.get(pk=userid)
|
||||
except User.DoesNotExist:
|
||||
messages.error(request, u"Utilisateur inexistant")
|
||||
return redirect(reverse('cotisations:index'))
|
||||
facture = CreditSoldeForm(request.POST or None)
|
||||
if facture.is_valid():
|
||||
facture_instance = facture.save(commit=False)
|
||||
|
@ -351,11 +314,11 @@ def credit_solde(request, userid):
|
|||
reversion.set_comment("Création")
|
||||
messages.success(request, "Solde modifié")
|
||||
return redirect(reverse('cotisations:index'))
|
||||
return form({'factureform': facture}, 'cotisations/facture.html', request)
|
||||
return form({'factureform': facture, 'action_name' : 'Créditer'}, 'cotisations/facture.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('tresorier')
|
||||
@can_create(Article)
|
||||
def add_article(request):
|
||||
"""Ajoute un article. Champs : désignation,
|
||||
prix, est-ce une cotisation et si oui sa durée
|
||||
|
@ -372,19 +335,14 @@ def add_article(request):
|
|||
reversion.set_comment("Création")
|
||||
messages.success(request, "L'article a été ajouté")
|
||||
return redirect(reverse('cotisations:index-article'))
|
||||
return form({'factureform': article}, 'cotisations/facture.html', request)
|
||||
return form({'factureform': article, 'action_name' : 'Ajouter'}, 'cotisations/facture.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('tresorier')
|
||||
def edit_article(request, articleid):
|
||||
@can_edit(Article)
|
||||
def edit_article(request, article_instance, articleid):
|
||||
"""Edition d'un article (designation, prix, etc)
|
||||
Réservé au trésorier"""
|
||||
try:
|
||||
article_instance = Article.objects.get(pk=articleid)
|
||||
except Article.DoesNotExist:
|
||||
messages.error(request, u"Entrée inexistante")
|
||||
return redirect(reverse('cotisations:index-article'))
|
||||
article = ArticleForm(request.POST or None, instance=article_instance)
|
||||
if article.is_valid():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
|
@ -397,14 +355,14 @@ def edit_article(request, articleid):
|
|||
)
|
||||
messages.success(request, "Type d'article modifié")
|
||||
return redirect(reverse('cotisations:index-article'))
|
||||
return form({'factureform': article}, 'cotisations/facture.html', request)
|
||||
return form({'factureform': article, 'action_name' : 'Editer'}, 'cotisations/facture.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('tresorier')
|
||||
def del_article(request):
|
||||
@can_delete_set(Article)
|
||||
def del_article(request, instances):
|
||||
"""Suppression d'un article en vente"""
|
||||
article = DelArticleForm(request.POST or None)
|
||||
article = DelArticleForm(request.POST or None, instances=instances)
|
||||
if article.is_valid():
|
||||
article_del = article.cleaned_data['articles']
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
|
@ -412,11 +370,11 @@ def del_article(request):
|
|||
reversion.set_user(request.user)
|
||||
messages.success(request, "Le/les articles ont été supprimé")
|
||||
return redirect(reverse('cotisations:index-article'))
|
||||
return form({'factureform': article}, 'cotisations/facture.html', request)
|
||||
return form({'factureform': article, 'action_name' : 'Supprimer'}, 'cotisations/facture.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('tresorier')
|
||||
@can_create(Paiement)
|
||||
def add_paiement(request):
|
||||
"""Ajoute un moyen de paiement. Relié aux factures
|
||||
via foreign key"""
|
||||
|
@ -428,18 +386,13 @@ def add_paiement(request):
|
|||
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}, 'cotisations/facture.html', request)
|
||||
return form({'factureform': paiement, 'action_name' : 'Ajouter'}, 'cotisations/facture.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('tresorier')
|
||||
def edit_paiement(request, paiementid):
|
||||
@can_edit(Paiement)
|
||||
def edit_paiement(request, paiement_instance, paiementid):
|
||||
"""Edition d'un moyen de paiement"""
|
||||
try:
|
||||
paiement_instance = Paiement.objects.get(pk=paiementid)
|
||||
except Paiement.DoesNotExist:
|
||||
messages.error(request, u"Entrée inexistante")
|
||||
return redirect(reverse('cotisations:index-paiement'))
|
||||
paiement = PaiementForm(request.POST or None, instance=paiement_instance)
|
||||
if paiement.is_valid():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
|
@ -452,14 +405,14 @@ def edit_paiement(request, paiementid):
|
|||
)
|
||||
messages.success(request, "Type de paiement modifié")
|
||||
return redirect(reverse('cotisations:index-paiement'))
|
||||
return form({'factureform': paiement}, 'cotisations/facture.html', request)
|
||||
return form({'factureform': paiement, 'action_name' : 'Editer'}, 'cotisations/facture.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('tresorier')
|
||||
def del_paiement(request):
|
||||
@can_delete_set(Paiement)
|
||||
def del_paiement(request, instances):
|
||||
"""Suppression d'un moyen de paiement"""
|
||||
paiement = DelPaiementForm(request.POST or None)
|
||||
paiement = DelPaiementForm(request.POST or None, instances=instances)
|
||||
if paiement.is_valid():
|
||||
paiement_dels = paiement.cleaned_data['paiements']
|
||||
for paiement_del in paiement_dels:
|
||||
|
@ -479,11 +432,11 @@ def del_paiement(request):
|
|||
facture, vous ne pouvez pas le supprimer" % paiement_del
|
||||
)
|
||||
return redirect(reverse('cotisations:index-paiement'))
|
||||
return form({'factureform': paiement}, 'cotisations/facture.html', request)
|
||||
return form({'factureform': paiement, 'action_name' : 'Supprimer'}, 'cotisations/facture.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('cableur')
|
||||
@can_create(Banque)
|
||||
def add_banque(request):
|
||||
"""Ajoute une banque à la liste des banques"""
|
||||
banque = BanqueForm(request.POST or None)
|
||||
|
@ -494,18 +447,13 @@ def add_banque(request):
|
|||
reversion.set_comment("Création")
|
||||
messages.success(request, "La banque a été ajoutée")
|
||||
return redirect(reverse('cotisations:index-banque'))
|
||||
return form({'factureform': banque}, 'cotisations/facture.html', request)
|
||||
return form({'factureform': banque, 'action_name' : 'Ajouter'}, 'cotisations/facture.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('tresorier')
|
||||
def edit_banque(request, banqueid):
|
||||
@can_edit(Banque)
|
||||
def edit_banque(request, banque_instance, banqueid):
|
||||
"""Edite le nom d'une banque"""
|
||||
try:
|
||||
banque_instance = Banque.objects.get(pk=banqueid)
|
||||
except Banque.DoesNotExist:
|
||||
messages.error(request, u"Entrée inexistante")
|
||||
return redirect(reverse('cotisations:index-banque'))
|
||||
banque = BanqueForm(request.POST or None, instance=banque_instance)
|
||||
if banque.is_valid():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
|
@ -518,14 +466,14 @@ def edit_banque(request, banqueid):
|
|||
)
|
||||
messages.success(request, "Banque modifiée")
|
||||
return redirect(reverse('cotisations:index-banque'))
|
||||
return form({'factureform': banque}, 'cotisations/facture.html', request)
|
||||
return form({'factureform': banque, 'action_name' : 'Editer'}, 'cotisations/facture.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('tresorier')
|
||||
def del_banque(request):
|
||||
@can_delete_set(Banque)
|
||||
def del_banque(request, instances):
|
||||
"""Supprime une banque"""
|
||||
banque = DelBanqueForm(request.POST or None)
|
||||
banque = DelBanqueForm(request.POST or None, instances=instances)
|
||||
if banque.is_valid():
|
||||
banque_dels = banque.cleaned_data['banques']
|
||||
for banque_del in banque_dels:
|
||||
|
@ -539,16 +487,16 @@ def del_banque(request):
|
|||
messages.error(request, "La banque %s est affectée à au moins\
|
||||
une facture, vous ne pouvez pas la supprimer" % banque_del)
|
||||
return redirect(reverse('cotisations:index-banque'))
|
||||
return form({'factureform': banque}, 'cotisations/facture.html', request)
|
||||
return form({'factureform': banque, 'action_name' : 'Supprimer'}, 'cotisations/facture.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('tresorier')
|
||||
@can_view_all(Facture)
|
||||
@can_change(Facture, 'control')
|
||||
def control(request):
|
||||
"""Pour le trésorier, vue pour controler en masse les
|
||||
factures.Case à cocher, pratique"""
|
||||
options, _created = GeneralOption.objects.get_or_create()
|
||||
pagination_number = options.pagination_number
|
||||
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
||||
facture_list = Facture.objects.select_related('user').select_related('paiement')
|
||||
facture_list = SortTable.sort(
|
||||
facture_list,
|
||||
|
@ -583,7 +531,7 @@ def control(request):
|
|||
|
||||
|
||||
@login_required
|
||||
@permission_required('cableur')
|
||||
@can_view_all(Article)
|
||||
def index_article(request):
|
||||
"""Affiche l'ensemble des articles en vente"""
|
||||
article_list = Article.objects.order_by('name')
|
||||
|
@ -593,7 +541,7 @@ def index_article(request):
|
|||
|
||||
|
||||
@login_required
|
||||
@permission_required('cableur')
|
||||
@can_view_all(Paiement)
|
||||
def index_paiement(request):
|
||||
"""Affiche l'ensemble des moyens de paiement en vente"""
|
||||
paiement_list = Paiement.objects.order_by('moyen')
|
||||
|
@ -603,7 +551,7 @@ def index_paiement(request):
|
|||
|
||||
|
||||
@login_required
|
||||
@permission_required('cableur')
|
||||
@can_view_all(Banque)
|
||||
def index_banque(request):
|
||||
"""Affiche l'ensemble des banques"""
|
||||
banque_list = Banque.objects.order_by('name')
|
||||
|
@ -613,11 +561,10 @@ def index_banque(request):
|
|||
|
||||
|
||||
@login_required
|
||||
@permission_required('cableur')
|
||||
@can_view_all(Facture)
|
||||
def index(request):
|
||||
"""Affiche l'ensemble des factures, pour les cableurs et +"""
|
||||
options, _created = GeneralOption.objects.get_or_create()
|
||||
pagination_number = options.pagination_number
|
||||
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
||||
facture_list = Facture.objects.select_related('user')\
|
||||
.select_related('paiement').prefetch_related('vente_set')
|
||||
facture_list = SortTable.sort(
|
||||
|
@ -642,57 +589,122 @@ def index(request):
|
|||
|
||||
|
||||
@login_required
|
||||
def history(request, object_name, object_id):
|
||||
"""Affiche l'historique de chaque objet"""
|
||||
if object_name == 'facture':
|
||||
try:
|
||||
object_instance = Facture.objects.get(pk=object_id)
|
||||
except Facture.DoesNotExist:
|
||||
messages.error(request, "Facture inexistante")
|
||||
return redirect(reverse('cotisations:index'))
|
||||
if not request.user.has_perms(('cableur',))\
|
||||
and object_instance.user != request.user:
|
||||
messages.error(request, "Vous ne pouvez pas afficher l'historique\
|
||||
d'une facture d'un autre user que vous sans droit cableur")
|
||||
def new_facture_solde(request, userid):
|
||||
"""Creation d'une facture pour un user. Renvoie la liste des articles
|
||||
et crée des factures dans un formset. Utilise un peu de js coté template
|
||||
pour ajouter des articles.
|
||||
Parse les article et boucle dans le formset puis save les ventes,
|
||||
enfin sauve la facture parente.
|
||||
TODO : simplifier cette fonction, déplacer l'intelligence coté models
|
||||
Facture et Vente."""
|
||||
user = request.user
|
||||
facture = Facture(user=user)
|
||||
paiement, _created = Paiement.objects.get_or_create(moyen='Solde')
|
||||
facture.paiement = paiement
|
||||
# Le template a besoin de connaitre les articles pour le js
|
||||
article_list = Article.objects.filter(
|
||||
Q(type_user='All') | Q(type_user=request.user.class_name)
|
||||
)
|
||||
if request.user.is_class_club:
|
||||
article_formset = formset_factory(SelectClubArticleForm)(request.POST or None)
|
||||
else:
|
||||
article_formset = formset_factory(SelectUserArticleForm)(request.POST or None)
|
||||
if article_formset.is_valid():
|
||||
articles = article_formset
|
||||
# Si au moins un article est rempli
|
||||
if any(art.cleaned_data for art in articles):
|
||||
user_solde = OptionalUser.get_cached_value('user_solde')
|
||||
solde_negatif = OptionalUser.get_cached_value('solde_negatif')
|
||||
# Si on paye par solde, que l'option est activée,
|
||||
# on vérifie que le négatif n'est pas atteint
|
||||
if user_solde:
|
||||
prix_total = 0
|
||||
for art_item in articles:
|
||||
if art_item.cleaned_data:
|
||||
prix_total += art_item.cleaned_data['article']\
|
||||
.prix*art_item.cleaned_data['quantity']
|
||||
if float(user.solde) - float(prix_total) < solde_negatif:
|
||||
messages.error(request, "Le solde est insuffisant pour\
|
||||
effectuer l'opération")
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
kwargs={'userid':str(request.user.id)}
|
||||
kwargs={'userid': userid}
|
||||
))
|
||||
elif object_name == 'paiement' and request.user.has_perms(('cableur',)):
|
||||
try:
|
||||
object_instance = Paiement.objects.get(pk=object_id)
|
||||
except Paiement.DoesNotExist:
|
||||
messages.error(request, "Paiement inexistant")
|
||||
return redirect(reverse('cotisations:index'))
|
||||
elif object_name == 'article' and request.user.has_perms(('cableur',)):
|
||||
try:
|
||||
object_instance = Article.objects.get(pk=object_id)
|
||||
except Article.DoesNotExist:
|
||||
messages.error(request, "Article inexistante")
|
||||
return redirect(reverse('cotisations:index'))
|
||||
elif object_name == 'banque' and request.user.has_perms(('cableur',)):
|
||||
try:
|
||||
object_instance = Banque.objects.get(pk=object_id)
|
||||
except Banque.DoesNotExist:
|
||||
messages.error(request, "Banque inexistante")
|
||||
return redirect(reverse('cotisations:index'))
|
||||
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']
|
||||
quantity = art_item.cleaned_data['quantity']
|
||||
new_vente = Vente.objects.create(
|
||||
facture=facture,
|
||||
name=article.name,
|
||||
prix=article.prix,
|
||||
type_cotisation=article.type_cotisation,
|
||||
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(
|
||||
request,
|
||||
"La cotisation a été prolongée\
|
||||
pour l'adhérent %s jusqu'au %s" % (
|
||||
user.pseudo, user.end_adhesion()
|
||||
)
|
||||
)
|
||||
else:
|
||||
messages.error(request, "Objet inconnu")
|
||||
return redirect(reverse('cotisations:index'))
|
||||
options, _created = GeneralOption.objects.get_or_create()
|
||||
pagination_number = options.pagination_number
|
||||
reversions = Version.objects.get_for_object(object_instance)
|
||||
paginator = Paginator(reversions, pagination_number)
|
||||
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 results.
|
||||
reversions = paginator.page(paginator.num_pages)
|
||||
return render(request, 're2o/history.html', {
|
||||
'reversions': reversions,
|
||||
'object': object_instance
|
||||
})
|
||||
messages.success(request, "La facture a été crée")
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
kwargs={'userid': userid}
|
||||
))
|
||||
messages.error(
|
||||
request,
|
||||
u"Il faut au moins un article valide pour créer une facture"
|
||||
)
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
kwargs={'userid': userid}
|
||||
))
|
||||
|
||||
return form({
|
||||
'venteform': article_formset,
|
||||
'articlelist': article_list
|
||||
}, 'cotisations/new_facture_solde.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
def recharge(request):
|
||||
if AssoOption.get_cached_value('payment') == 'NONE':
|
||||
messages.error(
|
||||
request,
|
||||
"Le paiement en ligne est désactivé."
|
||||
)
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
kwargs={'userid': request.user.id}
|
||||
))
|
||||
f = RechargeForm(request.POST or None, user=request.user)
|
||||
if f.is_valid():
|
||||
facture = Facture(user=request.user)
|
||||
paiement, _created = Paiement.objects.get_or_create(moyen='Rechargement en ligne')
|
||||
facture.paiement = paiement
|
||||
facture.valid = False
|
||||
facture.save()
|
||||
v = Vente.objects.create(
|
||||
facture=facture,
|
||||
name='solde',
|
||||
prix=f.cleaned_data['value'],
|
||||
number=1,
|
||||
)
|
||||
v.save()
|
||||
content = payment.PAYMENT_SYSTEM[AssoOption.get_cached_value('payment')](facture, request)
|
||||
return render(request, 'cotisations/payment.html', content)
|
||||
return form({'rechargeform':f}, 'cotisations/recharge.html', request)
|
||||
|
|
|
@ -36,6 +36,15 @@ export DEBIAN_FRONTEND=noninteractive
|
|||
|
||||
apt-get -y install sudo dialog
|
||||
|
||||
HEIGHT=15
|
||||
WIDTH=40
|
||||
init=$(dialog --clear \
|
||||
--title "Installation de Re2o !" \
|
||||
--msgbox "Cet utilitaire va procéder à l'installation initiale de re2o. Le serveur présent doit être vierge de préférence. Preconfiguration..." \
|
||||
$HEIGHT $WIDTH \
|
||||
2>&1 >/dev/tty)
|
||||
|
||||
|
||||
HEIGHT=15
|
||||
WIDTH=40
|
||||
CHOICE_HEIGHT=4
|
||||
|
@ -145,7 +154,14 @@ ldap_is_local=$(dialog --clear \
|
|||
"${OPTIONS[@]}" \
|
||||
2>&1 >/dev/tty)
|
||||
|
||||
echo "Vous devrez fournir un login/host dans le cas où le ldap est non local"
|
||||
|
||||
HEIGHT=15
|
||||
WIDTH=40
|
||||
instal_ldap=$(dialog --clear \
|
||||
--title "Installation de Re2o !" \
|
||||
--msgbox "Vous devrez fournir un login/host dans le cas où le ldap est non local" \
|
||||
$HEIGHT $WIDTH \
|
||||
2>&1 >/dev/tty)
|
||||
|
||||
TITLE="Mot de passe ldap"
|
||||
ldap_password=$(dialog --title "$TITLE" \
|
||||
|
@ -213,9 +229,16 @@ ldap_cn+=$ldap_dn
|
|||
ldap_host="localhost"
|
||||
fi
|
||||
|
||||
HEIGHT=15
|
||||
WIDTH=40
|
||||
install_base=$(dialog --clear \
|
||||
--title "Installation de Re2o !" \
|
||||
--msgbox "Installation des paquets de base" \
|
||||
$HEIGHT $WIDTH \
|
||||
2>&1 >/dev/tty)
|
||||
|
||||
echo "Installation des paquets de base"
|
||||
apt-get -y install python3-django python3-dateutil texlive-latex-base texlive-fonts-recommended python3-djangorestframework python3-django-reversion python3-pip libsasl2-dev libldap2-dev libssl-dev
|
||||
apt-get -y install python3-django python3-dateutil texlive-latex-base texlive-fonts-recommended python3-djangorestframework python3-django-reversion python3-pip libsasl2-dev libldap2-dev libssl-dev python3-crypto
|
||||
pip3 install django-bootstrap3
|
||||
pip3 install django-ldapdb
|
||||
pip3 install django-macaddress
|
||||
|
@ -270,13 +293,20 @@ then
|
|||
setup_ldap $ldap_password $ldap_dn
|
||||
|
||||
else
|
||||
echo "Vous devrez manuellement effectuer les opérations de setup de la base ldap sur le serveurs distant.
|
||||
Lancez la commande : ./install_re2o.sh ldap $ldap_password $ldap_dn"
|
||||
|
||||
HEIGHT=15
|
||||
WIDTH=40
|
||||
ldap_setup=$(dialog --clear \
|
||||
--title "Setup ldap" \
|
||||
--msgbox "Vous devrez manuellement effectuer les opérations de setup de la base ldap sur le serveurs distant. Lancez la commande : ./install_re2o.sh ldap $ldap_password $ldap_dn" \
|
||||
$HEIGHT $WIDTH \
|
||||
2>&1 >/dev/tty)
|
||||
fi
|
||||
|
||||
echo "Ecriture de settings_local"
|
||||
|
||||
django_secret_key=$(python -c "import random; print(''.join([random.SystemRandom().choice('abcdefghijklmnopqrstuvwxyz0123456789%=+') for i in range(50)]))")
|
||||
aes_key=$(python -c "import random; print(''.join([random.SystemRandom().choice('abcdefghijklmnopqrstuvwxyz0123456789%=+') for i in range(32)]))")
|
||||
|
||||
cp re2o/settings_local.example.py re2o/settings_local.py
|
||||
if [ $sql_bdd_type == 1 ]
|
||||
|
@ -286,6 +316,7 @@ else
|
|||
sed -i 's/db_engine/django.db.backends.postgresql_psycopg2/g' re2o/settings_local.py
|
||||
fi
|
||||
sed -i 's/SUPER_SECRET_KEY/'"$django_secret_key"'/g' re2o/settings_local.py
|
||||
sed -i 's/THE_AES_KEY/'"$aes_key"'/g' re2o/settings_local.py
|
||||
sed -i 's/SUPER_SECRET_DB/'"$sql_password"'/g' re2o/settings_local.py
|
||||
sed -i 's/db_name_value/'"$sql_name"'/g' re2o/settings_local.py
|
||||
sed -i 's/db_user_value/'"$sql_login"'/g' re2o/settings_local.py
|
||||
|
@ -298,10 +329,22 @@ sed -i 's/example.org/'"$extension_locale"'/g' re2o/settings_local.py
|
|||
sed -i 's/MY_EMAIL_HOST/'"$email_host"'/g' re2o/settings_local.py
|
||||
sed -i 's/MY_EMAIL_PORT/'"$email_port"'/g' re2o/settings_local.py
|
||||
|
||||
echo "Application des migrations"
|
||||
HEIGHT=15
|
||||
WIDTH=40
|
||||
migrations=$(dialog --clear \
|
||||
--title "Setup django" \
|
||||
--msgbox "Application des migrations" \
|
||||
$HEIGHT $WIDTH \
|
||||
2>&1 >/dev/tty)
|
||||
python3 manage.py migrate
|
||||
|
||||
echo "Collecte des statics"
|
||||
HEIGHT=15
|
||||
WIDTH=40
|
||||
static=$(dialog --clear \
|
||||
--title "Setup django" \
|
||||
--msgbox "Collecte des statiques" \
|
||||
$HEIGHT $WIDTH \
|
||||
2>&1 >/dev/tty)
|
||||
python3 manage.py collectstatic
|
||||
|
||||
BACKTITLE="Fin de l'installation"
|
||||
|
@ -319,7 +362,7 @@ web_serveur=$(dialog --clear \
|
|||
|
||||
clear
|
||||
|
||||
TITLE="Url où servir le serveur web (ex : re2o.example.org)"
|
||||
TITLE="Url où servir le serveur web (ex : re2o.example.org). Assurez-vous que ce tld existe bien et répond auprès du DNS"
|
||||
url_server=$(dialog --title "$TITLE" \
|
||||
--backtitle "$BACKTITLE" \
|
||||
--inputbox "$TITLE" $HEIGHT $WIDTH \
|
||||
|
@ -365,11 +408,25 @@ sed -i 's|PATH|'"$current_path"'|g' /etc/apache2/sites-available/re2o.conf
|
|||
a2ensite re2o
|
||||
service apache2 reload
|
||||
else
|
||||
echo "Nginx non supporté, vous devrez installer manuellement"
|
||||
HEIGHT=15
|
||||
WIDTH=40
|
||||
web_server=$(dialog --clear \
|
||||
--title "Setup serveur web" \
|
||||
--msgbox "Nginx non supporté, vous devrez installer manuellement" \
|
||||
$HEIGHT $WIDTH \
|
||||
2>&1 >/dev/tty)
|
||||
|
||||
fi
|
||||
|
||||
python3 manage.py createsuperuser
|
||||
|
||||
HEIGHT=15
|
||||
WIDTH=40
|
||||
end=$(dialog --clear \
|
||||
--title "Installation terminée" \
|
||||
--msgbox "Vous pouvez à présent vous rendre sur $url_server, et vous connecter. Votre utilisateur dispose des privilèges superuser" \
|
||||
$HEIGHT $WIDTH \
|
||||
2>&1 >/dev/tty)
|
||||
}
|
||||
|
||||
main_function() {
|
||||
|
|
|
@ -21,3 +21,4 @@
|
|||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
from .acl import *
|
||||
|
|
40
logs/acl.py
Normal file
40
logs/acl.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
# -*- 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 © 2017 Gabriel Détraz
|
||||
# Copyright © 2017 Goulven Kermarec
|
||||
# Copyright © 2017 Augustin Lemesle
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""logs.acl
|
||||
|
||||
Here are defined some functions to check acl on the application.
|
||||
"""
|
||||
|
||||
def can_view(user):
|
||||
"""Check if an user can view the application.
|
||||
|
||||
Args:
|
||||
user: The user who wants to view the application.
|
||||
|
||||
Returns:
|
||||
A couple (allowed, msg) where allowed is a boolean which is True if
|
||||
viewing is granted and msg is a message (can be None).
|
||||
"""
|
||||
can = user.has_module_perms('admin')
|
||||
return can, None if can else "Vous ne pouvez pas voir cette application."
|
|
@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endif %}
|
||||
|
||||
{% load logs_extra %}
|
||||
{% load acl %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
|
@ -47,14 +48,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ revision.user }}</td>
|
||||
<td>{{ revision.date_created }}</td>
|
||||
<td>{{ revision.comment }}</td>
|
||||
{% if is_bureau %}
|
||||
{% can_edit_history %}
|
||||
<td>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' revision.id %}">
|
||||
<i class="glyphicon glyphicon-remove"></i>
|
||||
<i class="fa fa-times"></i>
|
||||
Annuler
|
||||
</a>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
|
|
@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endif %}
|
||||
|
||||
{% load logs_extra %}
|
||||
|
||||
{% load acl %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -51,14 +51,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endif %}
|
||||
</i>)
|
||||
</td>
|
||||
{% if is_bureau %}
|
||||
{% can_edit_history %}
|
||||
<td>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
||||
<i class="glyphicon glyphicon-remove"></i>
|
||||
<i class="fa fa-times"></i>
|
||||
Annuler
|
||||
</a>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</tr>
|
||||
{% elif v.version.content_type.model == 'whitelist' %}
|
||||
<tr class="success">
|
||||
|
@ -74,14 +74,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endif %}
|
||||
</i>)
|
||||
</td>
|
||||
{% if is_bureau %}
|
||||
{% can_edit_history%}
|
||||
<td>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
||||
<i class="glyphicon glyphicon-remove"></i>
|
||||
<i class="fa fa-times"></i>
|
||||
Annuler
|
||||
</a>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</tr>
|
||||
{% elif v.version.content_type.model == 'user' %}
|
||||
<tr>
|
||||
|
@ -93,14 +93,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
(<i>{{ v.comment }}</i>)
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if is_bureau %}
|
||||
{% can_edit_history %}
|
||||
<td>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
||||
<i class="glyphicon glyphicon-remove"></i>
|
||||
<i class="fa fa-times"></i>
|
||||
Annuler
|
||||
</a>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</tr>
|
||||
{% elif v.version.content_type.model == 'vente' %}
|
||||
<tr>
|
||||
|
@ -112,14 +112,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
(<i>+{{ v.version.object.duration }} mois</i>)
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if is_bureau %}
|
||||
{% can_edit_history %}
|
||||
<td>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
||||
<i class="glyphicon glyphicon-remove"></i>
|
||||
<i class="fa fa-times"></i>
|
||||
Annuler
|
||||
</a>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</tr>
|
||||
{% elif v.version.content_type.model == 'interface' %}
|
||||
<tr>
|
||||
|
@ -131,14 +131,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
(<i>{{ v.comment }}</i>)
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if is_bureau %}
|
||||
{% can_edit_history %}
|
||||
<td>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
||||
<i class="glyphicon glyphicon-remove"></i>
|
||||
<i class="fa fa-times"></i>
|
||||
Annuler
|
||||
</a>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
|
|
@ -23,32 +23,33 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
{% block sidebar %}
|
||||
{% if is_cableur %}
|
||||
{% can_view_app logs %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "logs:index" %}">
|
||||
<i class="glyphicon glyphicon-stats"></i>
|
||||
<i class="fa fa-clipboard-list"></i>
|
||||
Résumé
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-info" href="{% url "logs:stats-logs" %}">
|
||||
<i class="glyphicon glyphicon-stats"></i>
|
||||
<i class="fa fa-calendar-alt"></i>
|
||||
Évènements
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-info" href="{% url "logs:stats-general" %}">
|
||||
<i class="glyphicon glyphicon-stats"></i>
|
||||
<i class="fa fa-chart-area"></i>
|
||||
Général
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-info" href="{% url "logs:stats-models" %}">
|
||||
<i class="glyphicon glyphicon-stats"></i>
|
||||
<i class="fa fa-database"></i>
|
||||
Base de données
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-info" href="{% url "logs:stats-actions" %}">
|
||||
<i class="glyphicon glyphicon-stats"></i>
|
||||
<i class="fa fa-plug"></i>
|
||||
Actions de cablage
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-info" href="{% url "logs:stats-users" %}">
|
||||
<i class="glyphicon glyphicon-stats"></i>
|
||||
<i class="fa fa-users"></i>
|
||||
Utilisateurs
|
||||
</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -41,7 +41,7 @@ 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.contrib.auth.decorators import login_required
|
||||
from django.db.models import Count
|
||||
|
||||
from reversion.models import Revision
|
||||
|
@ -50,7 +50,6 @@ from reversion.models import Version, ContentType
|
|||
from users.models import (
|
||||
User,
|
||||
ServiceUser,
|
||||
Right,
|
||||
School,
|
||||
ListRight,
|
||||
ListShell,
|
||||
|
@ -93,7 +92,17 @@ from topologie.models import (
|
|||
)
|
||||
from preferences.models import GeneralOption
|
||||
from re2o.views import form
|
||||
from re2o.utils import all_whitelisted, all_baned, all_has_access, all_adherent
|
||||
from re2o.utils import (
|
||||
all_whitelisted,
|
||||
all_baned,
|
||||
all_has_access,
|
||||
all_adherent,
|
||||
)
|
||||
from re2o.acl import (
|
||||
can_view_all,
|
||||
can_view_app,
|
||||
can_edit_history,
|
||||
)
|
||||
from re2o.utils import all_active_assigned_interfaces_count
|
||||
from re2o.utils import all_active_interfaces_count, SortTable
|
||||
|
||||
|
@ -108,12 +117,11 @@ STATS_DICT = {
|
|||
|
||||
|
||||
@login_required
|
||||
@permission_required('cableur')
|
||||
@can_view_app('logs')
|
||||
def index(request):
|
||||
"""Affiche les logs affinés, date reformatées, selectionne
|
||||
les event importants (ajout de droits, ajout de ban/whitelist)"""
|
||||
options, _created = GeneralOption.objects.get_or_create()
|
||||
pagination_number = options.pagination_number
|
||||
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
||||
# The types of content kept for display
|
||||
content_type_filter = ['ban', 'whitelist', 'vente', 'interface', 'user']
|
||||
# Select only wanted versions
|
||||
|
@ -167,12 +175,11 @@ def index(request):
|
|||
|
||||
|
||||
@login_required
|
||||
@permission_required('cableur')
|
||||
@can_view_all(GeneralOption)
|
||||
def stats_logs(request):
|
||||
"""Affiche l'ensemble des logs et des modifications sur les objets,
|
||||
classés par date croissante, en vrac"""
|
||||
options, _created = GeneralOption.objects.get_or_create()
|
||||
pagination_number = options.pagination_number
|
||||
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
||||
revisions = Revision.objects.all().select_related('user')\
|
||||
.prefetch_related('version_set__object')
|
||||
revisions = SortTable.sort(
|
||||
|
@ -197,7 +204,7 @@ def stats_logs(request):
|
|||
|
||||
|
||||
@login_required
|
||||
@permission_required('bureau')
|
||||
@can_edit_history
|
||||
def revert_action(request, revision_id):
|
||||
""" Annule l'action en question """
|
||||
try:
|
||||
|
@ -215,7 +222,9 @@ def revert_action(request, revision_id):
|
|||
|
||||
|
||||
@login_required
|
||||
@permission_required('cableur')
|
||||
@can_view_all(IpList)
|
||||
@can_view_all(Interface)
|
||||
@can_view_all(User)
|
||||
def stats_general(request):
|
||||
"""Statistiques générales affinées sur les ip, activées, utilisées par
|
||||
range, et les statistiques générales sur les users : users actifs,
|
||||
|
@ -298,7 +307,10 @@ def stats_general(request):
|
|||
|
||||
|
||||
@login_required
|
||||
@permission_required('cableur')
|
||||
@can_view_app('users')
|
||||
@can_view_app('cotisations')
|
||||
@can_view_app('machines')
|
||||
@can_view_app('topologie')
|
||||
def stats_models(request):
|
||||
"""Statistiques générales, affiche les comptages par models:
|
||||
nombre d'users, d'écoles, de droits, de bannissements,
|
||||
|
@ -310,7 +322,6 @@ def stats_models(request):
|
|||
'clubs': [Club.PRETTY_NAME, Club.objects.count()],
|
||||
'serviceuser': [ServiceUser.PRETTY_NAME,
|
||||
ServiceUser.objects.count()],
|
||||
'right': [Right.PRETTY_NAME, Right.objects.count()],
|
||||
'school': [School.PRETTY_NAME, School.objects.count()],
|
||||
'listright': [ListRight.PRETTY_NAME, ListRight.objects.count()],
|
||||
'listshell': [ListShell.PRETTY_NAME, ListShell.objects.count()],
|
||||
|
@ -340,7 +351,7 @@ def stats_models(request):
|
|||
OuverturePortList.objects.count()
|
||||
],
|
||||
'vlan': [Vlan.PRETTY_NAME, Vlan.objects.count()],
|
||||
'SOA': [Mx.PRETTY_NAME, Mx.objects.count()],
|
||||
'SOA': [SOA.PRETTY_NAME, SOA.objects.count()],
|
||||
'Mx': [Mx.PRETTY_NAME, Mx.objects.count()],
|
||||
'Ns': [Ns.PRETTY_NAME, Ns.objects.count()],
|
||||
'nas': [Nas.PRETTY_NAME, Nas.objects.count()],
|
||||
|
@ -368,7 +379,7 @@ def stats_models(request):
|
|||
|
||||
|
||||
@login_required
|
||||
@permission_required('cableur')
|
||||
@can_view_app('users')
|
||||
def stats_users(request):
|
||||
"""Affiche les statistiques base de données aggrégées par user :
|
||||
nombre de machines par user, d'etablissements par user,
|
||||
|
@ -395,7 +406,7 @@ def stats_users(request):
|
|||
num=Count('whitelist')
|
||||
).order_by('-num')[:10],
|
||||
'Droits': User.objects.annotate(
|
||||
num=Count('right')
|
||||
num=Count('groups')
|
||||
).order_by('-num')[:10],
|
||||
},
|
||||
'Etablissement': {
|
||||
|
@ -422,7 +433,7 @@ def stats_users(request):
|
|||
|
||||
|
||||
@login_required
|
||||
@permission_required('cableur')
|
||||
@can_view_app('users')
|
||||
def stats_actions(request):
|
||||
"""Vue qui affiche les statistiques de modifications d'objets par
|
||||
utilisateurs.
|
||||
|
|
|
@ -21,3 +21,4 @@
|
|||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
from .acl import *
|
||||
|
|
40
machines/acl.py
Normal file
40
machines/acl.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
# -*- 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 © 2017 Gabriel Détraz
|
||||
# Copyright © 2017 Goulven Kermarec
|
||||
# Copyright © 2017 Augustin Lemesle
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""machines.acl
|
||||
|
||||
Here are defined some functions to check acl on the application.
|
||||
"""
|
||||
|
||||
def can_view(user):
|
||||
"""Check if an user can view the application.
|
||||
|
||||
Args:
|
||||
user: The user who wants to view the application.
|
||||
|
||||
Returns:
|
||||
A couple (allowed, msg) where allowed is a boolean which is True if
|
||||
viewing is granted and msg is a message (can be None).
|
||||
"""
|
||||
can = user.has_module_perms('machines')
|
||||
return can, None if can else "Vous ne pouvez pas voir cette application."
|
|
@ -38,6 +38,8 @@ from __future__ import unicode_literals
|
|||
from django.forms import ModelForm, Form
|
||||
from django import forms
|
||||
|
||||
from re2o.field_permissions import FieldPermissionFormMixin
|
||||
|
||||
from .models import (
|
||||
Domain,
|
||||
Machine,
|
||||
|
@ -55,10 +57,11 @@ from .models import (
|
|||
Nas,
|
||||
IpType,
|
||||
OuverturePortList,
|
||||
Ipv6List,
|
||||
)
|
||||
|
||||
|
||||
class EditMachineForm(ModelForm):
|
||||
class EditMachineForm(FieldPermissionFormMixin, ModelForm):
|
||||
"""Formulaire d'édition d'une machine"""
|
||||
class Meta:
|
||||
model = Machine
|
||||
|
@ -76,14 +79,7 @@ class NewMachineForm(EditMachineForm):
|
|||
fields = ['name']
|
||||
|
||||
|
||||
class BaseEditMachineForm(EditMachineForm):
|
||||
"""Edition basique, ne permet que de changer le nom et le statut.
|
||||
Réservé aux users sans droits spécifiques"""
|
||||
class Meta(EditMachineForm.Meta):
|
||||
fields = ['name', 'active']
|
||||
|
||||
|
||||
class EditInterfaceForm(ModelForm):
|
||||
class EditInterfaceForm(FieldPermissionFormMixin, ModelForm):
|
||||
"""Edition d'une interface. Edition complète"""
|
||||
class Meta:
|
||||
model = Interface
|
||||
|
@ -91,13 +87,21 @@ class EditInterfaceForm(ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
user = kwargs.get('user')
|
||||
super(EditInterfaceForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields['mac_address'].label = 'Adresse mac'
|
||||
self.fields['type'].label = 'Type de machine'
|
||||
self.fields['type'].empty_label = "Séléctionner un type de machine"
|
||||
if "ipv4" in self.fields:
|
||||
self.fields['ipv4'].empty_label = "Assignation automatique\
|
||||
de l'ipv4"
|
||||
self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4"
|
||||
self.fields['ipv4'].queryset = IpList.objects.filter(
|
||||
interface__isnull=True
|
||||
)
|
||||
if not IpType.can_use_all(user):
|
||||
self.fields['ipv4'].queryset = IpList.objects.filter(
|
||||
interface__isnull=True
|
||||
).filter(ip_type__in=IpType.objects.filter(need_infra=False))
|
||||
else:
|
||||
self.fields['ipv4'].queryset = IpList.objects.filter(
|
||||
interface__isnull=True
|
||||
)
|
||||
|
@ -108,6 +112,10 @@ class EditInterfaceForm(ModelForm):
|
|||
if "machine" in self.fields:
|
||||
self.fields['machine'].queryset = Machine.objects.all()\
|
||||
.select_related('user')
|
||||
if not MachineType.can_use_all(user):
|
||||
self.fields['type'].queryset = MachineType.objects.filter(
|
||||
ip_type__in=IpType.objects.filter(need_infra=False)
|
||||
)
|
||||
|
||||
|
||||
class AddInterfaceForm(EditInterfaceForm):
|
||||
|
@ -116,58 +124,6 @@ class AddInterfaceForm(EditInterfaceForm):
|
|||
class Meta(EditInterfaceForm.Meta):
|
||||
fields = ['type', 'ipv4', 'mac_address', 'details']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
infra = kwargs.pop('infra')
|
||||
super(AddInterfaceForm, self).__init__(*args, **kwargs)
|
||||
self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4"
|
||||
if not infra:
|
||||
self.fields['type'].queryset = MachineType.objects.filter(
|
||||
ip_type__in=IpType.objects.filter(need_infra=False)
|
||||
)
|
||||
self.fields['ipv4'].queryset = IpList.objects.filter(
|
||||
interface__isnull=True
|
||||
).filter(ip_type__in=IpType.objects.filter(need_infra=False))
|
||||
else:
|
||||
self.fields['ipv4'].queryset = IpList.objects.filter(
|
||||
interface__isnull=True
|
||||
)
|
||||
|
||||
|
||||
class NewInterfaceForm(EditInterfaceForm):
|
||||
"""Formulaire light, sans choix de l'ipv4; d'ajout d'une interface"""
|
||||
class Meta(EditInterfaceForm.Meta):
|
||||
fields = ['type', 'mac_address', 'details']
|
||||
|
||||
|
||||
class BaseEditInterfaceForm(EditInterfaceForm):
|
||||
"""Edition basique d'une interface. En fonction des droits,
|
||||
ajoute ou non l'ensemble des ipv4 disponibles (infra)"""
|
||||
class Meta(EditInterfaceForm.Meta):
|
||||
fields = ['type', 'ipv4', 'mac_address', 'details']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
infra = kwargs.pop('infra')
|
||||
super(BaseEditInterfaceForm, self).__init__(*args, **kwargs)
|
||||
self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4"
|
||||
if not infra:
|
||||
self.fields['type'].queryset = MachineType.objects.filter(
|
||||
ip_type__in=IpType.objects.filter(need_infra=False)
|
||||
)
|
||||
self.fields['ipv4'].queryset = IpList.objects.filter(
|
||||
interface__isnull=True
|
||||
).filter(ip_type__in=IpType.objects.filter(need_infra=False))
|
||||
# Add it's own address
|
||||
self.fields['ipv4'].queryset |= IpList.objects.filter(
|
||||
interface=self.instance
|
||||
)
|
||||
else:
|
||||
self.fields['ipv4'].queryset = IpList.objects.filter(
|
||||
interface__isnull=True
|
||||
)
|
||||
self.fields['ipv4'].queryset |= IpList.objects.filter(
|
||||
interface=self.instance
|
||||
)
|
||||
|
||||
|
||||
class AliasForm(ModelForm):
|
||||
"""Ajout d'un alias (et edition), CNAME, contenant nom et extension"""
|
||||
|
@ -177,9 +133,10 @@ class AliasForm(ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
infra = kwargs.pop('infra')
|
||||
user = kwargs.pop('user')
|
||||
super(AliasForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
if not infra:
|
||||
can_use_all, reason = Extension.can_use_all(user)
|
||||
if not can_use_all:
|
||||
self.fields['extension'].queryset = Extension.objects.filter(
|
||||
need_infra=False
|
||||
)
|
||||
|
@ -233,11 +190,19 @@ class MachineTypeForm(ModelForm):
|
|||
class DelMachineTypeForm(Form):
|
||||
"""Suppression d'un ou plusieurs machinetype"""
|
||||
machinetypes = forms.ModelMultipleChoiceField(
|
||||
queryset=MachineType.objects.all(),
|
||||
queryset=MachineType.objects.none(),
|
||||
label="Types de machines actuelles",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop('instances', None)
|
||||
super(DelMachineTypeForm, self).__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields['machinetypes'].queryset = instances
|
||||
else:
|
||||
self.fields['machinetypes'].queryset = MachineType.objects.all()
|
||||
|
||||
|
||||
class IpTypeForm(ModelForm):
|
||||
"""Formulaire d'ajout d'un iptype. Pas d'edition de l'ip de start et de
|
||||
|
@ -264,11 +229,19 @@ class EditIpTypeForm(IpTypeForm):
|
|||
class DelIpTypeForm(Form):
|
||||
"""Suppression d'un ou plusieurs iptype"""
|
||||
iptypes = forms.ModelMultipleChoiceField(
|
||||
queryset=IpType.objects.all(),
|
||||
queryset=IpType.objects.none(),
|
||||
label="Types d'ip actuelles",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop('instances', None)
|
||||
super(DelIpTypeForm, self).__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields['iptypes'].queryset = instances
|
||||
else:
|
||||
self.fields['iptypes'].queryset = IpType.objects.all()
|
||||
|
||||
|
||||
class ExtensionForm(ModelForm):
|
||||
"""Formulaire d'ajout et edition d'une extension"""
|
||||
|
@ -288,11 +261,30 @@ class ExtensionForm(ModelForm):
|
|||
class DelExtensionForm(Form):
|
||||
"""Suppression d'une ou plusieurs extensions"""
|
||||
extensions = forms.ModelMultipleChoiceField(
|
||||
queryset=Extension.objects.all(),
|
||||
queryset=Extension.objects.none(),
|
||||
label="Extensions actuelles",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop('instances', None)
|
||||
super(DelExtensionForm, self).__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields['extensions'].queryset = instances
|
||||
else:
|
||||
self.fields['extensions'].queryset = Extension.objects.all()
|
||||
|
||||
|
||||
class Ipv6ListForm(FieldPermissionFormMixin, ModelForm):
|
||||
"""Gestion des ipv6 d'une machine"""
|
||||
class Meta:
|
||||
model = Ipv6List
|
||||
fields = ['ipv6', 'slaac_ip']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(Ipv6ListForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class SOAForm(ModelForm):
|
||||
"""Ajout et edition d'un SOA"""
|
||||
|
@ -308,11 +300,19 @@ class SOAForm(ModelForm):
|
|||
class DelSOAForm(Form):
|
||||
"""Suppression d'un ou plusieurs SOA"""
|
||||
soa = forms.ModelMultipleChoiceField(
|
||||
queryset=SOA.objects.all(),
|
||||
queryset=SOA.objects.none(),
|
||||
label="SOA actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop('instances', None)
|
||||
super(DelSOAForm, self).__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields['soa'].queryset = instances
|
||||
else:
|
||||
self.fields['soa'].queryset = SOA.objects.all()
|
||||
|
||||
|
||||
class MxForm(ModelForm):
|
||||
"""Ajout et edition d'un MX"""
|
||||
|
@ -327,15 +327,22 @@ class MxForm(ModelForm):
|
|||
interface_parent=None
|
||||
).select_related('extension')
|
||||
|
||||
|
||||
class DelMxForm(Form):
|
||||
"""Suppression d'un ou plusieurs MX"""
|
||||
mx = forms.ModelMultipleChoiceField(
|
||||
queryset=Mx.objects.all(),
|
||||
queryset=Mx.objects.none(),
|
||||
label="MX actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop('instances', None)
|
||||
super(DelMxForm, self).__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields['mx'].queryset = instances
|
||||
else:
|
||||
self.fields['mx'].queryset = Mx.objects.all()
|
||||
|
||||
|
||||
class NsForm(ModelForm):
|
||||
"""Ajout d'un NS pour une zone
|
||||
|
@ -356,11 +363,19 @@ class NsForm(ModelForm):
|
|||
class DelNsForm(Form):
|
||||
"""Suppresion d'un ou plusieurs NS"""
|
||||
ns = forms.ModelMultipleChoiceField(
|
||||
queryset=Ns.objects.all(),
|
||||
queryset=Ns.objects.none(),
|
||||
label="Enregistrements NS actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop('instances', None)
|
||||
super(DelNsForm, self).__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields['ns'].queryset = instances
|
||||
else:
|
||||
self.fields['ns'].queryset = Ns.objects.all()
|
||||
|
||||
|
||||
class TxtForm(ModelForm):
|
||||
"""Ajout d'un txt pour une zone"""
|
||||
|
@ -376,11 +391,19 @@ class TxtForm(ModelForm):
|
|||
class DelTxtForm(Form):
|
||||
"""Suppression d'un ou plusieurs TXT"""
|
||||
txt = forms.ModelMultipleChoiceField(
|
||||
queryset=Txt.objects.all(),
|
||||
queryset=Txt.objects.none(),
|
||||
label="Enregistrements Txt actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop('instances', None)
|
||||
super(DelTxtForm, self).__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields['txt'].queryset = instances
|
||||
else:
|
||||
self.fields['txt'].queryset = Txt.objects.all()
|
||||
|
||||
|
||||
class SrvForm(ModelForm):
|
||||
"""Ajout d'un srv pour une zone"""
|
||||
|
@ -396,11 +419,19 @@ class SrvForm(ModelForm):
|
|||
class DelSrvForm(Form):
|
||||
"""Suppression d'un ou plusieurs Srv"""
|
||||
srv = forms.ModelMultipleChoiceField(
|
||||
queryset=Srv.objects.all(),
|
||||
queryset=Srv.objects.none(),
|
||||
label="Enregistrements Srv actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop('instances', None)
|
||||
super(DelSrvForm, self).__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields['srv'].queryset = instances
|
||||
else:
|
||||
self.fields['srv'].queryset = Srv.objects.all()
|
||||
|
||||
|
||||
class NasForm(ModelForm):
|
||||
"""Ajout d'un type de nas (machine d'authentification,
|
||||
|
@ -417,11 +448,19 @@ class NasForm(ModelForm):
|
|||
class DelNasForm(Form):
|
||||
"""Suppression d'un ou plusieurs nas"""
|
||||
nas = forms.ModelMultipleChoiceField(
|
||||
queryset=Nas.objects.all(),
|
||||
queryset=Nas.objects.none(),
|
||||
label="Enregistrements Nas actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop('instances', None)
|
||||
super(DelNasForm, self).__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields['nas'].queryset = instances
|
||||
else:
|
||||
self.fields['nas'].queryset = Nas.objects.all()
|
||||
|
||||
|
||||
class ServiceForm(ModelForm):
|
||||
"""Ajout et edition d'une classe de service : dns, dhcp, etc"""
|
||||
|
@ -446,11 +485,19 @@ class ServiceForm(ModelForm):
|
|||
class DelServiceForm(Form):
|
||||
"""Suppression d'un ou plusieurs service"""
|
||||
service = forms.ModelMultipleChoiceField(
|
||||
queryset=Service.objects.all(),
|
||||
queryset=Service.objects.none(),
|
||||
label="Services actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop('instances', None)
|
||||
super(DelServiceForm, self).__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields['service'].queryset = instances
|
||||
else:
|
||||
self.fields['service'].queryset = Service.objects.all()
|
||||
|
||||
|
||||
class VlanForm(ModelForm):
|
||||
"""Ajout d'un vlan : id, nom"""
|
||||
|
@ -466,11 +513,19 @@ class VlanForm(ModelForm):
|
|||
class DelVlanForm(Form):
|
||||
"""Suppression d'un ou plusieurs vlans"""
|
||||
vlan = forms.ModelMultipleChoiceField(
|
||||
queryset=Vlan.objects.all(),
|
||||
queryset=Vlan.objects.none(),
|
||||
label="Vlan actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop('instances', None)
|
||||
super(DelVlanForm, self).__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields['vlan'].queryset = instances
|
||||
else:
|
||||
self.fields['vlan'].queryset = Vlan.objects.all()
|
||||
|
||||
|
||||
class EditOuverturePortConfigForm(ModelForm):
|
||||
"""Edition de la liste des profils d'ouverture de ports
|
||||
|
|
79
machines/migrations/0070_auto_20171231_1947.py
Normal file
79
machines/migrations/0070_auto_20171231_1947.py
Normal file
|
@ -0,0 +1,79 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-12-31 18:47
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('machines', '0069_auto_20171116_0822'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='domain',
|
||||
options={'permissions': (('view_domain', 'Peut voir un objet domain'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='extension',
|
||||
options={'permissions': (('view_extension', 'Peut voir un objet extension'), ('use_all_extension', 'Peut utiliser toutes les extension'))},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='interface',
|
||||
options={'permissions': (('view_interface', 'Peut voir un objet interface'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='iplist',
|
||||
options={'permissions': (('view_iplist', 'Peut voir un objet iplist'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='iptype',
|
||||
options={'permissions': (('view_iptype', 'Peut voir un objet iptype'), ('use_all_iptype', 'Peut utiliser tous les iptype'))},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='machine',
|
||||
options={'permissions': (('view_machine', 'Peut voir un objet machine quelquonque'), ('change_machine_user', "Peut changer le propriétaire d'une machine"))},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='machinetype',
|
||||
options={'permissions': (('view_machinetype', 'Peut voir un objet machinetype'), ('use_all_machinetype', "Peut utiliser n'importe quel type de machine"))},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='mx',
|
||||
options={'permissions': (('view_mx', 'Peut voir un objet mx'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='nas',
|
||||
options={'permissions': (('view_nas', 'Peut voir un objet Nas'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='ns',
|
||||
options={'permissions': (('view_nx', 'Peut voir un objet nx'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='ouvertureportlist',
|
||||
options={'permissions': (('view_ouvertureportlist', 'Peut voir un objet ouvertureport'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='service',
|
||||
options={'permissions': (('view_service', 'Peut voir un objet service'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='soa',
|
||||
options={'permissions': (('view_soa', 'Peut voir un objet soa'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='srv',
|
||||
options={'permissions': (('view_soa', 'Peut voir un objet soa'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='txt',
|
||||
options={'permissions': (('view_txt', 'Peut voir un objet txt'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='vlan',
|
||||
options={'permissions': (('view_vlan', 'Peut voir un objet vlan'),)},
|
||||
),
|
||||
]
|
19
machines/migrations/0071_auto_20171231_2100.py
Normal file
19
machines/migrations/0071_auto_20171231_2100.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-12-31 20:00
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('machines', '0070_auto_20171231_1947'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='ns',
|
||||
options={'permissions': (('view_ns', 'Peut voir un objet ns'),)},
|
||||
),
|
||||
]
|
19
machines/migrations/0072_auto_20180108_1822.py
Normal file
19
machines/migrations/0072_auto_20180108_1822.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-08 17:22
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('machines', '0071_auto_20171231_2100'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='interface',
|
||||
options={'permissions': (('view_interface', 'Peut voir un objet interface'), ('change_interface_machine', "Peut changer le propriétaire d'une interface"))},
|
||||
),
|
||||
]
|
29
machines/migrations/0073_auto_20180128_2203.py
Normal file
29
machines/migrations/0073_auto_20180128_2203.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-28 21:03
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('machines', '0072_auto_20180108_1822'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Ipv6List',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('ipv6', models.GenericIPAddressField(protocol='IPv6', unique=True)),
|
||||
('slaac_ip', models.BooleanField(default=False)),
|
||||
('interface', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='machines.Interface')),
|
||||
],
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='ipv6list',
|
||||
unique_together=set([('interface', 'slaac_ip')]),
|
||||
),
|
||||
]
|
19
machines/migrations/0074_auto_20180129_0352.py
Normal file
19
machines/migrations/0074_auto_20180129_0352.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-29 02:52
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('machines', '0073_auto_20180128_2203'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='ipv6list',
|
||||
options={'permissions': (('view_ipv6list', 'Peut voir un objet ipv6'), ('change_ipv6list_slaac_ip', 'Peut changer la valeur slaac sur une ipv6'))},
|
||||
),
|
||||
]
|
19
machines/migrations/0075_auto_20180130_0052.py
Normal file
19
machines/migrations/0075_auto_20180130_0052.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-29 23:52
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('machines', '0074_auto_20180129_0352'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='ipv6list',
|
||||
unique_together=set([]),
|
||||
),
|
||||
]
|
21
machines/migrations/0076_auto_20180130_1623.py
Normal file
21
machines/migrations/0076_auto_20180130_1623.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-30 15:23
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('machines', '0075_auto_20180130_0052'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='ipv6list',
|
||||
name='interface',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ipv6list', to='machines.Interface'),
|
||||
),
|
||||
]
|
1337
machines/models.py
1337
machines/models.py
File diff suppressed because it is too large
Load diff
|
@ -37,7 +37,8 @@ from machines.models import (
|
|||
Service_link,
|
||||
Ns,
|
||||
OuverturePortList,
|
||||
OuverturePort
|
||||
OuverturePort,
|
||||
Ipv6List
|
||||
)
|
||||
|
||||
|
||||
|
@ -57,6 +58,12 @@ class IpListSerializer(serializers.ModelSerializer):
|
|||
fields = ('ipv4', 'ip_type')
|
||||
|
||||
|
||||
class Ipv6ListSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Ipv6List
|
||||
fields = ('ipv6', 'slaac_ip')
|
||||
|
||||
|
||||
class InterfaceSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation d'une interface, ipv4, domain et extension sont
|
||||
des foreign_key, on les override et on les evalue avec des fonctions
|
||||
|
@ -81,8 +88,9 @@ class InterfaceSerializer(serializers.ModelSerializer):
|
|||
|
||||
|
||||
class FullInterfaceSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation complete d'une interface avec l'ipv6 en plus"""
|
||||
"""Serialisation complete d'une interface avec les ipv6 en plus"""
|
||||
ipv4 = IpListSerializer(read_only=True)
|
||||
ipv6 = Ipv6ListSerializer(read_only=True, many=True)
|
||||
mac_address = serializers.SerializerMethodField('get_macaddress')
|
||||
domain = serializers.SerializerMethodField('get_dns')
|
||||
extension = serializers.SerializerMethodField('get_interface_extension')
|
||||
|
@ -124,6 +132,7 @@ class TypeSerializer(serializers.ModelSerializer):
|
|||
class Meta:
|
||||
model = IpType
|
||||
fields = ('type', 'extension', 'domaine_ip_start', 'domaine_ip_stop',
|
||||
'prefix_v6',
|
||||
'ouverture_ports_tcp_in', 'ouverture_ports_tcp_out',
|
||||
'ouverture_ports_udp_in', 'ouverture_ports_udp_out',)
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -33,7 +35,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<tr>
|
||||
<td>{{ alias }}</td>
|
||||
<td class="text-right">
|
||||
{% 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -45,9 +47,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ extension.origin_v6 }}</td>
|
||||
{% endif %}
|
||||
<td class="text-right">
|
||||
{% if is_infra %}
|
||||
{% can_create Extension %}
|
||||
{% include 'buttons/edit.html' with href='machines:edit-extension' id=extension.id %}
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% include 'buttons/history.html' with href='machines:history' name='extension' id=extension.id %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -48,9 +50,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ type.vlan }}</td>
|
||||
<td>{{ type.ouverture_ports }}</td>
|
||||
<td class="text-right">
|
||||
{% if is_infra %}
|
||||
{% can_edit type %}
|
||||
{% include 'buttons/edit.html' with href='machines:edit-iptype' id=type.id %}
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% include 'buttons/history.html' with href='machines:history' name='iptype' id=type.id %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
51
machines/templates/machines/aff_ipv6.html
Normal file
51
machines/templates/machines/aff_ipv6.html
Normal file
|
@ -0,0 +1,51 @@
|
|||
{% comment %}
|
||||
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 © 2017 Gabriel Détraz
|
||||
Copyright © 2017 Goulven Kermarec
|
||||
Copyright © 2017 Augustin Lemesle
|
||||
|
||||
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.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Ipv6</th>
|
||||
<th>Slaac</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for ipv6 in ipv6_list %}
|
||||
<tr>
|
||||
<td>{{ ipv6.ipv6 }}</td>
|
||||
<td>{{ ipv6.slaac_ip }}</td>
|
||||
<td class="text-right">
|
||||
{% can_edit ipv6 %}
|
||||
{% include 'buttons/edit.html' with href='machines:edit-ipv6list' id=ipv6.id %}
|
||||
{% acl_end %}
|
||||
{% can_delete ipv6 %}
|
||||
{% include 'buttons/suppr.html' with href='machines:del-ipv6list' id=ipv6.id %}
|
||||
{% acl_end %}
|
||||
{% include 'buttons/history.html' with href='machines:history' name='ipv6list' id=ipv6.id %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
|
@ -22,11 +22,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
{% if machines_list.paginator %}
|
||||
{% include "pagination.html" with list=machines_list %}
|
||||
{% endif %}
|
||||
|
||||
<table class="table">
|
||||
<table class="table" id="machines_table">
|
||||
<colgroup>
|
||||
<col>
|
||||
<col>
|
||||
|
@ -44,36 +46,29 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% for machine in machines_list %}
|
||||
<tr class="info">
|
||||
<td colspan="4">
|
||||
<b>{{ machine.name|default:'<i>Pas de nom</i>' }}</b> <i class="glyphicon glyphicon-chevron-right"></i>
|
||||
<b>{{ machine.name|default:'<i>Pas de nom</i>' }}</b> <i class="fa-angle-right"></i>
|
||||
<a href="{% url 'users:profil' userid=machine.user.id %}" title="Voir le profil">
|
||||
<i class="glyphicon glyphicon-user"></i> {{ machine.user }}
|
||||
<i class="fa fa-user"></i> {{ machine.user }}
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{% can_create Interface machine.id %}
|
||||
{% include 'buttons/add.html' with href='machines:new-interface' id=machine.id desc='Ajouter une interface' %}
|
||||
{% acl_end %}
|
||||
{% include 'buttons/history.html' with href='machines:history' name='machine' id=machine.id %}
|
||||
{% can_delete machine %}
|
||||
{% include 'buttons/suppr.html' with href='machines:del-machine' id=machine.id %}
|
||||
{% acl_end %}
|
||||
</td>
|
||||
</tr>
|
||||
{% for interface in machine.interface_set.all %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if interface.domain.related_domain.all %}
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-default dropdown-toggle" type="button" id="editioninterface" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
{{ interface.domain }} <span class="caret"></span>
|
||||
{{ interface.domain }}
|
||||
<button class="btn btn-default btn-xs" type="button" data-toggle="collapse" data-target="#collapseDomain_{{interface.id}}" aria-expanded="true" aria-controls="collapseDomain_{{interface.id}}">
|
||||
Afficher les alias
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="editioninterface">
|
||||
{% for al in interface.domain.related_domain.all %}
|
||||
<li>
|
||||
<a href="http://{{ al }}">
|
||||
{{ al }}
|
||||
<i class="glyphicon glyphicon-share-alt"></i>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ interface.domain }}
|
||||
{% endif %}
|
||||
|
@ -88,36 +83,90 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<b>IPv4</b> {{ interface.ipv4 }}
|
||||
<br>
|
||||
{% if ipv6_enabled and interface.ipv6 != 'None'%}
|
||||
<b>IPv6</b> {{ interface.ipv6 }}
|
||||
<b>IPv6</b>
|
||||
<button class="btn btn-default btn-xs" type="button" data-toggle="collapse" data-target="#collapseIpv6_{{interface.id}}" aria-expanded="true" aria-controls="collapseIpv6_{{interface.id}}">
|
||||
Afficher l'IPV6
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<div class="dropdown" style="width: 128px;">
|
||||
<button class="btn btn-primary btn-sm dropdown-toggle" type="button" id="editioninterface" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
<i class="glyphicon glyphicon-edit"></i> <span class="caret"></span>
|
||||
<i class="fa fa-edit"></i> <span class="caret"></span>
|
||||
</button>
|
||||
{% include 'buttons/history.html' with href='machines:history' name='interface' id=interface.id %}
|
||||
{% can_delete interface %}
|
||||
{% include 'buttons/suppr.html' with href='machines:del-interface' id=interface.id %}
|
||||
{% acl_end %}
|
||||
<ul class="dropdown-menu" aria-labelledby="editioninterface">
|
||||
{% can_edit interface %}
|
||||
<li>
|
||||
<a href="{% url 'machines:edit-interface' interface.id %}">
|
||||
<i class="glyphicon glyphicon-edit"></i> Editer
|
||||
<i class="fa fa-edit"></i> Editer
|
||||
</a>
|
||||
</li>
|
||||
{% acl_end %}
|
||||
{% can_create Domain interface.id %}
|
||||
<li>
|
||||
<a href="{% url 'machines:index-alias' interface.id %}">
|
||||
<i class="glyphicon glyphicon-edit"></i> Gerer les alias
|
||||
<i class="fa fa-edit"></i> Gerer les alias
|
||||
</a>
|
||||
</li>
|
||||
{% acl_end %}
|
||||
{% can_create Ipv6List interface.id %}
|
||||
<li>
|
||||
<a href="{% url 'machines:index-ipv6' interface.id %}">
|
||||
<i class="fa fa-edit"></i> Gerer les ipv6
|
||||
</a>
|
||||
</li>
|
||||
{% acl_end %}
|
||||
{% can_create OuverturePortList %}
|
||||
<li>
|
||||
<a href="{% url 'machines:port-config' interface.id%}">
|
||||
<i class="glyphicon glyphicon-edit"></i> Gerer la configuration des ports
|
||||
<i class="fa fa-edit"></i> Gerer la configuration des ports
|
||||
</a>
|
||||
</li>
|
||||
{% acl_end %}
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{% if ipv6_enabled and interface.ipv6 != 'None'%}
|
||||
<tr>
|
||||
<td colspan=5 style="border-top: none; padding: 1px;">
|
||||
<div class="collapse in" id="collapseIpv6_{{interface.id}}">
|
||||
<ul class="list-group" style="margin-bottom: 0px;">
|
||||
{% for ipv6 in interface.ipv6.all %}
|
||||
<li class="list-group-item col-xs-6 col-sm-6 col-md-6" style="border: none;">
|
||||
{{ipv6}}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
<tr>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if interface.domain.related_domain.all %}
|
||||
<tr>
|
||||
<td colspan=5 style="border-top: none; padding: 1px;">
|
||||
<div class="collapse in" id="collapseDomain_{{interface.id}}">
|
||||
<ul class="list-group" style="margin-bottom: 0px;">
|
||||
{% for al in interface.domain.related_domain.all %}
|
||||
<li class="list-group-item col-xs-6 col-sm-4 col-md-3" style="border: none;">
|
||||
<a href="http://{{ al }}">
|
||||
{{ al }}
|
||||
<i class="fa fa-share"></i>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
<tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td colspan="8"></td>
|
||||
|
@ -126,6 +175,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
$("#machines_table").ready( function() {
|
||||
var alias_div = [{% for machine in machines_list %}{% for interface in machine.interface_set.all %}{% if interface.domain.related_domain.all %}$("#collapseDomain_{{interface.id}}"), {% endif %}{% endfor %}{% endfor %}];
|
||||
for (var i=0 ; i<alias_div.length ; i++) {
|
||||
alias_div[i].collapse('hide');
|
||||
}
|
||||
} );
|
||||
$("#machines_table").ready( function() {
|
||||
var ipv6_div = [{% for machine in machines_list %}{% for interface in machine.interface_set.all %}{% if interface.ipv6.all %}$("#collapseIpv6_{{interface.id}}"), {% endif %}{% endfor %}{% endfor %}];
|
||||
for (var i=0 ; i<ipv6_div.length ; i++) {
|
||||
ipv6_div[i].collapse('hide');
|
||||
}
|
||||
} );
|
||||
</script>
|
||||
|
||||
{% if machines_list.paginator %}
|
||||
{% include "pagination.html" with list=machines_list %}
|
||||
{% endif %}
|
||||
|
|
|
@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -35,9 +37,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ type.type }}</td>
|
||||
<td>{{ type.ip_type }}</td>
|
||||
<td class="text-right">
|
||||
{% if is_infra %}
|
||||
{% can_edit type %}
|
||||
{% include 'buttons/edit.html' with href='machines:edit-machinetype' id=type.id %}
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% include 'buttons/history.html' with href='machines:history' name='machinetype' id=type.id %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -38,9 +40,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ mx.priority }}</td>
|
||||
<td>{{ mx.name }}</td>
|
||||
<td class="text-right">
|
||||
{% if is_infra %}
|
||||
{% can_edit mx %}
|
||||
{% include 'buttons/edit.html' with href='machines:edit-mx' id=mx.id %}
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% include 'buttons/history.html' with href='machines:history' name='mx' id=mx.id %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -41,9 +43,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ nas.port_access_mode }}</td>
|
||||
<td>{{ nas.autocapture_mac }}</td>
|
||||
<td class="text-right">
|
||||
{% if is_infra %}
|
||||
{% can_edit nas %}
|
||||
{% include 'buttons/edit.html' with href='machines:edit-nas' id=nas.id %}
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% include 'buttons/history.html' with href='machines:history' name='nas' id=nas.id %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -36,9 +38,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ ns.zone }}</td>
|
||||
<td>{{ ns.ns }}</td>
|
||||
<td class="text-right">
|
||||
{% if is_infra %}
|
||||
{% can_edit ns %}
|
||||
{% include 'buttons/edit.html' with href='machines:edit-ns' id=ns.id %}
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% include 'buttons/history.html' with href='machines:history' name='ns' id=ns.id %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -40,9 +42,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ service.regular_time_regen }}</td>
|
||||
<td>{% for serv in service.servers.all %}{{ serv }}, {% endfor %}</td>
|
||||
<td class="text-right">
|
||||
{% if is_infra %}
|
||||
{% can_edit service %}
|
||||
{% include 'buttons/edit.html' with href='machines:edit-service' id=service.id %}
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% include 'buttons/history.html' with href='machines:history' name='service' id=service.id %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -44,9 +46,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ soa.expire }}</td>
|
||||
<td>{{ soa.ttl }}</td>
|
||||
<td class="text-right">
|
||||
{% if is_infra %}
|
||||
{% can_edit soa %}
|
||||
{% include 'buttons/edit.html' with href='machines:edit-soa' id=soa.id %}
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% include 'buttons/history.html' with href='machines:history' name='soa' id=soa.id %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -48,9 +50,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ srv.port }}</td>
|
||||
<td>{{ srv.target }}</td>
|
||||
<td class="text-right">
|
||||
{% if is_infra %}
|
||||
{% can_edit srv %}
|
||||
{% include 'buttons/edit.html' with href='machines:edit-srv' id=srv.id %}
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% include 'buttons/history.html' with href='machines:history' name='srv' id=srv.id %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -36,9 +38,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ txt.zone }}</td>
|
||||
<td>{{ txt.dns_entry }}</td>
|
||||
<td class="text-right">
|
||||
{% if is_infra %}
|
||||
{% can_edit txt %}
|
||||
{% include 'buttons/edit.html' with href='machines:edit-txt' id=txt.id %}
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% include 'buttons/history.html' with href='machines:history' name='txt' id=txt.id %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -39,9 +41,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ vlan.comment }}</td>
|
||||
<td>{% for range in vlan.iptype_set.all %}{{ range }}, {% endfor%}</td>
|
||||
<td class="text-right">
|
||||
{% if is_infra %}
|
||||
{% can_create Vlan %}
|
||||
{% include 'buttons/edit.html' with href='machines:edit-vlan' id=vlan.id %}
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% include 'buttons/history.html' with href='machines:history' name='vlan' id=vlan.id %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -29,8 +29,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
{% block content %}
|
||||
<h2>Liste des alias de l'interface</h2>
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-alias' interface_id %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un alias</a>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-alias' interface_id %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs alias</a>
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-alias' interface_id %}"><i class="fa fa-plus"></i> Ajouter un alias</a>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-alias' interface_id %}"><i class="fa fa-trash"></i> Supprimer un ou plusieurs alias</a>
|
||||
{% include "machines/aff_alias.html" with alias_list=alias_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -25,45 +25,47 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
{% block title %}Machines{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Liste des extensions</h2>
|
||||
{% if is_infra %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-extension' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter une extension</a>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-extension' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer une ou plusieurs extensions</a>
|
||||
{% endif %}
|
||||
{% can_create Extension %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-extension' %}"><i class="fa fa-plus"></i> Ajouter une extension</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-extension' %}"><i class="fa fa-trash"></i> Supprimer une ou plusieurs extensions</a>
|
||||
{% include "machines/aff_extension.html" with extension_list=extension_list %}
|
||||
|
||||
<h2>Liste des enregistrements SOA</h2>
|
||||
{% if is_infra %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-soa' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un enregistrement SOA</a>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-soa' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un enregistrement SOA</a>
|
||||
{% endif %}
|
||||
{% can_create SOA %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-soa' %}"><i class="fa fa-plus"></i> Ajouter un enregistrement SOA</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-soa' %}"><i class="fa fa-trash"></i> Supprimer un enregistrement SOA</a>
|
||||
{% include "machines/aff_soa.html" with soa_list=soa_list %}
|
||||
<h2>Liste des enregistrements MX</h2>
|
||||
{% if is_infra %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-mx' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un enregistrement MX</a>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-mx' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un enregistrement MX</a>
|
||||
{% endif %}
|
||||
{% can_create Mx %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-mx' %}"><i class="fa fa-plus"></i> Ajouter un enregistrement MX</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-mx' %}"><i class="fa fa-trash"></i> Supprimer un enregistrement MX</a>
|
||||
{% include "machines/aff_mx.html" with mx_list=mx_list %}
|
||||
<h2>Liste des enregistrements NS</h2>
|
||||
{% if is_infra %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-ns' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un enregistrement NS</a>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-ns' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un enregistrement NS</a>
|
||||
{% endif %}
|
||||
{% can_create Ns %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-ns' %}"><i class="fa fa-plus"></i> Ajouter un enregistrement NS</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-ns' %}"><i class="fa fa-trash"></i> Supprimer un enregistrement NS</a>
|
||||
{% include "machines/aff_ns.html" with ns_list=ns_list %}
|
||||
<h2>Liste des enregistrements TXT</h2>
|
||||
{% if is_infra %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-txt' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un enregistrement TXT</a>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-txt' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un enregistrement TXT</a>
|
||||
{% endif %}
|
||||
{% can_create Txt %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-txt' %}"><i class="fa fa-plus"></i> Ajouter un enregistrement TXT</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-txt' %}"><i class="fa fa-trash"></i> Supprimer un enregistrement TXT</a>
|
||||
{% include "machines/aff_txt.html" with txt_list=txt_list %}
|
||||
<h2>Liste des enregistrements SRV</h2>
|
||||
{% if is_infra %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-srv' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un enregistrement SRV</a>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-srv' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un enregistrement SRV</a>
|
||||
{% endif %}
|
||||
{% can_create Srv %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-srv' %}"><i class="fa fa-plus"></i> Ajouter un enregistrement SRV</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-srv' %}"><i class="fa fa-trash"></i> Supprimer un enregistrement SRV</a>
|
||||
{% include "machines/aff_srv.html" with srv_list=srv_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -25,14 +25,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
{% block title %}Ip{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Liste des types d'ip</h2>
|
||||
{% if is_infra %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-iptype' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un type d'ip</a>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-iptype' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs types d'ip</a>
|
||||
{% endif %}
|
||||
{% can_create IpType %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-iptype' %}"><i class="fa fa-plus"></i> Ajouter un type d'ip</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-iptype' %}"><i class="fa fa-trash"></i> Supprimer un ou plusieurs types d'ip</a>
|
||||
{% include "machines/aff_iptype.html" with iptype_list=iptype_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
41
machines/templates/machines/index_ipv6.html
Normal file
41
machines/templates/machines/index_ipv6.html
Normal file
|
@ -0,0 +1,41 @@
|
|||
{% extends "machines/sidebar.html" %}
|
||||
{% comment %}
|
||||
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 © 2017 Gabriel Détraz
|
||||
Copyright © 2017 Goulven Kermarec
|
||||
Copyright © 2017 Augustin Lemesle
|
||||
|
||||
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.
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
{% load acl %}
|
||||
|
||||
{% block title %}Machines{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Liste des ipv6 de l'interface</h2>
|
||||
{% can_create Ipv6List interface_id %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:new-ipv6list' interface_id %}"><i class="fa fa-plus"></i> Ajouter une ipv6</a>
|
||||
{% acl_end %}
|
||||
{% include "machines/aff_ipv6.html" with ipv6_list=ipv6_list %}
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
{% endblock %}
|
||||
|
|
@ -25,14 +25,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
{% block title %}Machines{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Liste des types de machines</h2>
|
||||
{% if is_infra %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-machinetype' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un type de machine</a>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-machinetype' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs types de machines</a>
|
||||
{% endif %}
|
||||
{% can_create MachineType %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-machinetype' %}"><i class="fa fa-plus"></i> Ajouter un type de machine</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-machinetype' %}"><i class="fa fa-trash"></i> Supprimer un ou plusieurs types de machines</a>
|
||||
{% include "machines/aff_machinetype.html" with machinetype_list=machinetype_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -25,16 +25,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
{% block title %}Machines{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Liste des nas</h2>
|
||||
<h5>La correpondance nas-machinetype relie le type de nas à un type de machine.
|
||||
Elle est utile pour l'autoenregistrement des macs par radius, et permet de choisir le type de machine à affecter aux machines en fonction du type de nas</h5>
|
||||
{% if is_infra %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-nas' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un type de nas</a>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-nas' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs types nas</a>
|
||||
{% endif %}
|
||||
{% can_create Nas %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-nas' %}"><i class="fa fa-plus"></i> Ajouter un type de nas</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-nas' %}"><i class="fa fa-trash"></i> Supprimer un ou plusieurs types nas</a>
|
||||
{% include "machines/aff_nas.html" with nas_list=nas_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -2,11 +2,15 @@
|
|||
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
{% block title %}Configuration de ports{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Liste des configurations de ports</h2>
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-portlist' %}"><i class="glyphicon glyphicon-plus"></i>Ajouter une configuration</a>
|
||||
{% can_create OuverturePortList %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-portlist' %}"><i class="fa fa-plus"></i>Ajouter une configuration</a>
|
||||
{% acl_end %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -44,8 +48,12 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
<td class="text-right">
|
||||
{% can_delete pl %}
|
||||
{% include 'buttons/suppr.html' with href='machines:del-portlist' id=pl.id %}
|
||||
{% acl_end %}
|
||||
{% can_edit pl %}
|
||||
{% include 'buttons/edit.html' with href='machines:edit-portlist' id=pl.id %}
|
||||
{% acl_end %}
|
||||
</td>
|
||||
</tr>
|
||||
{%endfor%}
|
||||
|
|
|
@ -24,15 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endcomment %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
{% load acl %}
|
||||
|
||||
{% block title %}Machines{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Liste des services</h2>
|
||||
{% if is_infra %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-service' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un service</a>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-service' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs service</a>
|
||||
{% endif %}
|
||||
{% can_create machines.Service %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-service' %}"><i class="fa fa-plus"></i> Ajouter un service</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-service' %}"><i class="fa fa-trash"></i> Supprimer un ou plusieurs service</a>
|
||||
{% include "machines/aff_service.html" with service_list=service_list %}
|
||||
<h2>Etat des serveurs</h2>
|
||||
{% include "machines/aff_servers.html" with servers_list=servers_list %}
|
||||
|
|
|
@ -25,14 +25,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
{% block title %}Machines{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Liste des vlans</h2>
|
||||
{% if is_infra %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-vlan' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un vlan</a>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-vlan' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs vlan</a>
|
||||
{% endif %}
|
||||
{% can_create Vlan %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-vlan' %}"><i class="fa fa-plus"></i> Ajouter un vlan</a>
|
||||
{% acl_end %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-vlan' %}"><i class="fa fa-trash"></i> Supprimer un ou plusieurs vlan</a>
|
||||
{% include "machines/aff_vlan.html" with vlan_list=vlan_list %}
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -72,12 +72,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% if nasform %}
|
||||
{% bootstrap_form_errors nasform %}
|
||||
{% endif %}
|
||||
{% if ipv6form %}
|
||||
{% bootstrap_form_errors ipv6form %}
|
||||
{% endif %}
|
||||
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% if machineform %}
|
||||
<h3>Machine</h3>
|
||||
{% bootstrap_form machineform %}
|
||||
{% massive_bootstrap_form machineform 'user' %}
|
||||
{% endif %}
|
||||
{% if interfaceform %}
|
||||
<h3>Interface</h3>
|
||||
|
@ -139,7 +142,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<h3>NAS</h3>
|
||||
{% bootstrap_form nasform %}
|
||||
{% endif %}
|
||||
{% bootstrap_button "Créer ou modifier" button_type="submit" icon="star" %}
|
||||
{% if ipv6form %}
|
||||
<h3>Ipv6</h3>
|
||||
{% bootstrap_form ipv6form %}
|
||||
{% endif %}
|
||||
{% bootstrap_button action_name button_type="submit" icon="star" %}
|
||||
</form>
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -23,42 +23,55 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
{% block sidebar %}
|
||||
{% if is_cableur %}
|
||||
{% can_view_all Machine %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "machines:index" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
<i class="fa fa-list-ul"></i>
|
||||
Machines
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all MachineType %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "machines:index-machinetype" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
<i class="fa fa-list-ul"></i>
|
||||
Types de machines
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all Extension %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "machines:index-extension" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
<i class="fa fa-list-ul"></i>
|
||||
Extensions et zones
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all IpType %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "machines:index-iptype" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
<i class="fa fa-list-ul"></i>
|
||||
Plages d'IP
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all Vlan %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "machines:index-vlan" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
<i class="fa fa-list-ul"></i>
|
||||
Vlans
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all Nas %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "machines:index-nas" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
<i class="fa fa-list-ul"></i>
|
||||
Gestion des nas
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all machines.Service %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "machines:index-service" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
<i class="fa fa-list-ul"></i>
|
||||
Services (dhcp, dns...)
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if is_cableur %}
|
||||
{% acl_end %}
|
||||
{% can_view_all OuverturePortList %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "machines:index-portlist" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
<i class="fa fa-list-ul"></i>
|
||||
Ouverture de ports
|
||||
</a>
|
||||
{%endif%}
|
||||
{% acl_end %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
import re2o
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
|
@ -61,9 +61,13 @@ urlpatterns = [
|
|||
url(r'^del_srv/$', views.del_srv, name='del-srv'),
|
||||
url(r'^index_extension/$', views.index_extension, name='index-extension'),
|
||||
url(r'^add_alias/(?P<interfaceid>[0-9]+)$', views.add_alias, name='add-alias'),
|
||||
url(r'^edit_alias/(?P<aliasid>[0-9]+)$', views.edit_alias, name='edit-alias'),
|
||||
url(r'^edit_alias/(?P<domainid>[0-9]+)$', views.edit_alias, name='edit-alias'),
|
||||
url(r'^del_alias/(?P<interfaceid>[0-9]+)$', views.del_alias, name='del-alias'),
|
||||
url(r'^index_alias/(?P<interfaceid>[0-9]+)$', views.index_alias, name='index-alias'),
|
||||
url(r'^new_ipv6list/(?P<interfaceid>[0-9]+)$', views.new_ipv6list, name='new-ipv6list'),
|
||||
url(r'^edit_ipv6list/(?P<ipv6listid>[0-9]+)$', views.edit_ipv6list, name='edit-ipv6list'),
|
||||
url(r'^del_ipv6list/(?P<ipv6listid>[0-9]+)$', views.del_ipv6list, name='del-ipv6list'),
|
||||
url(r'^index_ipv6/(?P<interfaceid>[0-9]+)$', views.index_ipv6, name='index-ipv6'),
|
||||
url(r'^add_service/$', views.add_service, name='add-service'),
|
||||
url(r'^edit_service/(?P<serviceid>[0-9]+)$', views.edit_service, name='edit-service'),
|
||||
url(r'^del_service/$', views.del_service, name='del-service'),
|
||||
|
@ -76,20 +80,12 @@ urlpatterns = [
|
|||
url(r'^edit_nas/(?P<nasid>[0-9]+)$', views.edit_nas, name='edit-nas'),
|
||||
url(r'^del_nas/$', views.del_nas, name='del-nas'),
|
||||
url(r'^index_nas/$', views.index_nas, name='index-nas'),
|
||||
url(r'^history/(?P<object>machine)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>interface)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>machinetype)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>extension)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>soa)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>mx)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>ns)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>txt)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>srv)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>iptype)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>alias)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>vlan)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>nas)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>service)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(
|
||||
r'history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
|
||||
re2o.views.history,
|
||||
name='history',
|
||||
kwargs={'application':'machines'},
|
||||
),
|
||||
url(r'^$', views.index, name='index'),
|
||||
url(r'^rest/mac-ip/$', views.mac_ip, name='mac-ip'),
|
||||
url(r'^rest/regen-achieved/$', views.regen_achieved, name='regen-achieved'),
|
||||
|
@ -104,9 +100,9 @@ urlpatterns = [
|
|||
url(r'^rest/service_servers/$', views.service_servers, name='service-servers'),
|
||||
url(r'^rest/ouverture_ports/$', views.ouverture_ports, name='ouverture-ports'),
|
||||
url(r'index_portlist/$', views.index_portlist, name='index-portlist'),
|
||||
url(r'^edit_portlist/(?P<pk>[0-9]+)$', views.edit_portlist, name='edit-portlist'),
|
||||
url(r'^del_portlist/(?P<pk>[0-9]+)$', views.del_portlist, name='del-portlist'),
|
||||
url(r'^edit_portlist/(?P<ouvertureportlistid>[0-9]+)$', views.edit_portlist, name='edit-portlist'),
|
||||
url(r'^del_portlist/(?P<ouvertureportlistid>[0-9]+)$', views.del_portlist, name='del-portlist'),
|
||||
url(r'^add_portlist/$', views.add_portlist, name='add-portlist'),
|
||||
url(r'^port_config/(?P<pk>[0-9]+)$', views.configure_ports, name='port-config'),
|
||||
url(r'^port_config/(?P<interfaceid>[0-9]+)$', views.configure_ports, name='port-config'),
|
||||
|
||||
]
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,2 @@
|
|||
|
||||
from .acl import *
|
40
preferences/acl.py
Normal file
40
preferences/acl.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
# -*- 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 © 2017 Gabriel Détraz
|
||||
# Copyright © 2017 Goulven Kermarec
|
||||
# Copyright © 2017 Augustin Lemesle
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""preferences.acl
|
||||
|
||||
Here are defined some functions to check acl on the application.
|
||||
"""
|
||||
|
||||
def can_view(user):
|
||||
"""Check if an user can view the application.
|
||||
|
||||
Args:
|
||||
user: The user who wants to view the application.
|
||||
|
||||
Returns:
|
||||
A couple (allowed, msg) where allowed is a boolean which is True if
|
||||
viewing is granted and msg is a message (can be None).
|
||||
"""
|
||||
can = user.has_module_perms('preferences')
|
||||
return can, None if can else "Vous ne pouvez pas voir cette application."
|
56
preferences/aes_field.py
Normal file
56
preferences/aes_field.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
import string
|
||||
import binascii
|
||||
from random import choice
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
|
||||
EOD = '`%EofD%`' # This should be something that will not occur in strings
|
||||
|
||||
|
||||
def genstring(length=16, chars=string.printable):
|
||||
return ''.join([choice(chars) for i in range(length)])
|
||||
|
||||
|
||||
def encrypt(key, s):
|
||||
obj = AES.new(key)
|
||||
datalength = len(s) + len(EOD)
|
||||
if datalength < 16:
|
||||
saltlength = 16 - datalength
|
||||
else:
|
||||
saltlength = 16 - datalength % 16
|
||||
ss = ''.join([s, EOD, genstring(saltlength)])
|
||||
return obj.encrypt(ss)
|
||||
|
||||
|
||||
def decrypt(key, s):
|
||||
obj = AES.new(key)
|
||||
ss = obj.decrypt(s)
|
||||
return ss.split(bytes(EOD, 'utf-8'))[0]
|
||||
|
||||
|
||||
class AESEncryptedField(models.CharField):
|
||||
def save_form_data(self, instance, data):
|
||||
setattr(instance, self.name,
|
||||
binascii.b2a_base64(encrypt(settings.AES_KEY, data)))
|
||||
|
||||
def to_python(self, value):
|
||||
if value is None:
|
||||
return None
|
||||
return decrypt(settings.AES_KEY,
|
||||
binascii.a2b_base64(value)).decode('utf-8')
|
||||
|
||||
def from_db_value(self, value, expression, connection, *args):
|
||||
if value is None:
|
||||
return value
|
||||
return decrypt(settings.AES_KEY,
|
||||
binascii.a2b_base64(value)).decode('utf-8')
|
||||
|
||||
def get_prep_value(self, value):
|
||||
if value is None:
|
||||
return value
|
||||
return binascii.b2a_base64(encrypt(
|
||||
settings.AES_KEY,
|
||||
value
|
||||
))
|
|
@ -48,6 +48,9 @@ class EditOptionalUserForm(ModelForm):
|
|||
téléphone'
|
||||
self.fields['user_solde'].label = 'Activation du solde pour\
|
||||
les utilisateurs'
|
||||
self.fields['max_solde'].label = 'Solde maximum'
|
||||
self.fields['min_online_payment'].label = 'Montant de rechargement minimum en ligne'
|
||||
self.fields['self_adhesion'].label = 'Auto inscription'
|
||||
|
||||
|
||||
class EditOptionalMachineForm(ModelForm):
|
||||
|
@ -114,6 +117,7 @@ class EditGeneralOptionForm(ModelForm):
|
|||
self.fields['site_name'].label = 'Nom du site web'
|
||||
self.fields['email_from'].label = "Adresse mail d\
|
||||
'expedition automatique"
|
||||
self.fields['GTU_sum_up'].label = "Résumé des CGU"
|
||||
|
||||
|
||||
class EditAssoOptionForm(ModelForm):
|
||||
|
@ -173,7 +177,15 @@ class ServiceForm(ModelForm):
|
|||
class DelServiceForm(Form):
|
||||
"""Suppression de services sur la page d'accueil"""
|
||||
services = forms.ModelMultipleChoiceField(
|
||||
queryset=Service.objects.all(),
|
||||
queryset=Service.objects.none(),
|
||||
label="Enregistrements service actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop('instances', None)
|
||||
super(DelServiceForm, self).__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields['services'].queryset = instances
|
||||
else:
|
||||
self.fields['services'].queryset = Service.objects.all()
|
||||
|
|
43
preferences/migrations/0025_auto_20171231_2142.py
Normal file
43
preferences/migrations/0025_auto_20171231_2142.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-12-31 20:42
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('preferences', '0024_optionaluser_all_can_create'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='assooption',
|
||||
options={'permissions': (('view_assooption', "Peut voir les options de l'asso"),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='generaloption',
|
||||
options={'permissions': (('view_generaloption', 'Peut voir les options générales'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='mailmessageoption',
|
||||
options={'permissions': (('view_mailmessageoption', 'Peut voir les options de mail'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='optionalmachine',
|
||||
options={'permissions': (('view_optionalmachine', 'Peut voir les options de machine'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='optionaltopologie',
|
||||
options={'permissions': (('view_optionaltopologie', 'Peut voir les options de topologie'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='optionaluser',
|
||||
options={'permissions': (('view_optionaluser', "Peut voir les options de l'user"),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='service',
|
||||
options={'permissions': (('view_service', 'Peut voir les options de service'),)},
|
||||
),
|
||||
]
|
16
preferences/migrations/0027_merge_20180106_2019.py
Normal file
16
preferences/migrations/0027_merge_20180106_2019.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-06 19:19
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('preferences', '0025_auto_20171231_2142'),
|
||||
('preferences', '0026_auto_20171216_0401'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
]
|
21
preferences/migrations/0028_assooption_description.py
Normal file
21
preferences/migrations/0028_assooption_description.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-08 14:12
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('preferences', '0027_merge_20180106_2019'),
|
||||
('preferences', '0043_optionalmachine_create_machine'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='assooption',
|
||||
name='description',
|
||||
field=models.TextField(default=''),
|
||||
),
|
||||
]
|
20
preferences/migrations/0028_auto_20180111_1129.py
Normal file
20
preferences/migrations/0028_auto_20180111_1129.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-11 10:29
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('preferences', '0027_merge_20180106_2019'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='optionaluser',
|
||||
name='max_recharge',
|
||||
field=models.DecimalField(decimal_places=2, default=100, max_digits=5),
|
||||
),
|
||||
]
|
24
preferences/migrations/0028_auto_20180128_2203.py
Normal file
24
preferences/migrations/0028_auto_20180128_2203.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-28 21:03
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('preferences', '0027_merge_20180106_2019'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='optionalmachine',
|
||||
name='ipv6',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='optionalmachine',
|
||||
name='ipv6_mode',
|
||||
field=models.CharField(choices=[('SLAAC', 'Autoconfiguration par RA'), ('DHCPV6', 'Attribution des ip par dhcpv6'), ('DISABLED', 'Désactivé')], default='DISABLED', max_length=32),
|
||||
),
|
||||
]
|
20
preferences/migrations/0029_auto_20180111_1134.py
Normal file
20
preferences/migrations/0029_auto_20180111_1134.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-11 10:34
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('preferences', '0028_auto_20180111_1129'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='assooption',
|
||||
name='payment',
|
||||
field=models.CharField(choices=[('NONE', 'NONE'), ('COMNPAY', 'COMNPAY')], default='NONE', max_length=255),
|
||||
),
|
||||
]
|
24
preferences/migrations/0030_auto_20180111_2346.py
Normal file
24
preferences/migrations/0030_auto_20180111_2346.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-11 22:46
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('preferences', '0029_auto_20180111_1134'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='optionaluser',
|
||||
name='max_recharge',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='optionaluser',
|
||||
name='max_solde',
|
||||
field=models.DecimalField(decimal_places=2, default=50, max_digits=5),
|
||||
),
|
||||
]
|
20
preferences/migrations/0031_optionaluser_self_adhesion.py
Normal file
20
preferences/migrations/0031_optionaluser_self_adhesion.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-12 11:34
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('preferences', '0030_auto_20180111_2346'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='optionaluser',
|
||||
name='self_adhesion',
|
||||
field=models.BooleanField(default=False, help_text='Un nouvel utilisateur peut se créer son compte sur re2o'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-13 16:43
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('preferences', '0031_optionaluser_self_adhesion'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='optionaluser',
|
||||
name='min_online_payment',
|
||||
field=models.DecimalField(decimal_places=2, default=10, max_digits=5),
|
||||
),
|
||||
]
|
20
preferences/migrations/0033_generaloption_gtu_sum_up.py
Normal file
20
preferences/migrations/0033_generaloption_gtu_sum_up.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-14 19:12
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('preferences', '0032_optionaluser_min_online_payment'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='generaloption',
|
||||
name='GTU_sum_up',
|
||||
field=models.TextField(blank=True, default='', help_text='Résumé des CGU'),
|
||||
),
|
||||
]
|
25
preferences/migrations/0034_auto_20180114_2025.py
Normal file
25
preferences/migrations/0034_auto_20180114_2025.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-14 19:25
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('preferences', '0033_generaloption_gtu_sum_up'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='generaloption',
|
||||
name='GTU',
|
||||
field=models.FileField(default='', upload_to='GTU'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='generaloption',
|
||||
name='GTU_sum_up',
|
||||
field=models.TextField(blank=True, default=''),
|
||||
),
|
||||
]
|
20
preferences/migrations/0035_auto_20180114_2132.py
Normal file
20
preferences/migrations/0035_auto_20180114_2132.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-14 20:32
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('preferences', '0034_auto_20180114_2025'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='generaloption',
|
||||
name='GTU',
|
||||
field=models.FileField(default='', upload_to='/var/www/static/'),
|
||||
),
|
||||
]
|
20
preferences/migrations/0036_auto_20180114_2141.py
Normal file
20
preferences/migrations/0036_auto_20180114_2141.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-14 20:41
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('preferences', '0035_auto_20180114_2132'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='generaloption',
|
||||
name='GTU',
|
||||
field=models.FileField(default='', upload_to=''),
|
||||
),
|
||||
]
|
20
preferences/migrations/0037_auto_20180114_2156.py
Normal file
20
preferences/migrations/0037_auto_20180114_2156.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-14 20:56
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('preferences', '0036_auto_20180114_2141'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='generaloption',
|
||||
name='GTU',
|
||||
field=models.FileField(default='', null=True, upload_to=''),
|
||||
),
|
||||
]
|
20
preferences/migrations/0038_auto_20180114_2209.py
Normal file
20
preferences/migrations/0038_auto_20180114_2209.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-14 21:09
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('preferences', '0037_auto_20180114_2156'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='generaloption',
|
||||
name='GTU',
|
||||
field=models.FileField(blank=True, default='', null=True, upload_to=''),
|
||||
),
|
||||
]
|
21
preferences/migrations/0039_auto_20180115_0003.py
Normal file
21
preferences/migrations/0039_auto_20180115_0003.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-14 23:03
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import preferences.aes_field
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('preferences', '0038_auto_20180114_2209'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='assooption',
|
||||
name='payment_id',
|
||||
field=models.CharField(max_length=255, null=True),
|
||||
),
|
||||
]
|
26
preferences/migrations/0040_auto_20180129_1745.py
Normal file
26
preferences/migrations/0040_auto_20180129_1745.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-01-29 16:45
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import preferences.aes_field
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('preferences', '0039_auto_20180115_0003'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='assooption',
|
||||
name='payment_pass',
|
||||
field=preferences.aes_field.AESEncryptedField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='assooption',
|
||||
name='payment_id',
|
||||
field=models.CharField(default='', max_length=255),
|
||||
),
|
||||
]
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue