diff --git a/CHANGELOG.md b/CHANGELOG.md index 40ca06d6..ed81b2c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -178,3 +178,28 @@ Be carefull, you need the proper rights to edit a DocumentTemplate. Re2o now sends subscription voucher when an invoice is controlled. It uses one of the templates. You also need to set the name of the president of your association to be set in your settings. + +## MR 427: Tickets +Manually edit `settings_local.py` to provide the new `OPTIONNAL_APPS` lists: +```python +OPTIONNAL_APPS_RE2O = ('tickets',) +OPTIONNAL_APPS = OPTIONNAL_APPS_RE2O + (...,...,) +``` + +Don't forget to run migrations afterwards. + +## MR 433 : upgrade django-ldapdb to 1.3.0 + +Uninstall the existing django-ldapdb installation + + pip3 uninstall django-ldapdb + +Install debian(buster) supported version + + apt install python3-django-ldapdb + +If you use MySQL, please run + +``` +SET GLOBAL SQL_MODE=ANSI_QUOTES; +``` diff --git a/api/acl.py b/api/acl.py index 0c336281..490b88c7 100644 --- a/api/acl.py +++ b/api/acl.py @@ -33,21 +33,21 @@ from django.utils.translation import ugettext as _ def _create_api_permission(): """Creates the 'use_api' permission if not created. - + The 'use_api' is a fake permission in the sense it is not associated with an existing model and this ensure the permission is created every time this file is imported. """ api_content_type, created = ContentType.objects.get_or_create( app_label=settings.API_CONTENT_TYPE_APP_LABEL, - model=settings.API_CONTENT_TYPE_MODEL + model=settings.API_CONTENT_TYPE_MODEL, ) if created: api_content_type.save() api_permission, created = Permission.objects.get_or_create( name=settings.API_PERMISSION_NAME, content_type=api_content_type, - codename=settings.API_PERMISSION_CODENAME + codename=settings.API_PERMISSION_CODENAME, ) if created: api_permission.save() @@ -67,9 +67,13 @@ def can_view(user): viewing is granted and msg is a message (can be None). """ kwargs = { - 'app_label': settings.API_CONTENT_TYPE_APP_LABEL, - 'codename': settings.API_PERMISSION_CODENAME + "app_label": settings.API_CONTENT_TYPE_APP_LABEL, + "codename": settings.API_PERMISSION_CODENAME, } - can = user.has_perm('%(app_label)s.%(codename)s' % kwargs) - return can, None if can else _("You don't have the right to see this" - " application.") + permission = "%(app_label)s.%(codename)s" % kwargs + can = user.has_perm(permission) + return ( + can, + None if can else _("You don't have the right to view this application."), + (permission,), + ) diff --git a/api/authentication.py b/api/authentication.py index d426db24..88922987 100644 --- a/api/authentication.py +++ b/api/authentication.py @@ -41,9 +41,7 @@ class ExpiringTokenAuthentication(TokenAuthentication): user, token = base.authenticate_credentials(key) # Check that the genration time of the token is not too old - token_duration = datetime.timedelta( - seconds=settings.API_TOKEN_DURATION - ) + token_duration = datetime.timedelta(seconds=settings.API_TOKEN_DURATION) utc_now = datetime.datetime.now(datetime.timezone.utc) if token.created < utc_now - token_duration: raise exceptions.AuthenticationFailed(_("The token has expired.")) diff --git a/api/locale/fr/LC_MESSAGES/django.po b/api/locale/fr/LC_MESSAGES/django.po index f2d6755e..aba3d217 100644 --- a/api/locale/fr/LC_MESSAGES/django.po +++ b/api/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-01-08 23:06+0100\n" +"POT-Creation-Date: 2019-11-19 23:43+0100\n" "PO-Revision-Date: 2019-01-07 01:37+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -31,10 +31,10 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: acl.py:74 -msgid "You don't have the right to see this application." +#: api/acl.py:77 +msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: authentication.py:49 +#: api/authentication.py:47 msgid "The token has expired." msgstr "Le jeton a expiré." diff --git a/api/pagination.py b/api/pagination.py index d34c17ab..79da050e 100644 --- a/api/pagination.py +++ b/api/pagination.py @@ -38,8 +38,9 @@ class PageSizedPagination(pagination.PageNumberPagination): max_page_size: The maximum number of results a page can output no matter what is requested. """ - page_size_query_param = 'page_size' - all_pages_strings = ('all',) + + page_size_query_param = "page_size" + all_pages_strings = ("all",) max_page_size = 10000 def get_page_size(self, request): diff --git a/api/permissions.py b/api/permissions.py index 7d778c69..a8c70f67 100644 --- a/api/permissions.py +++ b/api/permissions.py @@ -55,17 +55,21 @@ def _get_param_in_view(view, param_name): AssertionError: None of the getter function or the attribute are defined in the view. """ - assert hasattr(view, 'get_' + param_name) \ - or getattr(view, param_name, None) is not None, ( - 'cannot apply {} on a view that does not set ' - '`.{}` or have a `.get_{}()` method.' - ).format(self.__class__.__name__, param_name, param_name) + assert ( + hasattr(view, "get_" + param_name) + or getattr(view, param_name, None) is not None + ), ( + "cannot apply {} on a view that does not set " + "`.{}` or have a `.get_{}()` method." + ).format( + self.__class__.__name__, param_name, param_name + ) - if hasattr(view, 'get_' + param_name): - param = getattr(view, 'get_' + param_name)() - assert param is not None, ( - '{}.get_{}() returned None' - ).format(view.__class__.__name__, param_name) + if hasattr(view, "get_" + param_name): + param = getattr(view, "get_" + param_name)() + assert param is not None, ("{}.get_{}() returned None").format( + view.__class__.__name__, param_name + ) return param return getattr(view, param_name) @@ -97,7 +101,7 @@ class ACLPermission(permissions.BasePermission): rest_framework.exception.MethodNotAllowed: The requested method is not allowed for this view. """ - perms_map = _get_param_in_view(view, 'perms_map') + perms_map = _get_param_in_view(view, "perms_map") if method not in perms_map: raise exceptions.MethodNotAllowed(method) @@ -123,7 +127,7 @@ class ACLPermission(permissions.BasePermission): """ # Workaround to ensure ACLPermissions are not applied # to the root view when using DefaultRouter. - if getattr(view, '_ignore_model_permissions', False): + if getattr(view, "_ignore_model_permissions", False): return True if not request.user or not request.user.is_authenticated: @@ -148,22 +152,22 @@ class AutodetectACLPermission(permissions.BasePermission): """ perms_map = { - 'GET': [can_see_api, lambda model: model.can_view_all], - 'OPTIONS': [can_see_api, lambda model: model.can_view_all], - 'HEAD': [can_see_api, lambda model: model.can_view_all], - 'POST': [can_see_api, lambda model: model.can_create], - 'PUT': [], # No restrictions, apply to objects - 'PATCH': [], # No restrictions, apply to objects - 'DELETE': [], # No restrictions, apply to objects + "GET": [can_see_api, lambda model: model.can_view_all], + "OPTIONS": [can_see_api, lambda model: model.can_view_all], + "HEAD": [can_see_api, lambda model: model.can_view_all], + "POST": [can_see_api, lambda model: model.can_create], + "PUT": [], # No restrictions, apply to objects + "PATCH": [], # No restrictions, apply to objects + "DELETE": [], # No restrictions, apply to objects } perms_obj_map = { - 'GET': [can_see_api, lambda obj: obj.can_view], - 'OPTIONS': [can_see_api, lambda obj: obj.can_view], - 'HEAD': [can_see_api, lambda obj: obj.can_view], - 'POST': [], # No restrictions, apply to models - 'PUT': [can_see_api, lambda obj: obj.can_edit], - 'PATCH': [can_see_api, lambda obj: obj.can_edit], - 'DELETE': [can_see_api, lambda obj: obj.can_delete], + "GET": [can_see_api, lambda obj: obj.can_view], + "OPTIONS": [can_see_api, lambda obj: obj.can_view], + "HEAD": [can_see_api, lambda obj: obj.can_view], + "POST": [], # No restrictions, apply to models + "PUT": [can_see_api, lambda obj: obj.can_edit], + "PATCH": [can_see_api, lambda obj: obj.can_edit], + "DELETE": [can_see_api, lambda obj: obj.can_delete], } def get_required_permissions(self, method, model): @@ -210,7 +214,7 @@ class AutodetectACLPermission(permissions.BasePermission): @staticmethod def _queryset(view): - return _get_param_in_view(view, 'queryset') + return _get_param_in_view(view, "queryset") def has_permission(self, request, view): """Check that the user has the model-based permissions to perform @@ -232,7 +236,7 @@ class AutodetectACLPermission(permissions.BasePermission): """ # Workaround to ensure ACLPermissions are not applied # to the root view when using DefaultRouter. - if getattr(view, '_ignore_model_permissions', False): + if getattr(view, "_ignore_model_permissions", False): return True if not request.user or not request.user.is_authenticated: @@ -274,7 +278,7 @@ class AutodetectACLPermission(permissions.BasePermission): # to make another lookup. raise Http404 - read_perms = self.get_required_object_permissions('GET', obj) + read_perms = self.get_required_object_permissions("GET", obj) if not read_perms(request.user)[0]: raise Http404 diff --git a/api/routers.py b/api/routers.py index 7c9e4a0f..5841d09c 100644 --- a/api/routers.py +++ b/api/routers.py @@ -74,9 +74,9 @@ class AllViewsRouter(DefaultRouter): Returns: The name to use for this route. """ - return pattern.split('/')[-1] + return pattern.split("/")[-1] - def get_api_root_view(self, schema_urls=None): + def get_api_root_view(self, schema_urls=None, api_urls=None): """Create a class-based view to use as the API root. Highly inspired by the base class. See details on the implementation @@ -102,12 +102,10 @@ class AllViewsRouter(DefaultRouter): if schema_urls and self.schema_title: view_renderers += list(self.schema_renderers) schema_generator = SchemaGenerator( - title=self.schema_title, - patterns=schema_urls + title=self.schema_title, patterns=schema_urls ) schema_media_types = [ - renderer.media_type - for renderer in self.schema_renderers + renderer.media_type for renderer in self.schema_renderers ] class APIRoot(views.APIView): @@ -128,14 +126,14 @@ class AllViewsRouter(DefaultRouter): namespace = request.resolver_match.namespace for key, url_name in api_root_dict.items(): if namespace: - url_name = namespace + ':' + url_name + url_name = namespace + ":" + url_name try: ret[key] = reverse( url_name, args=args, kwargs=kwargs, request=request, - format=kwargs.get('format', None) + format=kwargs.get("format", None), ) except NoReverseMatch: # Don't bail out if eg. no list routes exist, only detail routes. diff --git a/api/serializers.py b/api/serializers.py index 8c22ed21..ec4d0689 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -32,7 +32,7 @@ import users.models as users # The namespace used for the API. It must match the namespace used in the # urlpatterns to include the API URLs. -API_NAMESPACE = 'api' +API_NAMESPACE = "api" class NamespacedHRField(serializers.HyperlinkedRelatedField): @@ -42,7 +42,7 @@ class NamespacedHRField(serializers.HyperlinkedRelatedField): def __init__(self, view_name=None, **kwargs): if view_name is not None: - view_name = '%s:%s' % (API_NAMESPACE, view_name) + view_name = "%s:%s" % (API_NAMESPACE, view_name) super(NamespacedHRField, self).__init__(view_name=view_name, **kwargs) @@ -53,7 +53,7 @@ class NamespacedHIField(serializers.HyperlinkedIdentityField): def __init__(self, view_name=None, **kwargs): if view_name is not None: - view_name = '%s:%s' % (API_NAMESPACE, view_name) + view_name = "%s:%s" % (API_NAMESPACE, view_name) super(NamespacedHIField, self).__init__(view_name=view_name, **kwargs) @@ -61,6 +61,7 @@ class NamespacedHMSerializer(serializers.HyperlinkedModelSerializer): """A `rest_framework.serializers.HyperlinkedModelSerializer` subclass to automatically prefix view names with the API namespace. """ + serializer_related_field = NamespacedHRField serializer_url_field = NamespacedHIField @@ -74,14 +75,25 @@ class FactureSerializer(NamespacedHMSerializer): class Meta: model = cotisations.Facture - fields = ('user', 'paiement', 'banque', 'cheque', 'date', 'valid', - 'control', 'prix_total', 'name', 'api_url') + fields = ( + "user", + "paiement", + "banque", + "cheque", + "date", + "valid", + "control", + "prix_total", + "name", + "api_url", + ) class BaseInvoiceSerializer(NamespacedHMSerializer): class Meta: model = cotisations.BaseInvoice - fields = ('__all__') + fields = "__all__" + class VenteSerializer(NamespacedHMSerializer): """Serialize `cotisations.models.Vente` objects. @@ -89,9 +101,16 @@ class VenteSerializer(NamespacedHMSerializer): class Meta: model = cotisations.Vente - fields = ('facture', - 'number', 'name', 'prix', 'duration', - 'type_cotisation', 'prix_total', 'api_url') + fields = ( + "facture", + "number", + "name", + "prix", + "duration", + "type_cotisation", + "prix_total", + "api_url", + ) class ArticleSerializer(NamespacedHMSerializer): @@ -100,8 +119,7 @@ class ArticleSerializer(NamespacedHMSerializer): class Meta: model = cotisations.Article - fields = ('name', 'prix', 'duration', 'type_user', - 'type_cotisation', 'api_url') + fields = ("name", "prix", "duration", "type_user", "type_cotisation", "api_url") class BanqueSerializer(NamespacedHMSerializer): @@ -110,7 +128,7 @@ class BanqueSerializer(NamespacedHMSerializer): class Meta: model = cotisations.Banque - fields = ('name', 'api_url') + fields = ("name", "api_url") class PaiementSerializer(NamespacedHMSerializer): @@ -119,7 +137,7 @@ class PaiementSerializer(NamespacedHMSerializer): class Meta: model = cotisations.Paiement - fields = ('moyen', 'api_url') + fields = ("moyen", "api_url") class CotisationSerializer(NamespacedHMSerializer): @@ -128,8 +146,7 @@ class CotisationSerializer(NamespacedHMSerializer): class Meta: model = cotisations.Cotisation - fields = ('vente', 'type_cotisation', 'date_start', 'date_end', - 'api_url') + fields = ("vente", "type_cotisation", "date_start", "date_end", "api_url") # MACHINES @@ -141,7 +158,7 @@ class MachineSerializer(NamespacedHMSerializer): class Meta: model = machines.Machine - fields = ('user', 'name', 'active', 'api_url') + fields = ("user", "name", "active", "api_url") class MachineTypeSerializer(NamespacedHMSerializer): @@ -150,7 +167,7 @@ class MachineTypeSerializer(NamespacedHMSerializer): class Meta: model = machines.MachineType - fields = ('type', 'ip_type', 'api_url') + fields = ("name", "ip_type", "api_url") class IpTypeSerializer(NamespacedHMSerializer): @@ -159,9 +176,17 @@ class IpTypeSerializer(NamespacedHMSerializer): class Meta: model = machines.IpType - fields = ('type', 'extension', 'need_infra', 'domaine_ip_start', - 'domaine_ip_stop', 'prefix_v6', 'vlan', 'ouverture_ports', - 'api_url') + fields = ( + "name", + "extension", + "need_infra", + "domaine_ip_start", + "domaine_ip_stop", + "prefix_v6", + "vlan", + "ouverture_ports", + "api_url", + ) class VlanSerializer(NamespacedHMSerializer): @@ -170,8 +195,17 @@ class VlanSerializer(NamespacedHMSerializer): class Meta: model = machines.Vlan - fields = ('vlan_id', 'name', 'comment', 'arp_protect', 'dhcp_snooping', - 'dhcpv6_snooping', 'igmp', 'mld', 'api_url') + fields = ( + "vlan_id", + "name", + "comment", + "arp_protect", + "dhcp_snooping", + "dhcpv6_snooping", + "igmp", + "mld", + "api_url", + ) class NasSerializer(NamespacedHMSerializer): @@ -180,8 +214,14 @@ class NasSerializer(NamespacedHMSerializer): class Meta: model = machines.Nas - fields = ('name', 'nas_type', 'machine_type', 'port_access_mode', - 'autocapture_mac', 'api_url') + fields = ( + "name", + "nas_type", + "machine_type", + "port_access_mode", + "autocapture_mac", + "api_url", + ) class SOASerializer(NamespacedHMSerializer): @@ -190,8 +230,7 @@ class SOASerializer(NamespacedHMSerializer): class Meta: model = machines.SOA - fields = ('name', 'mail', 'refresh', 'retry', 'expire', 'ttl', - 'api_url') + fields = ("name", "mail", "refresh", "retry", "expire", "ttl", "api_url") class ExtensionSerializer(NamespacedHMSerializer): @@ -200,8 +239,7 @@ class ExtensionSerializer(NamespacedHMSerializer): class Meta: model = machines.Extension - fields = ('name', 'need_infra', 'origin', 'origin_v6', 'soa', - 'api_url') + fields = ("name", "need_infra", "origin", "origin_v6", "soa", "api_url") class MxSerializer(NamespacedHMSerializer): @@ -210,7 +248,7 @@ class MxSerializer(NamespacedHMSerializer): class Meta: model = machines.Mx - fields = ('zone', 'priority', 'name', 'api_url') + fields = ("zone", "priority", "name", "api_url") class DNameSerializer(NamespacedHMSerializer): @@ -219,7 +257,7 @@ class DNameSerializer(NamespacedHMSerializer): class Meta: model = machines.DName - fields = ('zone', 'alias', 'api_url') + fields = ("zone", "alias", "api_url") class NsSerializer(NamespacedHMSerializer): @@ -228,7 +266,7 @@ class NsSerializer(NamespacedHMSerializer): class Meta: model = machines.Ns - fields = ('zone', 'ns', 'api_url') + fields = ("zone", "ns", "api_url") class TxtSerializer(NamespacedHMSerializer): @@ -237,7 +275,7 @@ class TxtSerializer(NamespacedHMSerializer): class Meta: model = machines.Txt - fields = ('zone', 'field1', 'field2', 'api_url') + fields = ("zone", "field1", "field2", "api_url") class SrvSerializer(NamespacedHMSerializer): @@ -246,8 +284,17 @@ class SrvSerializer(NamespacedHMSerializer): class Meta: model = machines.Srv - fields = ('service', 'protocole', 'extension', 'ttl', 'priority', - 'weight', 'port', 'target', 'api_url') + fields = ( + "service", + "protocole", + "extension", + "ttl", + "priority", + "weight", + "port", + "target", + "api_url", + ) class SshFpSerializer(NamespacedHMSerializer): @@ -256,19 +303,28 @@ class SshFpSerializer(NamespacedHMSerializer): class Meta: model = machines.SshFp - field = ('machine', 'pub_key_entry', 'algo', 'comment', 'api_url') + field = ("machine", "pub_key_entry", "algo", "comment", "api_url") class InterfaceSerializer(NamespacedHMSerializer): """Serialize `machines.models.Interface` objects. """ + mac_address = serializers.CharField() - active = serializers.BooleanField(source='is_active') + active = serializers.BooleanField(source="is_active") class Meta: model = machines.Interface - fields = ('ipv4', 'mac_address', 'machine', 'type', 'details', - 'port_lists', 'active', 'api_url') + fields = ( + "ipv4", + "mac_address", + "machine", + "machine_type", + "details", + "port_lists", + "active", + "api_url", + ) class Ipv6ListSerializer(NamespacedHMSerializer): @@ -277,7 +333,7 @@ class Ipv6ListSerializer(NamespacedHMSerializer): class Meta: model = machines.Ipv6List - fields = ('ipv6', 'interface', 'slaac_ip', 'api_url') + fields = ("ipv6", "interface", "slaac_ip", "api_url") class DomainSerializer(NamespacedHMSerializer): @@ -286,8 +342,7 @@ class DomainSerializer(NamespacedHMSerializer): class Meta: model = machines.Domain - fields = ('interface_parent', 'name', 'extension', 'cname', - 'api_url') + fields = ("interface_parent", "name", "extension", "cname", "api_url") class IpListSerializer(NamespacedHMSerializer): @@ -296,7 +351,7 @@ class IpListSerializer(NamespacedHMSerializer): class Meta: model = machines.IpList - fields = ('ipv4', 'ip_type', 'need_infra', 'api_url') + fields = ("ipv4", "ip_type", "need_infra", "api_url") class ServiceSerializer(NamespacedHMSerializer): @@ -305,8 +360,13 @@ class ServiceSerializer(NamespacedHMSerializer): class Meta: model = machines.Service - fields = ('service_type', 'min_time_regen', 'regular_time_regen', - 'servers', 'api_url') + fields = ( + "service_type", + "min_time_regen", + "regular_time_regen", + "servers", + "api_url", + ) class ServiceLinkSerializer(NamespacedHMSerializer): @@ -315,25 +375,44 @@ class ServiceLinkSerializer(NamespacedHMSerializer): class Meta: model = machines.Service_link - fields = ('service', 'server', 'last_regen', 'asked_regen', - 'need_regen', 'api_url') - extra_kwargs = { - 'api_url': {'view_name': 'servicelink-detail'} - } + fields = ( + "service", + "server", + "last_regen", + "asked_regen", + "need_regen", + "api_url", + ) + extra_kwargs = {"api_url": {"view_name": "servicelink-detail"}} class OuverturePortListSerializer(NamespacedHMSerializer): """Serialize `machines.models.OuverturePortList` objects. """ - tcp_ports_in = NamespacedHRField(view_name='ouvertureport-detail', many=True, read_only=True) - udp_ports_in = NamespacedHRField(view_name='ouvertureport-detail', many=True, read_only=True) - tcp_ports_out = NamespacedHRField(view_name='ouvertureport-detail', many=True, read_only=True) - udp_ports_out = NamespacedHRField(view_name='ouvertureport-detail', many=True, read_only=True) + + tcp_ports_in = NamespacedHRField( + view_name="ouvertureport-detail", many=True, read_only=True + ) + udp_ports_in = NamespacedHRField( + view_name="ouvertureport-detail", many=True, read_only=True + ) + tcp_ports_out = NamespacedHRField( + view_name="ouvertureport-detail", many=True, read_only=True + ) + udp_ports_out = NamespacedHRField( + view_name="ouvertureport-detail", many=True, read_only=True + ) class Meta: model = machines.OuverturePortList - fields = ('name', 'tcp_ports_in', 'udp_ports_in', 'tcp_ports_out', - 'udp_ports_out', 'api_url') + fields = ( + "name", + "tcp_ports_in", + "udp_ports_in", + "tcp_ports_out", + "udp_ports_out", + "api_url", + ) class OuverturePortSerializer(NamespacedHMSerializer): @@ -342,17 +421,18 @@ class OuverturePortSerializer(NamespacedHMSerializer): class Meta: model = machines.OuverturePort - fields = ('begin', 'end', 'port_list', 'protocole', 'io', 'api_url') + fields = ("begin", "end", "port_list", "protocole", "io", "api_url") class RoleSerializer(NamespacedHMSerializer): """Serialize `machines.models.OuverturePort` objects. """ + servers = InterfaceSerializer(read_only=True, many=True) class Meta: model = machines.Role - fields = ('role_type', 'servers', 'api_url') + fields = ("role_type", "servers", "api_url") # PREFERENCES @@ -361,15 +441,22 @@ class RoleSerializer(NamespacedHMSerializer): class OptionalUserSerializer(NamespacedHMSerializer): """Serialize `preferences.models.OptionalUser` objects. """ - tel_mandatory = serializers.BooleanField(source='is_tel_mandatory') + + tel_mandatory = serializers.BooleanField(source="is_tel_mandatory") shell_default = serializers.StringRelatedField() class Meta: model = preferences.OptionalUser - fields = ('tel_mandatory', 'gpg_fingerprint', - 'all_can_create_club', 'self_adhesion', 'shell_default', - 'self_change_shell', 'local_email_accounts_enabled', 'local_email_domain', - 'max_email_address', + fields = ( + "tel_mandatory", + "gpg_fingerprint", + "all_can_create_club", + "self_adhesion", + "shell_default", + "self_change_shell", + "local_email_accounts_enabled", + "local_email_domain", + "max_email_address", ) @@ -379,22 +466,35 @@ class OptionalMachineSerializer(NamespacedHMSerializer): class Meta: model = preferences.OptionalMachine - fields = ('password_machine', 'max_lambdauser_interfaces', - 'max_lambdauser_aliases', 'ipv6_mode', 'create_machine', - 'ipv6') + fields = ( + "password_machine", + "max_lambdauser_interfaces", + "max_lambdauser_aliases", + "ipv6_mode", + "create_machine", + "ipv6", + ) class OptionalTopologieSerializer(NamespacedHMSerializer): """Serialize `preferences.models.OptionalTopologie` objects. """ + switchs_management_interface_ip = serializers.CharField() class Meta: model = preferences.OptionalTopologie - fields = ('switchs_ip_type', 'switchs_web_management', - 'switchs_web_management_ssl', 'switchs_rest_management', - 'switchs_management_utils', 'switchs_management_interface_ip', - 'provision_switchs_enabled', 'switchs_provision', 'switchs_management_sftp_creds') + fields = ( + "switchs_ip_type", + "switchs_web_management", + "switchs_web_management_ssl", + "switchs_rest_management", + "switchs_management_utils", + "switchs_management_interface_ip", + "provision_switchs_enabled", + "switchs_provision", + "switchs_management_sftp_creds", + ) class RadiusOptionSerializer(NamespacedHMSerializer): @@ -403,11 +503,20 @@ class RadiusOptionSerializer(NamespacedHMSerializer): class Meta: model = preferences.RadiusOption - fields = ('radius_general_policy', 'unknown_machine', - 'unknown_machine_vlan', 'unknown_port', - 'unknown_port_vlan', 'unknown_room', 'unknown_room_vlan', - 'non_member', 'non_member_vlan', 'banned', 'banned_vlan', - 'vlan_decision_ok') + fields = ( + "radius_general_policy", + "unknown_machine", + "unknown_machine_vlan", + "unknown_port", + "unknown_port_vlan", + "unknown_room", + "unknown_room_vlan", + "non_member", + "non_member_vlan", + "banned", + "banned_vlan", + "vlan_decision_ok", + ) class GeneralOptionSerializer(NamespacedHMSerializer): @@ -416,11 +525,20 @@ class GeneralOptionSerializer(NamespacedHMSerializer): class Meta: model = preferences.GeneralOption - fields = ('general_message_fr', 'general_message_en', - 'search_display_page', 'pagination_number', - 'pagination_large_number', 'req_expire_hrs', - 'site_name', 'main_site_url', 'email_from', - 'GTU_sum_up', 'GTU') + fields = ( + "general_message_fr", + "general_message_en", + "search_display_page", + "pagination_number", + "pagination_large_number", + "req_expire_hrs", + "site_name", + "main_site_url", + "email_from", + "GTU_sum_up", + "GTU", + ) + class HomeServiceSerializer(NamespacedHMSerializer): """Serialize `preferences.models.Service` objects. @@ -428,10 +546,8 @@ class HomeServiceSerializer(NamespacedHMSerializer): class Meta: model = preferences.Service - fields = ('name', 'url', 'description', 'image', 'api_url') - extra_kwargs = { - 'api_url': {'view_name': 'homeservice-detail'} - } + fields = ("name", "url", "description", "image", "api_url") + extra_kwargs = {"api_url": {"view_name": "homeservice-detail"}} class AssoOptionSerializer(NamespacedHMSerializer): @@ -440,8 +556,17 @@ class AssoOptionSerializer(NamespacedHMSerializer): class Meta: model = preferences.AssoOption - fields = ('name', 'siret', 'adresse1', 'adresse2', 'contact', - 'telephone', 'pseudo', 'utilisateur_asso', 'description') + fields = ( + "name", + "siret", + "adresse1", + "adresse2", + "contact", + "telephone", + "pseudo", + "utilisateur_asso", + "description", + ) class HomeOptionSerializer(NamespacedHMSerializer): @@ -450,7 +575,7 @@ class HomeOptionSerializer(NamespacedHMSerializer): class Meta: model = preferences.HomeOption - fields = ('facebook_url', 'twitter_url', 'twitter_account_name') + fields = ("facebook_url", "twitter_url", "twitter_account_name") class MailMessageOptionSerializer(NamespacedHMSerializer): @@ -459,7 +584,7 @@ class MailMessageOptionSerializer(NamespacedHMSerializer): class Meta: model = preferences.MailMessageOption - fields = ('welcome_mail_fr', 'welcome_mail_en') + fields = ("welcome_mail_fr", "welcome_mail_en") # TOPOLOGIE @@ -471,8 +596,14 @@ class StackSerializer(NamespacedHMSerializer): class Meta: model = topologie.Stack - fields = ('name', 'stack_id', 'details', 'member_id_min', - 'member_id_max', 'api_url') + fields = ( + "name", + "stack_id", + "details", + "member_id_min", + "member_id_max", + "api_url", + ) class AccessPointSerializer(NamespacedHMSerializer): @@ -481,18 +612,28 @@ class AccessPointSerializer(NamespacedHMSerializer): class Meta: model = topologie.AccessPoint - fields = ('user', 'name', 'active', 'location', 'api_url') + fields = ("user", "name", "active", "location", "api_url") class SwitchSerializer(NamespacedHMSerializer): """Serialize `topologie.models.Switch` objects """ - port_amount = serializers.IntegerField(source='number') + + port_amount = serializers.IntegerField(source="number") class Meta: model = topologie.Switch - fields = ('user', 'name', 'active', 'port_amount', 'stack', - 'stack_member_id', 'model', 'switchbay', 'api_url') + fields = ( + "user", + "name", + "active", + "port_amount", + "stack", + "stack_member_id", + "model", + "switchbay", + "api_url", + ) class ServerSerializer(NamespacedHMSerializer): @@ -501,7 +642,7 @@ class ServerSerializer(NamespacedHMSerializer): class Meta: model = topologie.Server - fields = ('user', 'name', 'active', 'api_url') + fields = ("user", "name", "active", "api_url") class ModelSwitchSerializer(NamespacedHMSerializer): @@ -510,7 +651,7 @@ class ModelSwitchSerializer(NamespacedHMSerializer): class Meta: model = topologie.ModelSwitch - fields = ('reference', 'constructor', 'api_url') + fields = ("reference", "constructor", "api_url") class ConstructorSwitchSerializer(NamespacedHMSerializer): @@ -519,7 +660,7 @@ class ConstructorSwitchSerializer(NamespacedHMSerializer): class Meta: model = topologie.ConstructorSwitch - fields = ('name', 'api_url') + fields = ("name", "api_url") class SwitchBaySerializer(NamespacedHMSerializer): @@ -528,7 +669,7 @@ class SwitchBaySerializer(NamespacedHMSerializer): class Meta: model = topologie.SwitchBay - fields = ('name', 'building', 'info', 'api_url') + fields = ("name", "building", "info", "api_url") class BuildingSerializer(NamespacedHMSerializer): @@ -537,34 +678,59 @@ class BuildingSerializer(NamespacedHMSerializer): class Meta: model = topologie.Building - fields = ('name', 'api_url') + fields = ("name", "api_url") class SwitchPortSerializer(NamespacedHMSerializer): """Serialize `topologie.models.Port` objects """ - get_port_profile = NamespacedHIField(view_name='portprofile-detail', read_only=True) + get_port_profile = NamespacedHIField(view_name="portprofile-detail", read_only=True) class Meta: model = topologie.Port - fields = ('switch', 'port', 'room', 'machine_interface', 'related', - 'custom_profile', 'state', 'get_port_profile', 'details', 'api_url') + fields = ( + "switch", + "port", + "room", + "machine_interface", + "related", + "custom_profile", + "state", + "get_port_profile", + "details", + "api_url", + ) extra_kwargs = { - 'related': {'view_name': 'switchport-detail'}, - 'api_url': {'view_name': 'switchport-detail'}, + "related": {"view_name": "switchport-detail"}, + "api_url": {"view_name": "switchport-detail"}, } class PortProfileSerializer(NamespacedHMSerializer): """Serialize `topologie.models.Room` objects """ + class Meta: model = topologie.PortProfile - fields = ('name', 'profil_default', 'vlan_untagged', 'vlan_tagged', - 'radius_type', 'radius_mode', 'speed', 'mac_limit', 'flow_control', - 'dhcp_snooping', 'dhcpv6_snooping', 'dhcpv6_snooping', 'arp_protect', - 'ra_guard', 'loop_protect', 'api_url') + fields = ( + "name", + "profil_default", + "vlan_untagged", + "vlan_tagged", + "radius_type", + "radius_mode", + "speed", + "mac_limit", + "flow_control", + "dhcp_snooping", + "dhcpv6_snooping", + "dhcpv6_snooping", + "arp_protect", + "ra_guard", + "loop_protect", + "api_url", + ) class RoomSerializer(NamespacedHMSerializer): @@ -573,7 +739,7 @@ class RoomSerializer(NamespacedHMSerializer): class Meta: model = topologie.Room - fields = ('name', 'details', 'api_url') + fields = ("name", "details", "api_url") class PortProfileSerializer(NamespacedHMSerializer): @@ -581,11 +747,24 @@ class PortProfileSerializer(NamespacedHMSerializer): class Meta: model = topologie.PortProfile - fields = ('name', 'profil_default', 'vlan_untagged', 'vlan_tagged', - 'radius_type', 'radius_mode', 'speed', 'mac_limit', - 'flow_control', 'dhcp_snooping', 'dhcpv6_snooping', - 'arp_protect', 'ra_guard', 'loop_protect', 'vlan_untagged', - 'vlan_tagged') + fields = ( + "name", + "profil_default", + "vlan_untagged", + "vlan_tagged", + "radius_type", + "radius_mode", + "speed", + "mac_limit", + "flow_control", + "dhcp_snooping", + "dhcpv6_snooping", + "arp_protect", + "ra_guard", + "loop_protect", + "vlan_untagged", + "vlan_tagged", + ) # USERS @@ -594,64 +773,111 @@ class PortProfileSerializer(NamespacedHMSerializer): class UserSerializer(NamespacedHMSerializer): """Serialize `users.models.User` objects. """ - access = serializers.BooleanField(source='has_access') - uid = serializers.IntegerField(source='uid_number') + + access = serializers.BooleanField(source="has_access") + uid = serializers.IntegerField(source="uid_number") class Meta: model = users.User - fields = ('surname', 'pseudo', 'email', 'local_email_redirect', - 'local_email_enabled', 'school', 'shell', 'comment', - 'state', 'registered', 'telephone', 'solde', 'access', - 'end_access', 'uid', 'class_name', 'api_url') - extra_kwargs = { - 'shell': {'view_name': 'shell-detail'} - } + fields = ( + "surname", + "pseudo", + "email", + "local_email_redirect", + "local_email_enabled", + "school", + "shell", + "comment", + "state", + "registered", + "telephone", + "solde", + "access", + "end_access", + "uid", + "class_type", + "api_url", + ) + extra_kwargs = {"shell": {"view_name": "shell-detail"}} class ClubSerializer(NamespacedHMSerializer): """Serialize `users.models.Club` objects. """ - name = serializers.CharField(source='surname') - access = serializers.BooleanField(source='has_access') - uid = serializers.IntegerField(source='uid_number') + + name = serializers.CharField(source="surname") + access = serializers.BooleanField(source="has_access") + uid = serializers.IntegerField(source="uid_number") class Meta: model = users.Club - fields = ('name', 'pseudo', 'email', 'local_email_redirect', - 'local_email_enabled', 'school', 'shell', 'comment', - 'state', 'registered', 'telephone', 'solde', 'room', - 'access', 'end_access', 'administrators', 'members', - 'mailing', 'uid', 'api_url') - extra_kwargs = { - 'shell': {'view_name': 'shell-detail'} - } + fields = ( + "name", + "pseudo", + "email", + "local_email_redirect", + "local_email_enabled", + "school", + "shell", + "comment", + "state", + "registered", + "telephone", + "solde", + "room", + "access", + "end_access", + "administrators", + "members", + "mailing", + "uid", + "api_url", + ) + extra_kwargs = {"shell": {"view_name": "shell-detail"}} class AdherentSerializer(NamespacedHMSerializer): """Serialize `users.models.Adherent` objects. """ - access = serializers.BooleanField(source='has_access') - uid = serializers.IntegerField(source='uid_number') + + access = serializers.BooleanField(source="has_access") + uid = serializers.IntegerField(source="uid_number") class Meta: model = users.Adherent - fields = ('name', 'surname', 'pseudo', 'email', 'local_email_redirect', - 'local_email_enabled', 'school', 'shell', 'comment', - 'state', 'registered', 'telephone', 'room', 'solde', - 'access', 'end_access', 'uid', 'api_url', 'gid') - extra_kwargs = { - 'shell': {'view_name': 'shell-detail'} - } + fields = ( + "name", + "surname", + "pseudo", + "email", + "local_email_redirect", + "local_email_enabled", + "school", + "shell", + "comment", + "state", + "registered", + "telephone", + "room", + "solde", + "access", + "end_access", + "uid", + "api_url", + "gid", + ) + extra_kwargs = {"shell": {"view_name": "shell-detail"}} class BasicUserSerializer(NamespacedHMSerializer): """Serialize 'users.models.User' minimal infos""" - uid = serializers.IntegerField(source='uid_number') - gid = serializers.IntegerField(source='gid_number') + + uid = serializers.IntegerField(source="uid_number") + gid = serializers.IntegerField(source="gid_number") class Meta: model = users.User - fields = ('pseudo', 'uid', 'gid') + fields = ("pseudo", "uid", "gid") class ServiceUserSerializer(NamespacedHMSerializer): @@ -660,7 +886,7 @@ class ServiceUserSerializer(NamespacedHMSerializer): class Meta: model = users.ServiceUser - fields = ('pseudo', 'access_group', 'comment', 'api_url') + fields = ("pseudo", "access_group", "comment", "api_url") class SchoolSerializer(NamespacedHMSerializer): @@ -669,7 +895,7 @@ class SchoolSerializer(NamespacedHMSerializer): class Meta: model = users.School - fields = ('name', 'api_url') + fields = ("name", "api_url") class ListRightSerializer(NamespacedHMSerializer): @@ -678,7 +904,7 @@ class ListRightSerializer(NamespacedHMSerializer): class Meta: model = users.ListRight - fields = ('unix_name', 'gid', 'critical', 'details', 'api_url') + fields = ("unix_name", "gid", "critical", "details", "api_url") class ShellSerializer(NamespacedHMSerializer): @@ -687,41 +913,49 @@ class ShellSerializer(NamespacedHMSerializer): class Meta: model = users.ListShell - fields = ('shell', 'api_url') - extra_kwargs = { - 'api_url': {'view_name': 'shell-detail'} - } + fields = ("shell", "api_url") + extra_kwargs = {"api_url": {"view_name": "shell-detail"}} class BanSerializer(NamespacedHMSerializer): """Serialize `users.models.Ban` objects. """ - active = serializers.BooleanField(source='is_active') + + active = serializers.BooleanField(source="is_active") class Meta: model = users.Ban - fields = ('user', 'raison', 'date_start', 'date_end', 'state', - 'active', 'api_url') + fields = ( + "user", + "raison", + "date_start", + "date_end", + "state", + "active", + "api_url", + ) class WhitelistSerializer(NamespacedHMSerializer): """Serialize `users.models.Whitelist` objects. """ - active = serializers.BooleanField(source='is_active') + + active = serializers.BooleanField(source="is_active") class Meta: model = users.Whitelist - fields = ('user', 'raison', 'date_start', 'date_end', 'active', 'api_url') + fields = ("user", "raison", "date_start", "date_end", "active", "api_url") class EMailAddressSerializer(NamespacedHMSerializer): """Serialize `users.models.EMailAddress` objects. """ - user = serializers.CharField(source='user.pseudo', read_only=True) + + user = serializers.CharField(source="user.pseudo", read_only=True) class Meta: model = users.EMailAddress - fields = ('user', 'local_part', 'complete_email_address', 'api_url') + fields = ("user", "local_part", "complete_email_address", "api_url") # SERVICE REGEN @@ -730,51 +964,58 @@ class EMailAddressSerializer(NamespacedHMSerializer): class ServiceRegenSerializer(NamespacedHMSerializer): """Serialize the data about the services to regen. """ - hostname = serializers.CharField(source='server.domain.name', read_only=True) - service_name = serializers.CharField(source='service.service_type', read_only=True) + + hostname = serializers.CharField(source="server.domain.name", read_only=True) + service_name = serializers.CharField(source="service.service_type", read_only=True) need_regen = serializers.BooleanField() class Meta: model = machines.Service_link - fields = ('hostname', 'service_name', 'need_regen', 'api_url') - extra_kwargs = { - 'api_url': {'view_name': 'serviceregen-detail'} - } + fields = ("hostname", "service_name", "need_regen", "api_url") + extra_kwargs = {"api_url": {"view_name": "serviceregen-detail"}} + # Switches et ports + class InterfaceVlanSerializer(NamespacedHMSerializer): domain = serializers.CharField(read_only=True) ipv4 = serializers.CharField(read_only=True) ipv6 = Ipv6ListSerializer(read_only=True, many=True) - vlan_id = serializers.IntegerField(source='type.ip_type.vlan.vlan_id', read_only=True) + vlan_id = serializers.IntegerField( + source="machine_type.ip_type.vlan.vlan_id", read_only=True + ) class Meta: model = machines.Interface - fields = ('ipv4', 'ipv6', 'domain', 'vlan_id') + fields = ("ipv4", "ipv6", "domain", "vlan_id") + class InterfaceRoleSerializer(NamespacedHMSerializer): - interface = InterfaceVlanSerializer(source='machine.interface_set', read_only=True, many=True) + interface = InterfaceVlanSerializer( + source="machine.interface_set", read_only=True, many=True + ) class Meta: model = machines.Interface - fields = ('interface',) + fields = ("interface",) class RoleSerializer(NamespacedHMSerializer): """Serialize `machines.models.OuverturePort` objects. """ + servers = InterfaceRoleSerializer(read_only=True, many=True) class Meta: model = machines.Role - fields = ('role_type', 'servers', 'specific_role') + fields = ("role_type", "servers", "specific_role") class VlanPortSerializer(NamespacedHMSerializer): class Meta: model = machines.Vlan - fields = ('vlan_id', 'name') + fields = ("vlan_id", "name") class ProfilSerializer(NamespacedHMSerializer): @@ -783,7 +1024,24 @@ class ProfilSerializer(NamespacedHMSerializer): class Meta: model = topologie.PortProfile - fields = ('name', 'profil_default', 'vlan_untagged', 'vlan_tagged', 'radius_type', 'radius_mode', 'speed', 'mac_limit', 'flow_control', 'dhcp_snooping', 'dhcpv6_snooping', 'arp_protect', 'ra_guard', 'loop_protect', 'vlan_untagged', 'vlan_tagged') + fields = ( + "name", + "profil_default", + "vlan_untagged", + "vlan_tagged", + "radius_type", + "radius_mode", + "speed", + "mac_limit", + "flow_control", + "dhcp_snooping", + "dhcpv6_snooping", + "arp_protect", + "ra_guard", + "loop_protect", + "vlan_untagged", + "vlan_tagged", + ) class ModelSwitchSerializer(NamespacedHMSerializer): @@ -791,62 +1049,77 @@ class ModelSwitchSerializer(NamespacedHMSerializer): class Meta: model = topologie.ModelSwitch - fields = ('reference', 'firmware', 'constructor') + fields = ("reference", "firmware", "constructor") class SwitchBaySerializer(NamespacedHMSerializer): class Meta: model = topologie.SwitchBay - fields = ('name',) + fields = ("name",) class PortsSerializer(NamespacedHMSerializer): """Serialize `machines.models.Ipv6List` objects. """ - get_port_profile = ProfilSerializer(read_only=True) + get_port_profile = ProfilSerializer(read_only=True) class Meta: model = topologie.Port - fields = ('state', 'port', 'pretty_name', 'get_port_profile') - + fields = ("state", "port", "pretty_name", "get_port_profile") class SwitchPortSerializer(serializers.ModelSerializer): """Serialize the data about the switches""" + ports = PortsSerializer(many=True, read_only=True) model = ModelSwitchSerializer(read_only=True) switchbay = SwitchBaySerializer(read_only=True) - class Meta: model = topologie.Switch - fields = ('short_name', 'model', 'switchbay', 'ports', 'ipv4', 'ipv6', - 'interfaces_subnet', 'interfaces6_subnet', 'automatic_provision', 'rest_enabled', - 'web_management_enabled', 'get_radius_key_value', 'get_management_cred_value', - 'list_modules') + fields = ( + "short_name", + "model", + "switchbay", + "ports", + "ipv4", + "ipv6", + "interfaces_subnet", + "interfaces6_subnet", + "automatic_provision", + "rest_enabled", + "web_management_enabled", + "get_radius_key_value", + "get_management_cred_value", + "get_radius_servers", + "list_modules", + ) + # LOCAL EMAILS class LocalEmailUsersSerializer(NamespacedHMSerializer): - email_address = EMailAddressSerializer( - read_only=True, - many=True - ) + email_address = EMailAddressSerializer(read_only=True, many=True) class Meta: model = users.User - fields = ('local_email_enabled', 'local_email_redirect', - 'email_address', 'email') + fields = ( + "local_email_enabled", + "local_email_redirect", + "email_address", + "email", + ) # Firewall + class FirewallPortListSerializer(serializers.ModelSerializer): class Meta: model = machines.OuverturePort - fields = ('begin', 'end', 'protocole', 'io', 'show_port') + fields = ("begin", "end", "protocole", "io", "show_port") class FirewallOuverturePortListSerializer(serializers.ModelSerializer): @@ -857,7 +1130,7 @@ class FirewallOuverturePortListSerializer(serializers.ModelSerializer): class Meta: model = machines.OuverturePortList - fields = ('tcp_ports_in', 'udp_ports_in', 'tcp_ports_out', 'udp_ports_out') + fields = ("tcp_ports_in", "udp_ports_in", "tcp_ports_out", "udp_ports_out") class SubnetPortsOpenSerializer(serializers.ModelSerializer): @@ -865,17 +1138,23 @@ class SubnetPortsOpenSerializer(serializers.ModelSerializer): class Meta: model = machines.IpType - fields = ('type', 'domaine_ip_start', 'domaine_ip_stop', 'complete_prefixv6', 'ouverture_ports') + fields = ( + "name", + "domaine_ip_start", + "domaine_ip_stop", + "complete_prefixv6", + "ouverture_ports", + ) class InterfacePortsOpenSerializer(serializers.ModelSerializer): port_lists = FirewallOuverturePortListSerializer(read_only=True, many=True) - ipv4 = serializers.CharField(source='ipv4.ipv4', read_only=True) + ipv4 = serializers.CharField(source="ipv4.ipv4", read_only=True) ipv6 = Ipv6ListSerializer(many=True, read_only=True) class Meta: model = machines.Interface - fields = ('port_lists', 'ipv4', 'ipv6') + fields = ("port_lists", "ipv4", "ipv6") # DHCP @@ -885,14 +1164,15 @@ class HostMacIpSerializer(serializers.ModelSerializer): """Serialize the data about the hostname-ipv4-mac address association to build the DHCP lease files. """ - hostname = serializers.CharField(source='domain.name', read_only=True) - extension = serializers.CharField(source='domain.extension.name', read_only=True) + + hostname = serializers.CharField(source="domain.name", read_only=True) + extension = serializers.CharField(source="domain.extension.name", read_only=True) mac_address = serializers.CharField(read_only=True) - ipv4 = serializers.CharField(source='ipv4.ipv4', read_only=True) + ipv4 = serializers.CharField(source="ipv4.ipv4", read_only=True) class Meta: model = machines.Interface - fields = ('hostname', 'extension', 'mac_address', 'ipv4') + fields = ("hostname", "extension", "mac_address", "ipv4") # DNS @@ -905,7 +1185,7 @@ class SOARecordSerializer(SOASerializer): class Meta: model = machines.SOA - fields = ('name', 'mail', 'refresh', 'retry', 'expire', 'ttl') + fields = ("name", "mail", "refresh", "retry", "expire", "ttl") class OriginV4RecordSerializer(IpListSerializer): @@ -914,27 +1194,29 @@ class OriginV4RecordSerializer(IpListSerializer): """ class Meta(IpListSerializer.Meta): - fields = ('ipv4',) + fields = ("ipv4",) class NSRecordSerializer(NsSerializer): """Serialize `machines.models.Ns` objects with the data needed to generate a NS DNS record. """ - target = serializers.CharField(source='ns', read_only=True) + + target = serializers.CharField(source="ns", read_only=True) class Meta(NsSerializer.Meta): - fields = ('target',) + fields = ("target",) class MXRecordSerializer(MxSerializer): """Serialize `machines.models.Mx` objects with the data needed to generate a MX DNS record. """ - target = serializers.CharField(source='name', read_only=True) + + target = serializers.CharField(source="name", read_only=True) class Meta(MxSerializer.Meta): - fields = ('target', 'priority') + fields = ("target", "priority") class TXTRecordSerializer(TxtSerializer): @@ -943,17 +1225,18 @@ class TXTRecordSerializer(TxtSerializer): """ class Meta(TxtSerializer.Meta): - fields = ('field1', 'field2') + fields = ("field1", "field2") class SRVRecordSerializer(SrvSerializer): """Serialize `machines.models.Srv` objects with the data needed to generate a SRV DNS record. """ - target = serializers.CharField(source='target.name', read_only=True) + + target = serializers.CharField(source="target.name", read_only=True) class Meta(SrvSerializer.Meta): - fields = ('service', 'protocole', 'ttl', 'priority', 'weight', 'port', 'target') + fields = ("service", "protocole", "ttl", "priority", "weight", "port", "target") class SSHFPRecordSerializer(SshFpSerializer): @@ -962,127 +1245,171 @@ class SSHFPRecordSerializer(SshFpSerializer): """ class Meta(SshFpSerializer.Meta): - fields = ('algo_id', 'hash') + fields = ("algo_id", "hash") class SSHFPInterfaceSerializer(serializers.ModelSerializer): """Serialize `machines.models.Domain` objects with the data needed to generate a CNAME DNS record. """ - hostname = serializers.CharField(source='domain.name', read_only=True) - sshfp = SSHFPRecordSerializer(source='machine.sshfp_set', many=True, read_only=True) + + hostname = serializers.CharField(source="domain.name", read_only=True) + sshfp = SSHFPRecordSerializer(source="machine.sshfp_set", many=True, read_only=True) class Meta: model = machines.Interface - fields = ('hostname', 'sshfp') + fields = ("hostname", "sshfp") class ARecordSerializer(serializers.ModelSerializer): """Serialize `machines.models.Interface` objects with the data needed to generate a A DNS record. """ - hostname = serializers.CharField(source='domain.name', read_only=True) - ipv4 = serializers.CharField(source='ipv4.ipv4', read_only=True) + + hostname = serializers.CharField(source="domain.name", read_only=True) + ipv4 = serializers.CharField(source="ipv4.ipv4", read_only=True) class Meta: model = machines.Interface - fields = ('hostname', 'ipv4') + fields = ("hostname", "ipv4") class AAAARecordSerializer(serializers.ModelSerializer): """Serialize `machines.models.Interface` objects with the data needed to generate a AAAA DNS record. """ - hostname = serializers.CharField(source='domain.name', read_only=True) + + hostname = serializers.CharField(source="domain.name", read_only=True) ipv6 = Ipv6ListSerializer(many=True, read_only=True) class Meta: model = machines.Interface - fields = ('hostname', 'ipv6') + fields = ("hostname", "ipv6") class CNAMERecordSerializer(serializers.ModelSerializer): """Serialize `machines.models.Domain` objects with the data needed to generate a CNAME DNS record. """ - alias = serializers.CharField(source='cname', read_only=True) - hostname = serializers.CharField(source='name', read_only=True) + + alias = serializers.CharField(source="cname", read_only=True) + hostname = serializers.CharField(source="name", read_only=True) class Meta: model = machines.Domain - fields = ('alias', 'hostname') + fields = ("alias", "hostname") + class DNAMERecordSerializer(serializers.ModelSerializer): """Serialize `machines.models.Domain` objects with the data needed to generate a DNAME DNS record. """ + alias = serializers.CharField(read_only=True) zone = serializers.CharField(read_only=True) class Meta: model = machines.DName - fields = ('alias', 'zone') + fields = ("alias", "zone") class DNSZonesSerializer(serializers.ModelSerializer): """Serialize the data about DNS Zones. """ + soa = SOARecordSerializer() - ns_records = NSRecordSerializer(many=True, source='ns_set') - originv4 = OriginV4RecordSerializer(source='origin') - originv6 = serializers.CharField(source='origin_v6') - mx_records = MXRecordSerializer(many=True, source='mx_set') - txt_records = TXTRecordSerializer(many=True, source='txt_set') - srv_records = SRVRecordSerializer(many=True, source='srv_set') - a_records = ARecordSerializer(many=True, source='get_associated_a_records') - aaaa_records = AAAARecordSerializer(many=True, source='get_associated_aaaa_records') - cname_records = CNAMERecordSerializer(many=True, source='get_associated_cname_records') - dname_records = DNAMERecordSerializer(many=True, source='get_associated_dname_records') - sshfp_records = SSHFPInterfaceSerializer(many=True, source='get_associated_sshfp_records') + ns_records = NSRecordSerializer(many=True, source="ns_set") + originv4 = OriginV4RecordSerializer(source="origin") + originv6 = serializers.CharField(source="origin_v6") + mx_records = MXRecordSerializer(many=True, source="mx_set") + txt_records = TXTRecordSerializer(many=True, source="txt_set") + srv_records = SRVRecordSerializer(many=True, source="srv_set") + a_records = ARecordSerializer(many=True, source="get_associated_a_records") + aaaa_records = AAAARecordSerializer(many=True, source="get_associated_aaaa_records") + cname_records = CNAMERecordSerializer( + many=True, source="get_associated_cname_records" + ) + dname_records = DNAMERecordSerializer( + many=True, source="get_associated_dname_records" + ) + sshfp_records = SSHFPInterfaceSerializer( + many=True, source="get_associated_sshfp_records" + ) class Meta: model = machines.Extension - fields = ('name', 'soa', 'ns_records', 'originv4', 'originv6', - 'mx_records', 'txt_records', 'srv_records', 'a_records', - 'aaaa_records', 'cname_records', 'dname_records', 'sshfp_records') -#REMINDER + fields = ( + "name", + "soa", + "ns_records", + "originv4", + "originv6", + "mx_records", + "txt_records", + "srv_records", + "a_records", + "aaaa_records", + "cname_records", + "dname_records", + "sshfp_records", + ) + + +# REMINDER class ReminderUsersSerializer(UserSerializer): """Serialize the data about a mailing member. """ + class Meta(UserSerializer.Meta): - fields = ('get_full_name', 'get_mail') + fields = ("get_full_name", "get_mail") class ReminderSerializer(serializers.ModelSerializer): """ Serialize the data about a reminder """ + users_to_remind = ReminderUsersSerializer(many=True) class Meta: model = preferences.Reminder - fields = ('days','message','users_to_remind') + fields = ("days", "message", "users_to_remind") class DNSReverseZonesSerializer(serializers.ModelSerializer): """Serialize the data about DNS Zones. """ - soa = SOARecordSerializer(source='extension.soa') - extension = serializers.CharField(source='extension.name', read_only=True) - cidrs = serializers.ListField(child=serializers.CharField(), source='ip_set_cidrs_as_str', read_only=True) - ns_records = NSRecordSerializer(many=True, source='extension.ns_set') - mx_records = MXRecordSerializer(many=True, source='extension.mx_set') - txt_records = TXTRecordSerializer(many=True, source='extension.txt_set') - ptr_records = ARecordSerializer(many=True, source='get_associated_ptr_records') - ptr_v6_records = AAAARecordSerializer(many=True, source='get_associated_ptr_v6_records') + + soa = SOARecordSerializer(source="extension.soa") + extension = serializers.CharField(source="extension.name", read_only=True) + cidrs = serializers.ListField( + child=serializers.CharField(), source="ip_set_cidrs_as_str", read_only=True + ) + ns_records = NSRecordSerializer(many=True, source="extension.ns_set") + mx_records = MXRecordSerializer(many=True, source="extension.mx_set") + txt_records = TXTRecordSerializer(many=True, source="extension.txt_set") + ptr_records = ARecordSerializer(many=True, source="get_associated_ptr_records") + ptr_v6_records = AAAARecordSerializer( + many=True, source="get_associated_ptr_v6_records" + ) class Meta: model = machines.IpType - fields = ('type', 'extension', 'soa', 'ns_records', 'mx_records', - 'txt_records', 'ptr_records', 'ptr_v6_records', 'cidrs', - 'prefix_v6', 'prefix_v6_length') + fields = ( + "name", + "extension", + "soa", + "ns_records", + "mx_records", + "txt_records", + "ptr_records", + "ptr_v6_records", + "cidrs", + "prefix_v6", + "prefix_v6_length", + ) # MAILING @@ -1093,14 +1420,15 @@ class MailingMemberSerializer(UserSerializer): """ class Meta(UserSerializer.Meta): - fields = ('name', 'pseudo', 'get_mail') + fields = ("name", "pseudo", "get_mail") class MailingSerializer(ClubSerializer): """Serialize the data about a mailing. """ + members = MailingMemberSerializer(many=True) - admins = MailingMemberSerializer(source='administrators', many=True) + admins = MailingMemberSerializer(source="administrators", many=True) class Meta(ClubSerializer.Meta): - fields = ('name', 'members', 'admins') + fields = ("name", "members", "admins") diff --git a/api/settings.py b/api/settings.py index 0ddf5d69..1435aee6 100644 --- a/api/settings.py +++ b/api/settings.py @@ -24,28 +24,24 @@ # RestFramework config for API REST_FRAMEWORK = { - 'URL_FIELD_NAME': 'api_url', - 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'api.authentication.ExpiringTokenAuthentication', - 'rest_framework.authentication.SessionAuthentication', + "URL_FIELD_NAME": "api_url", + "DEFAULT_AUTHENTICATION_CLASSES": ( + "api.authentication.ExpiringTokenAuthentication", + "rest_framework.authentication.SessionAuthentication", ), - 'DEFAULT_PERMISSION_CLASSES': ( - 'api.permissions.AutodetectACLPermission', - ), - 'DEFAULT_PAGINATION_CLASS': 'api.pagination.PageSizedPagination', - 'PAGE_SIZE': 100 + "DEFAULT_PERMISSION_CLASSES": ("api.permissions.AutodetectACLPermission",), + "DEFAULT_PAGINATION_CLASS": "api.pagination.PageSizedPagination", + "PAGE_SIZE": 100, } # API permission settings -API_CONTENT_TYPE_APP_LABEL = 'api' -API_CONTENT_TYPE_MODEL = 'api' -API_PERMISSION_NAME = 'Can use the API' -API_PERMISSION_CODENAME = 'use_api' +API_CONTENT_TYPE_APP_LABEL = "api" +API_CONTENT_TYPE_MODEL = "api" +API_PERMISSION_NAME = "Can use the API" +API_PERMISSION_CODENAME = "use_api" # Activate token authentication -API_APPS = ( - 'rest_framework.authtoken', -) +API_APPS = ("rest_framework.authtoken",) # The expiration time for an authentication token API_TOKEN_DURATION = 86400 # 24 hours diff --git a/api/tests.py b/api/tests.py index 5c244152..df0b7a7d 100644 --- a/api/tests.py +++ b/api/tests.py @@ -50,171 +50,170 @@ class APIEndpointsTestCase(APITestCase): superuser: A superuser (with all permissions) used for the tests and initialized at the beggining of this test case. """ - no_auth_endpoints = [ - '/api/' - ] + + no_auth_endpoints = ["/api/"] auth_no_perm_endpoints = [] auth_perm_endpoints = [ - '/api/cotisations/article/', - '/api/cotisations/article/1/', - '/api/cotisations/banque/', - '/api/cotisations/banque/1/', - '/api/cotisations/cotisation/', - '/api/cotisations/cotisation/1/', - '/api/cotisations/facture/', - '/api/cotisations/facture/1/', - '/api/cotisations/paiement/', - '/api/cotisations/paiement/1/', - '/api/cotisations/vente/', - '/api/cotisations/vente/1/', - '/api/machines/domain/', - '/api/machines/domain/1/', - '/api/machines/extension/', - '/api/machines/extension/1/', - '/api/machines/interface/', - '/api/machines/interface/1/', - '/api/machines/iplist/', - '/api/machines/iplist/1/', - '/api/machines/iptype/', - '/api/machines/iptype/1/', - '/api/machines/ipv6list/', - '/api/machines/ipv6list/1/', - '/api/machines/machine/', - '/api/machines/machine/1/', - '/api/machines/machinetype/', - '/api/machines/machinetype/1/', - '/api/machines/mx/', - '/api/machines/mx/1/', - '/api/machines/nas/', - '/api/machines/nas/1/', - '/api/machines/ns/', - '/api/machines/ns/1/', - '/api/machines/ouvertureportlist/', - '/api/machines/ouvertureportlist/1/', - '/api/machines/ouvertureport/', - '/api/machines/ouvertureport/1/', - '/api/machines/servicelink/', - '/api/machines/servicelink/1/', - '/api/machines/service/', - '/api/machines/service/1/', - '/api/machines/soa/', - '/api/machines/soa/1/', - '/api/machines/srv/', - '/api/machines/srv/1/', - '/api/machines/txt/', - '/api/machines/txt/1/', - '/api/machines/vlan/', - '/api/machines/vlan/1/', - '/api/preferences/optionaluser/', - '/api/preferences/optionalmachine/', - '/api/preferences/optionaltopologie/', - '/api/preferences/generaloption/', - '/api/preferences/service/', - '/api/preferences/service/1/', - '/api/preferences/assooption/', - '/api/preferences/homeoption/', - '/api/preferences/mailmessageoption/', - '/api/topologie/acesspoint/', + "/api/cotisations/article/", + "/api/cotisations/article/1/", + "/api/cotisations/banque/", + "/api/cotisations/banque/1/", + "/api/cotisations/cotisation/", + "/api/cotisations/cotisation/1/", + "/api/cotisations/facture/", + "/api/cotisations/facture/1/", + "/api/cotisations/paiement/", + "/api/cotisations/paiement/1/", + "/api/cotisations/vente/", + "/api/cotisations/vente/1/", + "/api/machines/domain/", + "/api/machines/domain/1/", + "/api/machines/extension/", + "/api/machines/extension/1/", + "/api/machines/interface/", + "/api/machines/interface/1/", + "/api/machines/iplist/", + "/api/machines/iplist/1/", + "/api/machines/iptype/", + "/api/machines/iptype/1/", + "/api/machines/ipv6list/", + "/api/machines/ipv6list/1/", + "/api/machines/machine/", + "/api/machines/machine/1/", + "/api/machines/machinetype/", + "/api/machines/machinetype/1/", + "/api/machines/mx/", + "/api/machines/mx/1/", + "/api/machines/nas/", + "/api/machines/nas/1/", + "/api/machines/ns/", + "/api/machines/ns/1/", + "/api/machines/ouvertureportlist/", + "/api/machines/ouvertureportlist/1/", + "/api/machines/ouvertureport/", + "/api/machines/ouvertureport/1/", + "/api/machines/servicelink/", + "/api/machines/servicelink/1/", + "/api/machines/service/", + "/api/machines/service/1/", + "/api/machines/soa/", + "/api/machines/soa/1/", + "/api/machines/srv/", + "/api/machines/srv/1/", + "/api/machines/txt/", + "/api/machines/txt/1/", + "/api/machines/vlan/", + "/api/machines/vlan/1/", + "/api/preferences/optionaluser/", + "/api/preferences/optionalmachine/", + "/api/preferences/optionaltopologie/", + "/api/preferences/generaloption/", + "/api/preferences/service/", + "/api/preferences/service/1/", + "/api/preferences/assooption/", + "/api/preferences/homeoption/", + "/api/preferences/mailmessageoption/", + "/api/topologie/acesspoint/", # 2nd machine to be create (machines_machine_1, topologie_accesspoint_1) - '/api/topologie/acesspoint/2/', - '/api/topologie/building/', - '/api/topologie/building/1/', - '/api/topologie/constructorswitch/', - '/api/topologie/constructorswitch/1/', - '/api/topologie/modelswitch/', - '/api/topologie/modelswitch/1/', - '/api/topologie/room/', - '/api/topologie/room/1/', - '/api/topologie/server/', + "/api/topologie/acesspoint/2/", + "/api/topologie/building/", + "/api/topologie/building/1/", + "/api/topologie/constructorswitch/", + "/api/topologie/constructorswitch/1/", + "/api/topologie/modelswitch/", + "/api/topologie/modelswitch/1/", + "/api/topologie/room/", + "/api/topologie/room/1/", + "/api/topologie/server/", # 3rd machine to be create (machines_machine_1, topologie_accesspoint_1, # topologie_server_1) - '/api/topologie/server/3/', - '/api/topologie/stack/', - '/api/topologie/stack/1/', - '/api/topologie/switch/', + "/api/topologie/server/3/", + "/api/topologie/stack/", + "/api/topologie/stack/1/", + "/api/topologie/switch/", # 4th machine to be create (machines_machine_1, topologie_accesspoint_1, # topologie_server_1, topologie_switch_1) - '/api/topologie/switch/4/', - '/api/topologie/switchbay/', - '/api/topologie/switchbay/1/', - '/api/topologie/switchport/', - '/api/topologie/switchport/1/', - '/api/topologie/switchport/2/', - '/api/topologie/switchport/3/', - '/api/users/adherent/', + "/api/topologie/switch/4/", + "/api/topologie/switchbay/", + "/api/topologie/switchbay/1/", + "/api/topologie/switchport/", + "/api/topologie/switchport/1/", + "/api/topologie/switchport/2/", + "/api/topologie/switchport/3/", + "/api/users/adherent/", # 3rd user to be create (stduser, superuser, users_adherent_1) - '/api/users/adherent/3/', - '/api/users/ban/', - '/api/users/ban/1/', - '/api/users/club/', + "/api/users/adherent/3/", + "/api/users/ban/", + "/api/users/ban/1/", + "/api/users/club/", # 4th user to be create (stduser, superuser, users_adherent_1, # users_club_1) - '/api/users/club/4/', - '/api/users/listright/', + "/api/users/club/4/", + "/api/users/listright/", # TODO: Merge !145 # '/api/users/listright/1/', - '/api/users/school/', - '/api/users/school/1/', - '/api/users/serviceuser/', - '/api/users/serviceuser/1/', - '/api/users/shell/', - '/api/users/shell/1/', - '/api/users/user/', - '/api/users/user/1/', - '/api/users/whitelist/', - '/api/users/whitelist/1/', - '/api/dns/zones/', - '/api/dhcp/hostmacip/', - '/api/mailing/standard', - '/api/mailing/club', - '/api/services/regen/', + "/api/users/school/", + "/api/users/school/1/", + "/api/users/serviceuser/", + "/api/users/serviceuser/1/", + "/api/users/shell/", + "/api/users/shell/1/", + "/api/users/user/", + "/api/users/user/1/", + "/api/users/whitelist/", + "/api/users/whitelist/1/", + "/api/dns/zones/", + "/api/dhcp/hostmacip/", + "/api/mailing/standard", + "/api/mailing/club", + "/api/services/regen/", ] not_found_endpoints = [ - '/api/cotisations/article/4242/', - '/api/cotisations/banque/4242/', - '/api/cotisations/cotisation/4242/', - '/api/cotisations/facture/4242/', - '/api/cotisations/paiement/4242/', - '/api/cotisations/vente/4242/', - '/api/machines/domain/4242/', - '/api/machines/extension/4242/', - '/api/machines/interface/4242/', - '/api/machines/iplist/4242/', - '/api/machines/iptype/4242/', - '/api/machines/ipv6list/4242/', - '/api/machines/machine/4242/', - '/api/machines/machinetype/4242/', - '/api/machines/mx/4242/', - '/api/machines/nas/4242/', - '/api/machines/ns/4242/', - '/api/machines/ouvertureportlist/4242/', - '/api/machines/ouvertureport/4242/', - '/api/machines/servicelink/4242/', - '/api/machines/service/4242/', - '/api/machines/soa/4242/', - '/api/machines/srv/4242/', - '/api/machines/txt/4242/', - '/api/machines/vlan/4242/', - '/api/preferences/service/4242/', - '/api/topologie/acesspoint/4242/', - '/api/topologie/building/4242/', - '/api/topologie/constructorswitch/4242/', - '/api/topologie/modelswitch/4242/', - '/api/topologie/room/4242/', - '/api/topologie/server/4242/', - '/api/topologie/stack/4242/', - '/api/topologie/switch/4242/', - '/api/topologie/switchbay/4242/', - '/api/topologie/switchport/4242/', - '/api/users/adherent/4242/', - '/api/users/ban/4242/', - '/api/users/club/4242/', - '/api/users/listright/4242/', - '/api/users/school/4242/', - '/api/users/serviceuser/4242/', - '/api/users/shell/4242/', - '/api/users/user/4242/', - '/api/users/whitelist/4242/', + "/api/cotisations/article/4242/", + "/api/cotisations/banque/4242/", + "/api/cotisations/cotisation/4242/", + "/api/cotisations/facture/4242/", + "/api/cotisations/paiement/4242/", + "/api/cotisations/vente/4242/", + "/api/machines/domain/4242/", + "/api/machines/extension/4242/", + "/api/machines/interface/4242/", + "/api/machines/iplist/4242/", + "/api/machines/iptype/4242/", + "/api/machines/ipv6list/4242/", + "/api/machines/machine/4242/", + "/api/machines/machinetype/4242/", + "/api/machines/mx/4242/", + "/api/machines/nas/4242/", + "/api/machines/ns/4242/", + "/api/machines/ouvertureportlist/4242/", + "/api/machines/ouvertureport/4242/", + "/api/machines/servicelink/4242/", + "/api/machines/service/4242/", + "/api/machines/soa/4242/", + "/api/machines/srv/4242/", + "/api/machines/txt/4242/", + "/api/machines/vlan/4242/", + "/api/preferences/service/4242/", + "/api/topologie/acesspoint/4242/", + "/api/topologie/building/4242/", + "/api/topologie/constructorswitch/4242/", + "/api/topologie/modelswitch/4242/", + "/api/topologie/room/4242/", + "/api/topologie/server/4242/", + "/api/topologie/stack/4242/", + "/api/topologie/switch/4242/", + "/api/topologie/switchbay/4242/", + "/api/topologie/switchport/4242/", + "/api/users/adherent/4242/", + "/api/users/ban/4242/", + "/api/users/club/4242/", + "/api/users/listright/4242/", + "/api/users/school/4242/", + "/api/users/serviceuser/4242/", + "/api/users/shell/4242/", + "/api/users/user/4242/", + "/api/users/whitelist/4242/", ] stduser = None @@ -232,26 +231,18 @@ class APIEndpointsTestCase(APITestCase): # A user with no rights cls.stduser = users.User.objects.create_user( - "apistduser", - "apistduser", - "apistduser@example.net", - "apistduser" + "apistduser", "apistduser", "apistduser@example.net", "apistduser" ) # A user with all the rights cls.superuser = users.User.objects.create_superuser( - "apisuperuser", - "apisuperuser", - "apisuperuser@example.net", - "apisuperuser" + "apisuperuser", "apisuperuser", "apisuperuser@example.net", "apisuperuser" ) # Creates 1 instance for each object so the "details" endpoints # can be tested too. Objects need to be created in the right order. # Dependencies (relatedFields, ...) are highlighted by a comment at # the end of the concerned line (# Dep ). - cls.users_school_1 = users.School.objects.create( - name="users_school_1" - ) + cls.users_school_1 = users.School.objects.create(name="users_school_1") cls.users_school_1.save() cls.users_listshell_1 = users.ListShell.objects.create( shell="users_listshell_1" @@ -270,7 +261,7 @@ class APIEndpointsTestCase(APITestCase): registered=datetime.datetime.now(datetime.timezone.utc), telephone="0123456789", uid_number=21102, - rezo_rez_uid=21102 + rezo_rez_uid=21102, ) cls.users_user_1 = cls.users_adherent_1 cls.cotisations_article_1 = cotisations.Article.objects.create( @@ -278,14 +269,14 @@ class APIEndpointsTestCase(APITestCase): prix=10, duration=1, type_user=cotisations.Article.USER_TYPES[0][0], - type_cotisation=cotisations.Article.COTISATION_TYPE[0][0] + type_cotisation=cotisations.Article.COTISATION_TYPE[0][0], ) cls.cotisations_banque_1 = cotisations.Banque.objects.create( name="cotisations_banque_1" ) cls.cotisations_paiement_1 = cotisations.Paiement.objects.create( moyen="cotisations_paiement_1", - type_paiement=cotisations.Paiement.PAYMENT_TYPES[0][0] + type_paiement=cotisations.Paiement.PAYMENT_TYPES[0][0], ) cls.cotisations_facture_1 = cotisations.Facture.objects.create( user=cls.users_user_1, # Dep users.User @@ -294,7 +285,7 @@ class APIEndpointsTestCase(APITestCase): cheque="1234567890", date=datetime.datetime.now(datetime.timezone.utc), valid=True, - control=False + control=False, ) cls.cotisations_vente_1 = cotisations.Vente.objects.create( facture=cls.cotisations_facture_1, # Dep cotisations.Facture @@ -302,18 +293,18 @@ class APIEndpointsTestCase(APITestCase): name="cotisations_vente_1", prix=10, duration=1, - type_cotisation=cotisations.Vente.COTISATION_TYPE[0][0] + type_cotisation=cotisations.Vente.COTISATION_TYPE[0][0], ) # A cotisation is automatically created by the Vente object and # trying to create another cotisation associated with this vente # will fail so we simply retrieve it so it can be used in the tests cls.cotisations_cotisation_1 = cotisations.Cotisation.objects.get( - vente=cls.cotisations_vente_1, # Dep cotisations.Vente + vente=cls.cotisations_vente_1 # Dep cotisations.Vente ) cls.machines_machine_1 = machines.Machine.objects.create( user=cls.users_user_1, # Dep users.User name="machines_machine_1", - active=True + active=True, ) cls.machines_ouvertureportlist_1 = machines.OuverturePortList.objects.create( name="machines_ouvertureportlist_1" @@ -324,19 +315,17 @@ class APIEndpointsTestCase(APITestCase): refresh=86400, retry=7200, expire=3600000, - ttl=172800 + ttl=172800, ) cls.machines_extension_1 = machines.Extension.objects.create( name="machines_extension_1", need_infra=False, # Do not set origin because of circular dependency origin_v6="2001:db8:1234::", - soa=cls.machines_soa_1 # Dep machines.SOA + soa=cls.machines_soa_1, # Dep machines.SOA ) cls.machines_vlan_1 = machines.Vlan.objects.create( - vlan_id=0, - name="machines_vlan_1", - comment="machines Vlan 1" + vlan_id=0, name="machines_vlan_1", comment="machines Vlan 1" ) cls.machines_iptype_1 = machines.IpType.objects.create( type="machines_iptype_1", @@ -346,13 +335,12 @@ class APIEndpointsTestCase(APITestCase): domaine_ip_stop="10.0.0.255", prefix_v6="2001:db8:1234::", vlan=cls.machines_vlan_1, # Dep machines.Vlan - ouverture_ports=cls.machines_ouvertureportlist_1 # Dep machines.OuverturePortList + ouverture_ports=cls.machines_ouvertureportlist_1, # Dep machines.OuverturePortList ) # All IPs in the IpType range are autocreated so we can't create # new ones and thus we only retrieve it if needed in the tests cls.machines_iplist_1 = machines.IpList.objects.get( - ipv4="10.0.0.1", - ip_type=cls.machines_iptype_1, # Dep machines.IpType + ipv4="10.0.0.1", ip_type=cls.machines_iptype_1 # Dep machines.IpType ) cls.machines_machinetype_1 = machines.MachineType.objects.create( type="machines_machinetype_1", @@ -375,16 +363,16 @@ class APIEndpointsTestCase(APITestCase): cls.machines_mx_1 = machines.Mx.objects.create( zone=cls.machines_extension_1, # Dep machines.Extension priority=10, - name=cls.machines_domain_1 # Dep machines.Domain + name=cls.machines_domain_1, # Dep machines.Domain ) cls.machines_ns_1 = machines.Ns.objects.create( zone=cls.machines_extension_1, # Dep machines.Extension - ns=cls.machines_domain_1 # Dep machines.Domain + ns=cls.machines_domain_1, # Dep machines.Domain ) cls.machines_txt_1 = machines.Txt.objects.create( zone=cls.machines_extension_1, # Dep machines.Extension field1="machines_txt_1", - field2="machies Txt 1" + field2="machies Txt 1", ) cls.machines_srv_1 = machines.Srv.objects.create( service="machines_srv_1", @@ -398,7 +386,7 @@ class APIEndpointsTestCase(APITestCase): cls.machines_ipv6list_1 = machines.Ipv6List.objects.create( ipv6="2001:db8:1234::", interface=cls.machines_interface_1, # Dep machines.Interface - slaac_ip=False + slaac_ip=False, ) cls.machines_service_1 = machines.Service.objects.create( service_type="machines_service_1", @@ -410,45 +398,45 @@ class APIEndpointsTestCase(APITestCase): service=cls.machines_service_1, # Dep machines.Service server=cls.machines_interface_1, # Dep machines.Interface last_regen=datetime.datetime.now(datetime.timezone.utc), - asked_regen=False + asked_regen=False, ) cls.machines_ouvertureport_1 = machines.OuverturePort.objects.create( begin=1, end=2, port_list=cls.machines_ouvertureportlist_1, # Dep machines.OuverturePortList protocole=machines.OuverturePort.TCP, - io=machines.OuverturePort.OUT + io=machines.OuverturePort.OUT, ) cls.machines_nas_1 = machines.Nas.objects.create( name="machines_nas_1", nas_type=cls.machines_machinetype_1, # Dep machines.MachineType machine_type=cls.machines_machinetype_1, # Dep machines.MachineType port_access_mode=machines.Nas.AUTH[0][0], - autocapture_mac=False + autocapture_mac=False, ) cls.preferences_service_1 = preferences.Service.objects.create( name="preferences_service_1", url="https://example.net", description="preferences Service 1", - image="/media/logo/none.png" + image="/media/logo/none.png", ) cls.topologie_stack_1 = topologie.Stack.objects.create( name="topologie_stack_1", stack_id="1", details="topologie Stack 1", member_id_min=1, - member_id_max=10 + member_id_max=10, ) cls.topologie_accespoint_1 = topologie.AccessPoint.objects.create( user=cls.users_user_1, # Dep users.User name="machines_machine_1", active=True, - location="topologie AccessPoint 1" + location="topologie AccessPoint 1", ) cls.topologie_server_1 = topologie.Server.objects.create( user=cls.users_user_1, # Dep users.User name="machines_machine_1", - active=True + active=True, ) cls.topologie_building_1 = topologie.Building.objects.create( name="topologie_building_1" @@ -456,14 +444,14 @@ class APIEndpointsTestCase(APITestCase): cls.topologie_switchbay_1 = topologie.SwitchBay.objects.create( name="topologie_switchbay_1", building=cls.topologie_building_1, # Dep topologie.Building - info="topologie SwitchBay 1" + info="topologie SwitchBay 1", ) cls.topologie_constructorswitch_1 = topologie.ConstructorSwitch.objects.create( name="topologie_constructorswitch_1" ) cls.topologie_modelswitch_1 = topologie.ModelSwitch.objects.create( reference="topologie_modelswitch_1", - constructor=cls.topologie_constructorswitch_1 # Dep topologie.ConstructorSwitch + constructor=cls.topologie_constructorswitch_1, # Dep topologie.ConstructorSwitch ) cls.topologie_switch_1 = topologie.Switch.objects.create( user=cls.users_user_1, # Dep users.User @@ -473,11 +461,10 @@ class APIEndpointsTestCase(APITestCase): stack=cls.topologie_stack_1, # Dep topologie.Stack stack_member_id=1, model=cls.topologie_modelswitch_1, # Dep topologie.ModelSwitch - switchbay=cls.topologie_switchbay_1 # Dep topologie.SwitchBay + switchbay=cls.topologie_switchbay_1, # Dep topologie.SwitchBay ) cls.topologie_room_1 = topologie.Room.objects.create( - name="topologie_romm_1", - details="topologie Room 1" + name="topologie_romm_1", details="topologie Room 1" ) cls.topologie_port_1 = topologie.Port.objects.create( switch=cls.topologie_switch_1, # Dep topologie.Switch @@ -485,7 +472,7 @@ class APIEndpointsTestCase(APITestCase): room=cls.topologie_room_1, # Dep topologie.Room radius=topologie.Port.STATES[0][0], vlan_force=cls.machines_vlan_1, # Dep machines.Vlan - details="topologie_switch_1" + details="topologie_switch_1", ) cls.topologie_port_2 = topologie.Port.objects.create( switch=cls.topologie_switch_1, # Dep topologie.Switch @@ -493,7 +480,7 @@ class APIEndpointsTestCase(APITestCase): machine_interface=cls.machines_interface_1, # Dep machines.Interface radius=topologie.Port.STATES[0][0], vlan_force=cls.machines_vlan_1, # Dep machines.Vlan - details="topologie_switch_1" + details="topologie_switch_1", ) cls.topologie_port_3 = topologie.Port.objects.create( switch=cls.topologie_switch_1, # Dep topologie.Switch @@ -501,14 +488,15 @@ class APIEndpointsTestCase(APITestCase): room=cls.topologie_room_1, # Dep topologie.Room radius=topologie.Port.STATES[0][0], # Do not defines related because circular dependency # Dep machines.Vlan - details="topologie_switch_1" + details="topologie_switch_1", ) cls.users_ban_1 = users.Ban.objects.create( user=cls.users_user_1, # Dep users.User raison="users Ban 1", date_start=datetime.datetime.now(datetime.timezone.utc), - date_end=datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=1), - state=users.Ban.STATES[0][0] + date_end=datetime.datetime.now(datetime.timezone.utc) + + datetime.timedelta(days=1), + state=users.Ban.STATES[0][0], ) cls.users_club_1 = users.Club.objects.create( password="password", @@ -524,7 +512,7 @@ class APIEndpointsTestCase(APITestCase): registered=datetime.datetime.now(datetime.timezone.utc), telephone="0123456789", uid_number=21103, - rezo_rez_uid=21103 + rezo_rez_uid=21103, ) # Need merge of MR145 to work # TODO: Merge !145 @@ -539,17 +527,17 @@ class APIEndpointsTestCase(APITestCase): last_login=datetime.datetime.now(datetime.timezone.utc), pseudo="usersserviceuser1", access_group=users.ServiceUser.ACCESS[0][0], - comment="users ServiceUser 1" + comment="users ServiceUser 1", ) cls.users_whitelist_1 = users.Whitelist.objects.create( user=cls.users_user_1, raison="users Whitelist 1", date_start=datetime.datetime.now(datetime.timezone.utc), - date_end=datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=1) + date_end=datetime.datetime.now(datetime.timezone.utc) + + datetime.timedelta(days=1), ) - def check_responses_code(self, urls, expected_code, formats=None, - assert_more=None): + def check_responses_code(self, urls, expected_code, formats=None, assert_more=None): """Utility function to test if a list of urls answer an expected code. Args: @@ -665,17 +653,20 @@ class APIEndpointsTestCase(APITestCase): """ self.client.force_authenticate(user=self.superuser) - urls = self.no_auth_endpoints + self.auth_no_perm_endpoints + \ - self.auth_perm_endpoints + urls = ( + self.no_auth_endpoints + + self.auth_no_perm_endpoints + + self.auth_perm_endpoints + ) def assert_more(response, url, format): """Assert the response is valid json when format is json""" - if format is 'json': + if format is "json": json.loads(response.content.decode()) - self.check_responses_code(urls, codes.ok, - formats=[None, 'json', 'api'], - assert_more=assert_more) + self.check_responses_code( + urls, codes.ok, formats=[None, "json", "api"], assert_more=assert_more + ) class APIPaginationTestCase(APITestCase): @@ -688,56 +679,56 @@ class APIPaginationTestCase(APITestCase): """ endpoints = [ - '/api/cotisations/article/', - '/api/cotisations/banque/', - '/api/cotisations/cotisation/', - '/api/cotisations/facture/', - '/api/cotisations/paiement/', - '/api/cotisations/vente/', - '/api/machines/domain/', - '/api/machines/extension/', - '/api/machines/interface/', - '/api/machines/iplist/', - '/api/machines/iptype/', - '/api/machines/ipv6list/', - '/api/machines/machine/', - '/api/machines/machinetype/', - '/api/machines/mx/', - '/api/machines/nas/', - '/api/machines/ns/', - '/api/machines/ouvertureportlist/', - '/api/machines/ouvertureport/', - '/api/machines/servicelink/', - '/api/machines/service/', - '/api/machines/soa/', - '/api/machines/srv/', - '/api/machines/txt/', - '/api/machines/vlan/', - '/api/preferences/service/', - '/api/topologie/acesspoint/', - '/api/topologie/building/', - '/api/topologie/constructorswitch/', - '/api/topologie/modelswitch/', - '/api/topologie/room/', - '/api/topologie/server/', - '/api/topologie/stack/', - '/api/topologie/switch/', - '/api/topologie/switchbay/', - '/api/topologie/switchport/', - '/api/users/adherent/', - '/api/users/ban/', - '/api/users/club/', - '/api/users/listright/', - '/api/users/school/', - '/api/users/serviceuser/', - '/api/users/shell/', - '/api/users/user/', - '/api/users/whitelist/', - '/api/dns/zones/', - '/api/dhcp/hostmacip/', - '/api/mailing/standard', - '/api/mailing/club', - '/api/services/regen/', + "/api/cotisations/article/", + "/api/cotisations/banque/", + "/api/cotisations/cotisation/", + "/api/cotisations/facture/", + "/api/cotisations/paiement/", + "/api/cotisations/vente/", + "/api/machines/domain/", + "/api/machines/extension/", + "/api/machines/interface/", + "/api/machines/iplist/", + "/api/machines/iptype/", + "/api/machines/ipv6list/", + "/api/machines/machine/", + "/api/machines/machinetype/", + "/api/machines/mx/", + "/api/machines/nas/", + "/api/machines/ns/", + "/api/machines/ouvertureportlist/", + "/api/machines/ouvertureport/", + "/api/machines/servicelink/", + "/api/machines/service/", + "/api/machines/soa/", + "/api/machines/srv/", + "/api/machines/txt/", + "/api/machines/vlan/", + "/api/preferences/service/", + "/api/topologie/acesspoint/", + "/api/topologie/building/", + "/api/topologie/constructorswitch/", + "/api/topologie/modelswitch/", + "/api/topologie/room/", + "/api/topologie/server/", + "/api/topologie/stack/", + "/api/topologie/switch/", + "/api/topologie/switchbay/", + "/api/topologie/switchport/", + "/api/users/adherent/", + "/api/users/ban/", + "/api/users/club/", + "/api/users/listright/", + "/api/users/school/", + "/api/users/serviceuser/", + "/api/users/shell/", + "/api/users/user/", + "/api/users/whitelist/", + "/api/dns/zones/", + "/api/dhcp/hostmacip/", + "/api/mailing/standard", + "/api/mailing/club", + "/api/services/regen/", ] superuser = None @@ -745,14 +736,14 @@ class APIPaginationTestCase(APITestCase): def setUpTestData(cls): # A user with all the rights # We need to use a different username than for the first - # test case because TestCase is using rollbacks which don't + # test case because TestCase is using rollbacks which don't # trigger the ldap_sync() thus the LDAP still have data about # the old users. cls.superuser = users.User.objects.create_superuser( "apisuperuser2", "apisuperuser2", "apisuperuser2@example.net", - "apisuperuser2" + "apisuperuser2", ) @classmethod @@ -771,10 +762,10 @@ class APIPaginationTestCase(APITestCase): self.client.force_authenticate(self.superuser) for url in self.endpoints: with self.subTest(url=url): - response = self.client.get(url, format='json') + response = self.client.get(url, format="json") res_json = json.loads(response.content.decode()) - assert 'count' in res_json.keys() - assert 'next' in res_json.keys() - assert 'previous' in res_json.keys() - assert 'results' in res_json.keys() - assert not len('results') > 100 + assert "count" in res_json.keys() + assert "next" in res_json.keys() + assert "previous" in res_json.keys() + assert "results" in res_json.keys() + assert not len("results") > 100 diff --git a/api/urls.py b/api/urls.py index 4a34c1de..6c2bd4c2 100644 --- a/api/urls.py +++ b/api/urls.py @@ -34,95 +34,109 @@ from .routers import AllViewsRouter router = AllViewsRouter() # COTISATIONS -router.register_viewset(r'cotisations/facture', views.FactureViewSet) -router.register_viewset(r'cotisations/vente', views.VenteViewSet) -router.register_viewset(r'cotisations/article', views.ArticleViewSet) -router.register_viewset(r'cotisations/banque', views.BanqueViewSet) -router.register_viewset(r'cotisations/paiement', views.PaiementViewSet) -router.register_viewset(r'cotisations/cotisation', views.CotisationViewSet) +router.register_viewset(r"cotisations/facture", views.FactureViewSet) +router.register_viewset(r"cotisations/vente", views.VenteViewSet) +router.register_viewset(r"cotisations/article", views.ArticleViewSet) +router.register_viewset(r"cotisations/banque", views.BanqueViewSet) +router.register_viewset(r"cotisations/paiement", views.PaiementViewSet) +router.register_viewset(r"cotisations/cotisation", views.CotisationViewSet) # MACHINES -router.register_viewset(r'machines/machine', views.MachineViewSet) -router.register_viewset(r'machines/machinetype', views.MachineTypeViewSet) -router.register_viewset(r'machines/iptype', views.IpTypeViewSet) -router.register_viewset(r'machines/vlan', views.VlanViewSet) -router.register_viewset(r'machines/nas', views.NasViewSet) -router.register_viewset(r'machines/soa', views.SOAViewSet) -router.register_viewset(r'machines/extension', views.ExtensionViewSet) -router.register_viewset(r'machines/mx', views.MxViewSet) -router.register_viewset(r'machines/ns', views.NsViewSet) -router.register_viewset(r'machines/txt', views.TxtViewSet) -router.register_viewset(r'machines/dname', views.DNameViewSet) -router.register_viewset(r'machines/srv', views.SrvViewSet) -router.register_viewset(r'machines/sshfp', views.SshFpViewSet) -router.register_viewset(r'machines/interface', views.InterfaceViewSet) -router.register_viewset(r'machines/ipv6list', views.Ipv6ListViewSet) -router.register_viewset(r'machines/domain', views.DomainViewSet) -router.register_viewset(r'machines/iplist', views.IpListViewSet) -router.register_viewset(r'machines/service', views.ServiceViewSet) -router.register_viewset(r'machines/servicelink', views.ServiceLinkViewSet, base_name='servicelink') -router.register_viewset(r'machines/ouvertureportlist', views.OuverturePortListViewSet) -router.register_viewset(r'machines/ouvertureport', views.OuverturePortViewSet) -router.register_viewset(r'machines/role', views.RoleViewSet) +router.register_viewset(r"machines/machine", views.MachineViewSet) +router.register_viewset(r"machines/machinetype", views.MachineTypeViewSet) +router.register_viewset(r"machines/iptype", views.IpTypeViewSet) +router.register_viewset(r"machines/vlan", views.VlanViewSet) +router.register_viewset(r"machines/nas", views.NasViewSet) +router.register_viewset(r"machines/soa", views.SOAViewSet) +router.register_viewset(r"machines/extension", views.ExtensionViewSet) +router.register_viewset(r"machines/mx", views.MxViewSet) +router.register_viewset(r"machines/ns", views.NsViewSet) +router.register_viewset(r"machines/txt", views.TxtViewSet) +router.register_viewset(r"machines/dname", views.DNameViewSet) +router.register_viewset(r"machines/srv", views.SrvViewSet) +router.register_viewset(r"machines/sshfp", views.SshFpViewSet) +router.register_viewset(r"machines/interface", views.InterfaceViewSet) +router.register_viewset(r"machines/ipv6list", views.Ipv6ListViewSet) +router.register_viewset(r"machines/domain", views.DomainViewSet) +router.register_viewset(r"machines/iplist", views.IpListViewSet) +router.register_viewset(r"machines/service", views.ServiceViewSet) +router.register_viewset( + r"machines/servicelink", views.ServiceLinkViewSet, base_name="servicelink" +) +router.register_viewset(r"machines/ouvertureportlist", views.OuverturePortListViewSet) +router.register_viewset(r"machines/ouvertureport", views.OuverturePortViewSet) +router.register_viewset(r"machines/role", views.RoleViewSet) # PREFERENCES -router.register_view(r'preferences/optionaluser', views.OptionalUserView), -router.register_view(r'preferences/optionalmachine', views.OptionalMachineView), -router.register_view(r'preferences/optionaltopologie', views.OptionalTopologieView), -router.register_view(r'preferences/radiusoption', views.RadiusOptionView), -router.register_view(r'preferences/generaloption', views.GeneralOptionView), -router.register_viewset(r'preferences/service', views.HomeServiceViewSet, base_name='homeservice'), -router.register_view(r'preferences/assooption', views.AssoOptionView), -router.register_view(r'preferences/homeoption', views.HomeOptionView), -router.register_view(r'preferences/mailmessageoption', views.MailMessageOptionView), +router.register_view(r"preferences/optionaluser", views.OptionalUserView), +router.register_view(r"preferences/optionalmachine", views.OptionalMachineView), +router.register_view(r"preferences/optionaltopologie", views.OptionalTopologieView), +router.register_view(r"preferences/radiusoption", views.RadiusOptionView), +router.register_view(r"preferences/generaloption", views.GeneralOptionView), +router.register_viewset( + r"preferences/service", views.HomeServiceViewSet, base_name="homeservice" +), +router.register_view(r"preferences/assooption", views.AssoOptionView), +router.register_view(r"preferences/homeoption", views.HomeOptionView), +router.register_view(r"preferences/mailmessageoption", views.MailMessageOptionView), # TOPOLOGIE -router.register_viewset(r'topologie/stack', views.StackViewSet) -router.register_viewset(r'topologie/acesspoint', views.AccessPointViewSet) -router.register_viewset(r'topologie/switch', views.SwitchViewSet) -router.register_viewset(r'topologie/server', views.ServerViewSet) -router.register_viewset(r'topologie/modelswitch', views.ModelSwitchViewSet) -router.register_viewset(r'topologie/constructorswitch', views.ConstructorSwitchViewSet) -router.register_viewset(r'topologie/switchbay', views.SwitchBayViewSet) -router.register_viewset(r'topologie/building', views.BuildingViewSet) -router.register_viewset(r'topologie/switchport', views.SwitchPortViewSet, base_name='switchport') -router.register_viewset(r'topologie/portprofile', views.PortProfileViewSet, base_name='portprofile') -router.register_viewset(r'topologie/room', views.RoomViewSet) -router.register(r'topologie/portprofile', views.PortProfileViewSet) +router.register_viewset(r"topologie/stack", views.StackViewSet) +router.register_viewset(r"topologie/acesspoint", views.AccessPointViewSet) +router.register_viewset(r"topologie/switch", views.SwitchViewSet) +router.register_viewset(r"topologie/server", views.ServerViewSet) +router.register_viewset(r"topologie/modelswitch", views.ModelSwitchViewSet) +router.register_viewset(r"topologie/constructorswitch", views.ConstructorSwitchViewSet) +router.register_viewset(r"topologie/switchbay", views.SwitchBayViewSet) +router.register_viewset(r"topologie/building", views.BuildingViewSet) +router.register_viewset( + r"topologie/switchport", views.SwitchPortViewSet, base_name="switchport" +) +router.register_viewset( + r"topologie/portprofile", views.PortProfileViewSet, base_name="portprofile" +) +router.register_viewset(r"topologie/room", views.RoomViewSet) +router.register(r"topologie/portprofile", views.PortProfileViewSet) # USERS -router.register_viewset(r'users/user', views.UserViewSet, base_name='user') -router.register_viewset(r'users/homecreation', views.HomeCreationViewSet, base_name='homecreation') -router.register_viewset(r'users/normaluser', views.NormalUserViewSet, base_name='normaluser') -router.register_viewset(r'users/criticaluser', views.CriticalUserViewSet, base_name='criticaluser') -router.register_viewset(r'users/club', views.ClubViewSet) -router.register_viewset(r'users/adherent', views.AdherentViewSet) -router.register_viewset(r'users/serviceuser', views.ServiceUserViewSet) -router.register_viewset(r'users/school', views.SchoolViewSet) -router.register_viewset(r'users/listright', views.ListRightViewSet) -router.register_viewset(r'users/shell', views.ShellViewSet, base_name='shell') -router.register_viewset(r'users/ban', views.BanViewSet) -router.register_viewset(r'users/whitelist', views.WhitelistViewSet) -router.register_viewset(r'users/emailaddress', views.EMailAddressViewSet) +router.register_viewset(r"users/user", views.UserViewSet, base_name="user") +router.register_viewset( + r"users/homecreation", views.HomeCreationViewSet, base_name="homecreation" +) +router.register_viewset( + r"users/normaluser", views.NormalUserViewSet, base_name="normaluser" +) +router.register_viewset( + r"users/criticaluser", views.CriticalUserViewSet, base_name="criticaluser" +) +router.register_viewset(r"users/club", views.ClubViewSet) +router.register_viewset(r"users/adherent", views.AdherentViewSet) +router.register_viewset(r"users/serviceuser", views.ServiceUserViewSet) +router.register_viewset(r"users/school", views.SchoolViewSet) +router.register_viewset(r"users/listright", views.ListRightViewSet) +router.register_viewset(r"users/shell", views.ShellViewSet, base_name="shell") +router.register_viewset(r"users/ban", views.BanViewSet) +router.register_viewset(r"users/whitelist", views.WhitelistViewSet) +router.register_viewset(r"users/emailaddress", views.EMailAddressViewSet) # SERVICE REGEN -router.register_viewset(r'services/regen', views.ServiceRegenViewSet, base_name='serviceregen') +router.register_viewset( + r"services/regen", views.ServiceRegenViewSet, base_name="serviceregen" +) # DHCP -router.register_view(r'dhcp/hostmacip', views.HostMacIpView), +router.register_view(r"dhcp/hostmacip", views.HostMacIpView), # LOCAL EMAILS -router.register_view(r'localemail/users', views.LocalEmailUsersView), +router.register_view(r"localemail/users", views.LocalEmailUsersView), # Firewall -router.register_view(r'firewall/subnet-ports', views.SubnetPortsOpenView), -router.register_view(r'firewall/interface-ports', views.InterfacePortsOpenView), +router.register_view(r"firewall/subnet-ports", views.SubnetPortsOpenView), +router.register_view(r"firewall/interface-ports", views.InterfacePortsOpenView), # Switches config -router.register_view(r'switchs/ports-config', views.SwitchPortView), -router.register_view(r'switchs/role', views.RoleView), +router.register_view(r"switchs/ports-config", views.SwitchPortView), +router.register_view(r"switchs/role", views.RoleView), # Reminder -router.register_view(r'reminder/get-users', views.ReminderView), +router.register_view(r"reminder/get-users", views.ReminderView), # DNS -router.register_view(r'dns/zones', views.DNSZonesView), -router.register_view(r'dns/reverse-zones', views.DNSReverseZonesView), +router.register_view(r"dns/zones", views.DNSZonesView), +router.register_view(r"dns/reverse-zones", views.DNSReverseZonesView), # MAILING -router.register_view(r'mailing/standard', views.StandardMailingView), -router.register_view(r'mailing/club', views.ClubMailingView), +router.register_view(r"mailing/standard", views.StandardMailingView), +router.register_view(r"mailing/club", views.ClubMailingView), # TOKEN AUTHENTICATION -router.register_view(r'token-auth', views.ObtainExpiringAuthToken) +router.register_view(r"token-auth", views.ObtainExpiringAuthToken) -urlpatterns = [ - url(r'^', include(router.urls)), -] +urlpatterns = [url(r"^", include(router.urls))] diff --git a/api/views.py b/api/views.py index 3108f9f3..b53ee974 100644 --- a/api/views.py +++ b/api/views.py @@ -52,12 +52,15 @@ from .permissions import ACLPermission class FactureViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `cotisations.models.Facture` objects. """ + queryset = cotisations.Facture.objects.all() serializer_class = serializers.FactureSerializer + class FactureViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `cotisations.models.Facture` objects. """ + queryset = cotisations.BaseInvoice.objects.all() serializer_class = serializers.BaseInvoiceSerializer @@ -65,6 +68,7 @@ class FactureViewSet(viewsets.ReadOnlyModelViewSet): class VenteViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `cotisations.models.Vente` objects. """ + queryset = cotisations.Vente.objects.all() serializer_class = serializers.VenteSerializer @@ -72,6 +76,7 @@ class VenteViewSet(viewsets.ReadOnlyModelViewSet): class ArticleViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `cotisations.models.Article` objects. """ + queryset = cotisations.Article.objects.all() serializer_class = serializers.ArticleSerializer @@ -79,6 +84,7 @@ class ArticleViewSet(viewsets.ReadOnlyModelViewSet): class BanqueViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `cotisations.models.Banque` objects. """ + queryset = cotisations.Banque.objects.all() serializer_class = serializers.BanqueSerializer @@ -86,6 +92,7 @@ class BanqueViewSet(viewsets.ReadOnlyModelViewSet): class PaiementViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `cotisations.models.Paiement` objects. """ + queryset = cotisations.Paiement.objects.all() serializer_class = serializers.PaiementSerializer @@ -93,6 +100,7 @@ class PaiementViewSet(viewsets.ReadOnlyModelViewSet): class CotisationViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `cotisations.models.Cotisation` objects. """ + queryset = cotisations.Cotisation.objects.all() serializer_class = serializers.CotisationSerializer @@ -103,6 +111,7 @@ class CotisationViewSet(viewsets.ReadOnlyModelViewSet): class MachineViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.Machine` objects. """ + queryset = machines.Machine.objects.all() serializer_class = serializers.MachineSerializer @@ -110,6 +119,7 @@ class MachineViewSet(viewsets.ReadOnlyModelViewSet): class MachineTypeViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.MachineType` objects. """ + queryset = machines.MachineType.objects.all() serializer_class = serializers.MachineTypeSerializer @@ -117,6 +127,7 @@ class MachineTypeViewSet(viewsets.ReadOnlyModelViewSet): class IpTypeViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.IpType` objects. """ + queryset = machines.IpType.objects.all() serializer_class = serializers.IpTypeSerializer @@ -124,6 +135,7 @@ class IpTypeViewSet(viewsets.ReadOnlyModelViewSet): class VlanViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.Vlan` objects. """ + queryset = machines.Vlan.objects.all() serializer_class = serializers.VlanSerializer @@ -131,6 +143,7 @@ class VlanViewSet(viewsets.ReadOnlyModelViewSet): class NasViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.Nas` objects. """ + queryset = machines.Nas.objects.all() serializer_class = serializers.NasSerializer @@ -138,6 +151,7 @@ class NasViewSet(viewsets.ReadOnlyModelViewSet): class SOAViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.SOA` objects. """ + queryset = machines.SOA.objects.all() serializer_class = serializers.SOASerializer @@ -145,6 +159,7 @@ class SOAViewSet(viewsets.ReadOnlyModelViewSet): class ExtensionViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.Extension` objects. """ + queryset = machines.Extension.objects.all() serializer_class = serializers.ExtensionSerializer @@ -152,6 +167,7 @@ class ExtensionViewSet(viewsets.ReadOnlyModelViewSet): class MxViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.Mx` objects. """ + queryset = machines.Mx.objects.all() serializer_class = serializers.MxSerializer @@ -159,6 +175,7 @@ class MxViewSet(viewsets.ReadOnlyModelViewSet): class NsViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.Ns` objects. """ + queryset = machines.Ns.objects.all() serializer_class = serializers.NsSerializer @@ -166,6 +183,7 @@ class NsViewSet(viewsets.ReadOnlyModelViewSet): class TxtViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.Txt` objects. """ + queryset = machines.Txt.objects.all() serializer_class = serializers.TxtSerializer @@ -173,6 +191,7 @@ class TxtViewSet(viewsets.ReadOnlyModelViewSet): class DNameViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.DName` objects. """ + queryset = machines.DName.objects.all() serializer_class = serializers.DNameSerializer @@ -180,6 +199,7 @@ class DNameViewSet(viewsets.ReadOnlyModelViewSet): class SrvViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.Srv` objects. """ + queryset = machines.Srv.objects.all() serializer_class = serializers.SrvSerializer @@ -187,6 +207,7 @@ class SrvViewSet(viewsets.ReadOnlyModelViewSet): class SshFpViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.SshFp` objects. """ + queryset = machines.SshFp.objects.all() serializer_class = serializers.SshFpSerializer @@ -194,6 +215,7 @@ class SshFpViewSet(viewsets.ReadOnlyModelViewSet): class InterfaceViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.Interface` objects. """ + queryset = machines.Interface.objects.all() serializer_class = serializers.InterfaceSerializer @@ -201,6 +223,7 @@ class InterfaceViewSet(viewsets.ReadOnlyModelViewSet): class Ipv6ListViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.Ipv6List` objects. """ + queryset = machines.Ipv6List.objects.all() serializer_class = serializers.Ipv6ListSerializer @@ -208,6 +231,7 @@ class Ipv6ListViewSet(viewsets.ReadOnlyModelViewSet): class DomainViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.Domain` objects. """ + queryset = machines.Domain.objects.all() serializer_class = serializers.DomainSerializer @@ -215,6 +239,7 @@ class DomainViewSet(viewsets.ReadOnlyModelViewSet): class IpListViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.IpList` objects. """ + queryset = machines.IpList.objects.all() serializer_class = serializers.IpListSerializer @@ -222,6 +247,7 @@ class IpListViewSet(viewsets.ReadOnlyModelViewSet): class ServiceViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.Service` objects. """ + queryset = machines.Service.objects.all() serializer_class = serializers.ServiceSerializer @@ -229,6 +255,7 @@ class ServiceViewSet(viewsets.ReadOnlyModelViewSet): class ServiceLinkViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.Service_link` objects. """ + queryset = machines.Service_link.objects.all() serializer_class = serializers.ServiceLinkSerializer @@ -237,6 +264,7 @@ class OuverturePortListViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.OuverturePortList` objects. """ + queryset = machines.OuverturePortList.objects.all() serializer_class = serializers.OuverturePortListSerializer @@ -244,6 +272,7 @@ class OuverturePortListViewSet(viewsets.ReadOnlyModelViewSet): class OuverturePortViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.OuverturePort` objects. """ + queryset = machines.OuverturePort.objects.all() serializer_class = serializers.OuverturePortSerializer @@ -251,6 +280,7 @@ class OuverturePortViewSet(viewsets.ReadOnlyModelViewSet): class RoleViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `machines.models.Machine` objects. """ + queryset = machines.Role.objects.all() serializer_class = serializers.RoleSerializer @@ -259,11 +289,13 @@ class RoleViewSet(viewsets.ReadOnlyModelViewSet): # Those views differ a bit because there is only one object # to display, so we don't bother with the listing part + class OptionalUserView(generics.RetrieveAPIView): """Exposes details of `preferences.models.` settings. """ + permission_classes = (ACLPermission,) - perms_map = {'GET': [preferences.OptionalUser.can_view_all]} + perms_map = {"GET": [preferences.OptionalUser.can_view_all]} serializer_class = serializers.OptionalUserSerializer def get_object(self): @@ -273,8 +305,9 @@ class OptionalUserView(generics.RetrieveAPIView): class OptionalMachineView(generics.RetrieveAPIView): """Exposes details of `preferences.models.OptionalMachine` settings. """ + permission_classes = (ACLPermission,) - perms_map = {'GET': [preferences.OptionalMachine.can_view_all]} + perms_map = {"GET": [preferences.OptionalMachine.can_view_all]} serializer_class = serializers.OptionalMachineSerializer def get_object(self): @@ -284,8 +317,9 @@ class OptionalMachineView(generics.RetrieveAPIView): class OptionalTopologieView(generics.RetrieveAPIView): """Exposes details of `preferences.models.OptionalTopologie` settings. """ + permission_classes = (ACLPermission,) - perms_map = {'GET': [preferences.OptionalTopologie.can_view_all]} + perms_map = {"GET": [preferences.OptionalTopologie.can_view_all]} serializer_class = serializers.OptionalTopologieSerializer def get_object(self): @@ -295,8 +329,9 @@ class OptionalTopologieView(generics.RetrieveAPIView): class RadiusOptionView(generics.RetrieveAPIView): """Exposes details of `preferences.models.OptionalTopologie` settings. """ + permission_classes = (ACLPermission,) - perms_map = {'GET': [preferences.RadiusOption.can_view_all]} + perms_map = {"GET": [preferences.RadiusOption.can_view_all]} serializer_class = serializers.RadiusOptionSerializer def get_object(self): @@ -306,8 +341,9 @@ class RadiusOptionView(generics.RetrieveAPIView): class GeneralOptionView(generics.RetrieveAPIView): """Exposes details of `preferences.models.GeneralOption` settings. """ + permission_classes = (ACLPermission,) - perms_map = {'GET': [preferences.GeneralOption.can_view_all]} + perms_map = {"GET": [preferences.GeneralOption.can_view_all]} serializer_class = serializers.GeneralOptionSerializer def get_object(self): @@ -317,6 +353,7 @@ class GeneralOptionView(generics.RetrieveAPIView): class HomeServiceViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `preferences.models.Service` objects. """ + queryset = preferences.Service.objects.all() serializer_class = serializers.HomeServiceSerializer @@ -324,8 +361,9 @@ class HomeServiceViewSet(viewsets.ReadOnlyModelViewSet): class AssoOptionView(generics.RetrieveAPIView): """Exposes details of `preferences.models.AssoOption` settings. """ + permission_classes = (ACLPermission,) - perms_map = {'GET': [preferences.AssoOption.can_view_all]} + perms_map = {"GET": [preferences.AssoOption.can_view_all]} serializer_class = serializers.AssoOptionSerializer def get_object(self): @@ -335,8 +373,9 @@ class AssoOptionView(generics.RetrieveAPIView): class HomeOptionView(generics.RetrieveAPIView): """Exposes details of `preferences.models.HomeOption` settings. """ + permission_classes = (ACLPermission,) - perms_map = {'GET': [preferences.HomeOption.can_view_all]} + perms_map = {"GET": [preferences.HomeOption.can_view_all]} serializer_class = serializers.HomeOptionSerializer def get_object(self): @@ -346,8 +385,9 @@ class HomeOptionView(generics.RetrieveAPIView): class MailMessageOptionView(generics.RetrieveAPIView): """Exposes details of `preferences.models.MailMessageOption` settings. """ + permission_classes = (ACLPermission,) - perms_map = {'GET': [preferences.MailMessageOption.can_view_all]} + perms_map = {"GET": [preferences.MailMessageOption.can_view_all]} serializer_class = serializers.MailMessageOptionSerializer def get_object(self): @@ -360,6 +400,7 @@ class MailMessageOptionView(generics.RetrieveAPIView): class StackViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `topologie.models.Stack` objects. """ + queryset = topologie.Stack.objects.all() serializer_class = serializers.StackSerializer @@ -367,6 +408,7 @@ class StackViewSet(viewsets.ReadOnlyModelViewSet): class AccessPointViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `topologie.models.AccessPoint` objects. """ + queryset = topologie.AccessPoint.objects.all() serializer_class = serializers.AccessPointSerializer @@ -374,6 +416,7 @@ class AccessPointViewSet(viewsets.ReadOnlyModelViewSet): class SwitchViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `topologie.models.Switch` objects. """ + queryset = topologie.Switch.objects.all() serializer_class = serializers.SwitchSerializer @@ -381,6 +424,7 @@ class SwitchViewSet(viewsets.ReadOnlyModelViewSet): class ServerViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `topologie.models.Server` objects. """ + queryset = topologie.Server.objects.all() serializer_class = serializers.ServerSerializer @@ -388,6 +432,7 @@ class ServerViewSet(viewsets.ReadOnlyModelViewSet): class ModelSwitchViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `topologie.models.ModelSwitch` objects. """ + queryset = topologie.ModelSwitch.objects.all() serializer_class = serializers.ModelSwitchSerializer @@ -396,6 +441,7 @@ class ConstructorSwitchViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `topologie.models.ConstructorSwitch` objects. """ + queryset = topologie.ConstructorSwitch.objects.all() serializer_class = serializers.ConstructorSwitchSerializer @@ -403,6 +449,7 @@ class ConstructorSwitchViewSet(viewsets.ReadOnlyModelViewSet): class SwitchBayViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `topologie.models.SwitchBay` objects. """ + queryset = topologie.SwitchBay.objects.all() serializer_class = serializers.SwitchBaySerializer @@ -410,6 +457,7 @@ class SwitchBayViewSet(viewsets.ReadOnlyModelViewSet): class BuildingViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `topologie.models.Building` objects. """ + queryset = topologie.Building.objects.all() serializer_class = serializers.BuildingSerializer @@ -417,6 +465,7 @@ class BuildingViewSet(viewsets.ReadOnlyModelViewSet): class SwitchPortViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `topologie.models.Port` objects. """ + queryset = topologie.Port.objects.all() serializer_class = serializers.SwitchPortSerializer @@ -424,6 +473,7 @@ class SwitchPortViewSet(viewsets.ReadOnlyModelViewSet): class PortProfileViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `topologie.models.PortProfile` objects. """ + queryset = topologie.PortProfile.objects.all() serializer_class = serializers.PortProfileSerializer @@ -431,6 +481,7 @@ class PortProfileViewSet(viewsets.ReadOnlyModelViewSet): class RoomViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `topologie.models.Room` objects. """ + queryset = topologie.Room.objects.all() serializer_class = serializers.RoomSerializer @@ -438,6 +489,7 @@ class RoomViewSet(viewsets.ReadOnlyModelViewSet): class PortProfileViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `topologie.models.PortProfile` objects. """ + queryset = topologie.PortProfile.objects.all() serializer_class = serializers.PortProfileSerializer @@ -448,6 +500,7 @@ class PortProfileViewSet(viewsets.ReadOnlyModelViewSet): class UserViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `users.models.Users` objects. """ + queryset = users.User.objects.all() serializer_class = serializers.UserSerializer @@ -455,18 +508,25 @@ class UserViewSet(viewsets.ReadOnlyModelViewSet): class HomeCreationViewSet(viewsets.ReadOnlyModelViewSet): """Exposes infos of `users.models.Users` objects to create homes. """ - queryset = users.User.objects.exclude(Q(state=users.User.STATE_DISABLED) | Q(state=users.User.STATE_NOT_YET_ACTIVE)) + + queryset = users.User.objects.exclude( + Q(state=users.User.STATE_DISABLED) + | Q(state=users.User.STATE_NOT_YET_ACTIVE) + | Q(state=users.User.STATE_FULL_ARCHIVE) + ) serializer_class = serializers.BasicUserSerializer class NormalUserViewSet(viewsets.ReadOnlyModelViewSet): """Exposes infos of `users.models.Users`without specific rights objects.""" + queryset = users.User.objects.exclude(groups__listright__critical=True).distinct() serializer_class = serializers.BasicUserSerializer class CriticalUserViewSet(viewsets.ReadOnlyModelViewSet): """Exposes infos of `users.models.Users`without specific rights objects.""" + queryset = users.User.objects.filter(groups__listright__critical=True).distinct() serializer_class = serializers.BasicUserSerializer @@ -474,6 +534,7 @@ class CriticalUserViewSet(viewsets.ReadOnlyModelViewSet): class ClubViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `users.models.Club` objects. """ + queryset = users.Club.objects.all() serializer_class = serializers.ClubSerializer @@ -481,6 +542,7 @@ class ClubViewSet(viewsets.ReadOnlyModelViewSet): class AdherentViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `users.models.Adherent` objects. """ + queryset = users.Adherent.objects.all() serializer_class = serializers.AdherentSerializer @@ -488,6 +550,7 @@ class AdherentViewSet(viewsets.ReadOnlyModelViewSet): class ServiceUserViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `users.models.ServiceUser` objects. """ + queryset = users.ServiceUser.objects.all() serializer_class = serializers.ServiceUserSerializer @@ -495,6 +558,7 @@ class ServiceUserViewSet(viewsets.ReadOnlyModelViewSet): class SchoolViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `users.models.School` objects. """ + queryset = users.School.objects.all() serializer_class = serializers.SchoolSerializer @@ -502,6 +566,7 @@ class SchoolViewSet(viewsets.ReadOnlyModelViewSet): class ListRightViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `users.models.ListRight` objects. """ + queryset = users.ListRight.objects.all() serializer_class = serializers.ListRightSerializer @@ -509,6 +574,7 @@ class ListRightViewSet(viewsets.ReadOnlyModelViewSet): class ShellViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `users.models.ListShell` objects. """ + queryset = users.ListShell.objects.all() serializer_class = serializers.ShellSerializer @@ -516,6 +582,7 @@ class ShellViewSet(viewsets.ReadOnlyModelViewSet): class BanViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `users.models.Ban` objects. """ + queryset = users.Ban.objects.all() serializer_class = serializers.BanSerializer @@ -523,6 +590,7 @@ class BanViewSet(viewsets.ReadOnlyModelViewSet): class WhitelistViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `users.models.Whitelist` objects. """ + queryset = users.Whitelist.objects.all() serializer_class = serializers.WhitelistSerializer @@ -530,14 +598,13 @@ class WhitelistViewSet(viewsets.ReadOnlyModelViewSet): class EMailAddressViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `users.models.EMailAddress` objects. """ + serializer_class = serializers.EMailAddressSerializer queryset = users.EMailAddress.objects.none() def get_queryset(self): - if preferences.OptionalUser.get_cached_value( - 'local_email_accounts_enabled'): - return (users.EMailAddress.objects - .filter(user__local_email_enabled=True)) + if preferences.OptionalUser.get_cached_value("local_email_accounts_enabled"): + return users.EMailAddress.objects.filter(user__local_email_enabled=True) else: return users.EMailAddress.objects.none() @@ -548,34 +615,47 @@ class EMailAddressViewSet(viewsets.ReadOnlyModelViewSet): class ServiceRegenViewSet(viewsets.ModelViewSet): """Exposes list and details of the services to regen """ + serializer_class = serializers.ServiceRegenSerializer def get_queryset(self): queryset = machines.Service_link.objects.select_related( - 'server__domain' - ).select_related( - 'service' - ) - if 'hostname' in self.request.GET: - hostname = self.request.GET['hostname'] + "server__domain" + ).select_related("service") + if "hostname" in self.request.GET: + hostname = self.request.GET["hostname"] queryset = queryset.filter(server__domain__name__iexact=hostname) return queryset + # Config des switches + class SwitchPortView(generics.ListAPIView): """Output each port of a switch, to be serialized with additionnal informations (profiles etc) """ - queryset = topologie.Switch.objects.all().select_related("switchbay").select_related("model__constructor").prefetch_related("ports__custom_profile__vlan_tagged").prefetch_related("ports__custom_profile__vlan_untagged").prefetch_related("ports__machine_interface__domain__extension").prefetch_related("ports__room") + + queryset = ( + topologie.Switch.objects.all() + .select_related("switchbay") + .select_related("model__constructor") + .prefetch_related("ports__custom_profile__vlan_tagged") + .prefetch_related("ports__custom_profile__vlan_untagged") + .prefetch_related("ports__machine_interface__domain__extension") + .prefetch_related("ports__room") + ) serializer_class = serializers.SwitchPortSerializer + # Rappel fin adhésion + class ReminderView(generics.ListAPIView): """Output for users to remind an end of their subscription. """ + queryset = preferences.Reminder.objects.all() serializer_class = serializers.ReminderSerializer @@ -583,7 +663,8 @@ class ReminderView(generics.ListAPIView): class RoleView(generics.ListAPIView): """Output of roles for each server """ - queryset = machines.Role.objects.all().prefetch_related('servers') + + queryset = machines.Role.objects.all().prefetch_related("servers") serializer_class = serializers.RoleSerializer @@ -593,13 +674,12 @@ class RoleView(generics.ListAPIView): class LocalEmailUsersView(generics.ListAPIView): """Exposes all the aliases of the users that activated the internal address """ + serializer_class = serializers.LocalEmailUsersSerializer def get_queryset(self): - if preferences.OptionalUser.get_cached_value( - 'local_email_accounts_enabled'): - return (users.User.objects - .filter(local_email_enabled=True)) + if preferences.OptionalUser.get_cached_value("local_email_accounts_enabled"): + return users.User.objects.filter(local_email_enabled=True) else: return users.User.objects.none() @@ -611,6 +691,7 @@ class HostMacIpView(generics.ListAPIView): """Exposes the associations between hostname, mac address and IPv4 in order to build the DHCP lease files. """ + serializer_class = serializers.HostMacIpSerializer def get_queryset(self): @@ -619,6 +700,7 @@ class HostMacIpView(generics.ListAPIView): # Firewall + class SubnetPortsOpenView(generics.ListAPIView): queryset = machines.IpType.objects.all() serializer_class = serializers.SubnetPortsOpenSerializer @@ -636,14 +718,19 @@ class DNSZonesView(generics.ListAPIView): """Exposes the detailed information about each extension (hostnames, IPs, DNS records, etc.) in order to build the DNS zone files. """ - queryset = (machines.Extension.objects - .prefetch_related('soa') - .prefetch_related('ns_set').prefetch_related('ns_set__ns') - .prefetch_related('origin') - .prefetch_related('mx_set').prefetch_related('mx_set__name') - .prefetch_related('txt_set') - .prefetch_related('srv_set').prefetch_related('srv_set__target') - .all()) + + queryset = ( + machines.Extension.objects.prefetch_related("soa") + .prefetch_related("ns_set") + .prefetch_related("ns_set__ns") + .prefetch_related("origin") + .prefetch_related("mx_set") + .prefetch_related("mx_set__name") + .prefetch_related("txt_set") + .prefetch_related("srv_set") + .prefetch_related("srv_set__target") + .all() + ) serializer_class = serializers.DNSZonesSerializer @@ -651,7 +738,8 @@ class DNSReverseZonesView(generics.ListAPIView): """Exposes the detailed information about each extension (hostnames, IPs, DNS records, etc.) in order to build the DNS zone files. """ - queryset = (machines.IpType.objects.all()) + + queryset = machines.IpType.objects.all() serializer_class = serializers.DNSReverseZonesSerializer @@ -662,13 +750,16 @@ class StandardMailingView(views.APIView): """Exposes list and details of standard mailings (name and members) in order to building the corresponding mailing lists. """ + pagination_class = PageSizedPagination permission_classes = (ACLPermission,) - perms_map = {'GET': [users.User.can_view_all]} + perms_map = {"GET": [users.User.can_view_all]} def get(self, request, format=None): - adherents_data = serializers.MailingMemberSerializer(all_has_access(), many=True).data - data = [{'name': 'adherents', 'members': adherents_data}] + adherents_data = serializers.MailingMemberSerializer( + all_has_access(), many=True + ).data + data = [{"name": "adherents", "members": adherents_data}] paginator = self.pagination_class() paginator.paginate_queryset(data, request) return paginator.get_paginated_response(data) @@ -678,6 +769,7 @@ class ClubMailingView(generics.ListAPIView): """Exposes list and details of club mailings (name, members and admins) in order to build the corresponding mailing lists. """ + queryset = users.Club.objects.all() serializer_class = serializers.MailingSerializer @@ -696,12 +788,10 @@ class ObtainExpiringAuthToken(ObtainAuthToken): def post(self, request, *args, **kwargs): serializer = self.serializer_class(data=request.data) serializer.is_valid(raise_exception=True) - user = serializer.validated_data['user'] + user = serializer.validated_data["user"] token, created = Token.objects.get_or_create(user=user) - token_duration = datetime.timedelta( - seconds=settings.API_TOKEN_DURATION - ) + token_duration = datetime.timedelta(seconds=settings.API_TOKEN_DURATION) utc_now = datetime.datetime.now(datetime.timezone.utc) if not created and token.created < utc_now - token_duration: token.delete() @@ -709,7 +799,6 @@ class ObtainExpiringAuthToken(ObtainAuthToken): token.created = datetime.datetime.utcnow() token.save() - return Response({ - 'token': token.key, - 'expiration': token.created + token_duration - }) + return Response( + {"token": token.key, "expiration": token.created + token_duration} + ) diff --git a/apt_requirements.txt b/apt_requirements.txt index 0cf83a7c..1087e1fe 100644 --- a/apt_requirements.txt +++ b/apt_requirements.txt @@ -5,6 +5,7 @@ texlive-fonts-recommended python3-djangorestframework python3-django-reversion python3-pip +python3-pil libsasl2-dev libldap2-dev libssl-dev python3-crypto @@ -18,3 +19,4 @@ fonts-font-awesome graphviz git gettext +python3-django-ldapdb diff --git a/apt_requirements_radius.txt b/apt_requirements_radius.txt new file mode 100644 index 00000000..2d4e9bde --- /dev/null +++ b/apt_requirements_radius.txt @@ -0,0 +1,23 @@ +python-django +python-dateutil +texlive-latex-base +texlive-fonts-recommended +python-djangorestframework +python-django-reversion +python-pip +libsasl2-dev libldap2-dev +libssl-dev +python-crypto +python-git +javascript-common +libjs-jquery +libjs-jquery-ui +libjs-jquery-timepicker +libjs-bootstrap +fonts-font-awesome +graphviz +git +gettext +freeradius-common +freeradius-python2 +python-mysqldb diff --git a/cotisations/__init__.py b/cotisations/__init__.py index ee5a305d..be5cb5d2 100644 --- a/cotisations/__init__.py +++ b/cotisations/__init__.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify diff --git a/cotisations/acl.py b/cotisations/acl.py index 06c62fb8..07db6929 100644 --- a/cotisations/acl.py +++ b/cotisations/acl.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -38,9 +38,12 @@ def can_view(user): 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') + can = user.has_module_perms("cotisations") if can: - return can, None + return can, None, ("cotisations",) else: - return can, _("You don't have the right to view this application.") - + return ( + can, + _("You don't have the right to view this application."), + ("cotisations",), + ) diff --git a/cotisations/admin.py b/cotisations/admin.py index 4b47ccc8..ebe7d683 100644 --- a/cotisations/admin.py +++ b/cotisations/admin.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -35,42 +35,50 @@ from .models import CustomInvoice, CostEstimate class FactureAdmin(VersionAdmin): """Class admin d'une facture, tous les champs""" + pass class CostEstimateAdmin(VersionAdmin): """Admin class for cost estimates.""" + pass class CustomInvoiceAdmin(VersionAdmin): """Admin class for custom invoices.""" + pass class VenteAdmin(VersionAdmin): """Class admin d'une vente, tous les champs (facture related)""" + pass class ArticleAdmin(VersionAdmin): """Class admin d'un article en vente""" + pass class BanqueAdmin(VersionAdmin): """Class admin de la liste des banques (facture related)""" + pass class PaiementAdmin(VersionAdmin): """Class admin d'un moyen de paiement (facture related""" + pass class CotisationAdmin(VersionAdmin): """Class admin d'une cotisation (date de debut et de fin), Vente related""" + pass diff --git a/cotisations/forms.py b/cotisations/forms.py index 3f99382b..76db358c 100644 --- a/cotisations/forms.py +++ b/cotisations/forms.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # Copyright © 2018 Hugo Levy-Falk # @@ -47,8 +47,13 @@ from django.shortcuts import get_object_or_404 from re2o.field_permissions import FieldPermissionFormMixin from re2o.mixins import FormRevMixin from .models import ( - Article, Paiement, Facture, Banque, - CustomInvoice, Vente, CostEstimate, + Article, + Paiement, + Facture, + Banque, + CustomInvoice, + Vente, + CostEstimate, ) from .payment_methods import balance @@ -59,31 +64,27 @@ class FactureForm(FieldPermissionFormMixin, FormRevMixin, ModelForm): """ def __init__(self, *args, creation=False, **kwargs): - user = kwargs['user'] - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + user = kwargs["user"] + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(FactureForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['paiement'].empty_label = \ - _("Select a payment method") - self.fields['paiement'].queryset = Paiement.find_allowed_payments(user) + self.fields["paiement"].empty_label = _("Select a payment method") + self.fields["paiement"].queryset = Paiement.find_allowed_payments(user) if not creation: - self.fields['user'].label = _("Member") - self.fields['user'].empty_label = \ - _("Select the proprietary member") - self.fields['valid'].label = _("Validated invoice") + self.fields["user"].label = _("Member") + self.fields["user"].empty_label = _("Select the proprietary member") + self.fields["valid"].label = _("Validated invoice") else: - self.fields = {'paiement': self.fields['paiement']} + self.fields = {"paiement": self.fields["paiement"]} class Meta: model = Facture - fields = '__all__' + fields = "__all__" def clean(self): cleaned_data = super(FactureForm, self).clean() - paiement = cleaned_data.get('paiement') + paiement = cleaned_data.get("paiement") if not paiement: - raise forms.ValidationError( - _("A payment method must be specified.") - ) + raise forms.ValidationError(_("A payment method must be specified.")) return cleaned_data @@ -92,32 +93,30 @@ class SelectArticleForm(FormRevMixin, Form): Form used to select an article during the creation of an invoice for a member. """ + article = forms.ModelChoiceField( - queryset=Article.objects.none(), - label=_("Article"), - required=True + queryset=Article.objects.none(), label=_("Article"), required=True ) quantity = forms.IntegerField( - label=_("Quantity"), - validators=[MinValueValidator(1)], - required=True + label=_("Quantity"), validators=[MinValueValidator(1)], required=True ) def __init__(self, *args, **kwargs): - user = kwargs.pop('user') - target_user = kwargs.pop('target_user', None) + user = kwargs.pop("user") + target_user = kwargs.pop("target_user", None) super(SelectArticleForm, self).__init__(*args, **kwargs) - self.fields['article'].queryset = Article.find_allowed_articles( - user, target_user) + self.fields["article"].queryset = Article.find_allowed_articles( + user, target_user + ) class DiscountForm(Form): """ Form used in oder to create a discount on an invoice. """ + is_relative = forms.BooleanField( - label=_("Discount is on percentage."), - required=False, + label=_("Discount is in percentage."), required=False ) discount = forms.DecimalField( label=_("Discount"), @@ -130,53 +129,51 @@ class DiscountForm(Form): def apply_to_invoice(self, invoice): invoice_price = invoice.prix_total() - discount = self.cleaned_data['discount'] - is_relative = self.cleaned_data['is_relative'] + discount = self.cleaned_data["discount"] + is_relative = self.cleaned_data["is_relative"] if is_relative: - amount = discount/100 * invoice_price + amount = discount / 100 * invoice_price else: amount = discount if amount: - name = _("{}% discount") if is_relative else _("{}€ discount") + name = _("{}% discount") if is_relative else _("{} € discount") name = name.format(discount) - Vente.objects.create( - facture=invoice, - name=name, - prix=-amount, - number=1 - ) + Vente.objects.create(facture=invoice, name=name, prix=-amount, number=1) class CustomInvoiceForm(FormRevMixin, ModelForm): """ Form used to create a custom invoice. """ + class Meta: model = CustomInvoice - fields = '__all__' + fields = "__all__" class CostEstimateForm(FormRevMixin, ModelForm): """ Form used to create a cost estimate. """ + class Meta: model = CostEstimate - exclude = ['paid', 'final_invoice'] + exclude = ["paid", "final_invoice"] class ArticleForm(FormRevMixin, ModelForm): """ Form used to create an article. """ + class Meta: model = Article - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(ArticleForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['name'].label = _("Article name") + self.fields["name"].label = _("Article name") class DelArticleForm(FormRevMixin, Form): @@ -184,19 +181,20 @@ class DelArticleForm(FormRevMixin, Form): Form used to delete one or more of the currently available articles. The user must choose the one to delete by checking the boxes. """ + articles = forms.ModelMultipleChoiceField( queryset=Article.objects.none(), - label=_("Available articles"), - widget=forms.CheckboxSelectMultiple + label=_("Current articles"), + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) + instances = kwargs.pop("instances", None) super(DelArticleForm, self).__init__(*args, **kwargs) if instances: - self.fields['articles'].queryset = instances + self.fields["articles"].queryset = instances else: - self.fields['articles'].queryset = Article.objects.all() + self.fields["articles"].queryset = Article.objects.all() # TODO : change Paiement to Payment @@ -206,15 +204,16 @@ class PaiementForm(FormRevMixin, ModelForm): The 'cheque' type is used to associate a specific behaviour requiring a cheque number and a bank. """ + class Meta: model = Paiement # TODO : change moyen to method and type_paiement to payment_type - fields = ['moyen', 'available_for_everyone'] + fields = ["moyen", "available_for_everyone"] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(PaiementForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['moyen'].label = _("Payment method name") + self.fields["moyen"].label = _("Payment method name") # TODO : change paiement to payment @@ -223,20 +222,21 @@ class DelPaiementForm(FormRevMixin, Form): Form used to delete one or more payment methods. The user must choose the one to delete by checking the boxes. """ + # TODO : change paiement to payment paiements = forms.ModelMultipleChoiceField( queryset=Paiement.objects.none(), - label=_("Available payment methods"), - widget=forms.CheckboxSelectMultiple + label=_("Current payment methods"), + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) + instances = kwargs.pop("instances", None) super(DelPaiementForm, self).__init__(*args, **kwargs) if instances: - self.fields['paiements'].queryset = instances + self.fields["paiements"].queryset = instances else: - self.fields['paiements'].queryset = Paiement.objects.all() + self.fields["paiements"].queryset = Paiement.objects.all() # TODO : change banque to bank @@ -244,15 +244,16 @@ class BanqueForm(FormRevMixin, ModelForm): """ Form used to create a bank. """ + class Meta: # TODO : change banque to bank model = Banque - fields = ['name'] + fields = ["name"] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(BanqueForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['name'].label = _("Bank name") + self.fields["name"].label = _("Bank name") # TODO : change banque to bank @@ -261,20 +262,21 @@ class DelBanqueForm(FormRevMixin, Form): Form used to delete one or more banks. The use must choose the one to delete by checking the boxes. """ + # TODO : change banque to bank banques = forms.ModelMultipleChoiceField( queryset=Banque.objects.none(), - label=_("Available banks"), - widget=forms.CheckboxSelectMultiple + label=_("Current banks"), + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) + instances = kwargs.pop("instances", None) super(DelBanqueForm, self).__init__(*args, **kwargs) if instances: - self.fields['banques'].queryset = instances + self.fields["banques"].queryset = instances else: - self.fields['banques'].queryset = Banque.objects.all() + self.fields["banques"].queryset = Banque.objects.all() # TODO : Better name and docstring @@ -282,37 +284,36 @@ class RechargeForm(FormRevMixin, Form): """ Form used to refill a user's balance """ - value = forms.DecimalField( - label=_("Amount"), - min_value=0.01, - validators=[] - ) + + value = forms.DecimalField(label=_("Amount"), decimal_places=2) payment = forms.ModelChoiceField( - queryset=Paiement.objects.none(), - label=_("Payment method") + queryset=Paiement.objects.none(), label=_("Payment method") ) def __init__(self, *args, user=None, user_source=None, **kwargs): self.user = user super(RechargeForm, self).__init__(*args, **kwargs) - self.fields['payment'].empty_label = \ - _("Select a payment method") - self.fields['payment'].queryset = Paiement.find_allowed_payments( - user_source).exclude(is_balance=True) + self.fields["payment"].empty_label = _("Select a payment method") + self.fields["payment"].queryset = Paiement.find_allowed_payments( + user_source + ).exclude(is_balance=True) def clean(self): """ Returns a cleaned value from the received form by validating the value is well inside the possible limits """ - value = self.cleaned_data['value'] + value = self.cleaned_data["value"] balance_method = get_object_or_404(balance.PaymentMethod) - if balance_method.maximum_balance is not None and \ - value + self.user.solde > balance_method.maximum_balance: + if ( + balance_method.maximum_balance is not None + and value + self.user.solde > balance_method.maximum_balance + ): raise forms.ValidationError( - _("Requested amount is too high. Your balance can't exceed" - " %(max_online_balance)s €.") % { - 'max_online_balance': balance_method.maximum_balance - } + _( + "Requested amount is too high. Your balance can't exceed" + " %(max_online_balance)s €." + ) + % {"max_online_balance": balance_method.maximum_balance} ) return self.cleaned_data diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po index 2c29dc8d..1d68e1fe 100644 --- a/cotisations/locale/fr/LC_MESSAGES/django.po +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -1,5 +1,5 @@ # 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 +# se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # # Copyright © 2018 Maël Kervella @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-01-12 16:50+0100\n" +"POT-Creation-Date: 2019-11-20 01:24+0100\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language: fr_FR\n" @@ -29,92 +29,95 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: acl.py:45 +#: cotisations/acl.py:47 msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: forms.py:66 forms.py:299 +#: cotisations/forms.py:70 cotisations/forms.py:296 msgid "Select a payment method" msgstr "Sélectionnez un moyen de paiement" -#: forms.py:69 models.py:579 +#: cotisations/forms.py:73 cotisations/models.py:676 msgid "Member" msgstr "Adhérent" -#: forms.py:71 +#: cotisations/forms.py:74 msgid "Select the proprietary member" msgstr "Sélectionnez l'adhérent propriétaire" -#: forms.py:72 +#: cotisations/forms.py:75 msgid "Validated invoice" msgstr "Facture validée" -#: forms.py:85 +#: cotisations/forms.py:87 msgid "A payment method must be specified." msgstr "Un moyen de paiement doit être renseigné." -#: forms.py:97 templates/cotisations/aff_article.html:33 -#: templates/cotisations/facture.html:67 +#: cotisations/forms.py:98 +#: cotisations/templates/cotisations/aff_article.html:33 +#: cotisations/templates/cotisations/facture.html:64 msgid "Article" msgstr "Article" -#: forms.py:101 templates/cotisations/edit_facture.html:50 +#: cotisations/forms.py:101 +#: cotisations/templates/cotisations/edit_facture.html:50 msgid "Quantity" msgstr "Quantité" -#: forms.py:119 -msgid "Discount is on percentage." +#: cotisations/forms.py:119 +msgid "Discount is in percentage." msgstr "La réduction est en pourcentage." -#: forms.py:123 templates/cotisations/facture.html:78 +#: cotisations/forms.py:122 cotisations/templates/cotisations/facture.html:75 msgid "Discount" msgstr "Réduction" -#: forms.py:140 +#: cotisations/forms.py:139 #, python-format msgid "{}% discount" msgstr "{}% de réduction" -#: forms.py:140 -msgid "{}€ discount" -msgstr "{}€ de réduction" +#: cotisations/forms.py:139 +msgid "{} € discount" +msgstr "{} € de réduction" -#: forms.py:179 +#: cotisations/forms.py:176 msgid "Article name" msgstr "Nom de l'article" -#: forms.py:189 templates/cotisations/sidebar.html:55 -msgid "Available articles" -msgstr "Articles disponibles" +#: cotisations/forms.py:187 +msgid "Current articles" +msgstr "Articles actuels" -#: forms.py:217 +#: cotisations/forms.py:216 msgid "Payment method name" msgstr "Nom du moyen de paiement" -#: forms.py:229 -msgid "Available payment methods" -msgstr "Moyens de paiement disponibles" +#: cotisations/forms.py:229 +msgid "Current payment methods" +msgstr "Moyens de paiement actuels" -#: forms.py:255 +#: cotisations/forms.py:256 msgid "Bank name" msgstr "Nom de la banque" -#: forms.py:267 -msgid "Available banks" -msgstr "Banques disponibles" +#: cotisations/forms.py:269 +msgid "Current banks" +msgstr "Banques actuelles" -#: forms.py:286 +#: cotisations/forms.py:288 msgid "Amount" msgstr "Montant" -#: forms.py:292 templates/cotisations/aff_cost_estimate.html:42 -#: templates/cotisations/aff_cotisations.html:44 -#: templates/cotisations/aff_custom_invoice.html:42 -#: templates/cotisations/control.html:66 +#: cotisations/forms.py:290 +#: cotisations/templates/cotisations/aff_cost_estimate.html:42 +#: cotisations/templates/cotisations/aff_cotisations.html:44 +#: cotisations/templates/cotisations/aff_custom_invoice.html:42 +#: cotisations/templates/cotisations/control.html:66 msgid "Payment method" msgstr "Moyen de paiement" -#: forms.py:313 +#: cotisations/forms.py:314 #, python-format msgid "" "Requested amount is too high. Your balance can't exceed " @@ -123,54 +126,52 @@ msgstr "" "Le montant demandé est trop grand. Votre solde ne peut excéder " "%(max_online_balance)s €." -#: models.py:60 templates/cotisations/aff_cost_estimate.html:46 -#: templates/cotisations/aff_cotisations.html:48 -#: templates/cotisations/aff_custom_invoice.html:46 -#: templates/cotisations/control.html:70 -msgid "Date" -msgstr "Date" +#: cotisations/models.py:59 +msgid "date" +msgstr "date" -#: models.py:136 +#: cotisations/models.py:130 msgid "cheque number" msgstr "numéro de chèque" -#: models.py:141 +#: cotisations/models.py:133 msgid "validated" -msgstr "validée" +msgstr "validé" -#: models.py:146 +#: cotisations/models.py:135 msgid "controlled" -msgstr "contrôlée" +msgstr "contrôlé" -#: models.py:154 +#: cotisations/models.py:141 msgid "Can edit the \"controlled\" state" msgstr "Peut modifier l'état \"contrôlé\"" -#: models.py:156 +#: cotisations/models.py:142 msgid "Can view an invoice object" msgstr "Peut voir un objet facture" -#: models.py:158 +#: cotisations/models.py:143 msgid "Can edit all the previous invoices" msgstr "Peut modifier toutes les factures précédentes" -#: models.py:160 models.py:373 +#: cotisations/models.py:145 cotisations/models.py:431 cotisations/views.py:378 +#: cotisations/views.py:573 msgid "invoice" msgstr "facture" -#: models.py:161 +#: cotisations/models.py:146 msgid "invoices" msgstr "factures" -#: models.py:170 +#: cotisations/models.py:160 msgid "You don't have the right to edit an invoice." msgstr "Vous n'avez pas le droit de modifier une facture." -#: models.py:173 +#: cotisations/models.py:168 msgid "You don't have the right to edit this user's invoices." msgstr "Vous n'avez pas le droit de modifier les factures de cet utilisateur." -#: models.py:177 +#: cotisations/models.py:177 msgid "" "You don't have the right to edit an invoice already controlled or " "invalidated." @@ -178,15 +179,15 @@ msgstr "" "Vous n'avez pas le droit de modifier une facture précédemment contrôlée ou " "invalidée." -#: models.py:184 +#: cotisations/models.py:192 msgid "You don't have the right to delete an invoice." msgstr "Vous n'avez pas le droit de supprimer une facture." -#: models.py:187 +#: cotisations/models.py:200 msgid "You don't have the right to delete this user's invoices." msgstr "Vous n'avez pas le droit de supprimer les factures de cet utilisateur." -#: models.py:191 +#: cotisations/models.py:209 msgid "" "You don't have the right to delete an invoice already controlled or " "invalidated." @@ -194,130 +195,137 @@ msgstr "" "Vous n'avez pas le droit de supprimer une facture précédemment contrôlée ou " "invalidée." -#: models.py:199 +#: cotisations/models.py:223 msgid "You don't have the right to view someone else's invoices history." msgstr "" "Vous n'avez pas le droit de voir l'historique des factures d'un autre " "utilisateur." -#: models.py:202 +#: cotisations/models.py:231 msgid "The invoice has been invalidated." msgstr "La facture a été invalidée." -#: models.py:214 +#: cotisations/models.py:246 msgid "You don't have the right to edit the \"controlled\" state." msgstr "Vous n'avez pas le droit de modifier le statut \"contrôlé\"." -#: models.py:228 -msgid "There are no payment method which you can use." -msgstr "Il n'y a pas de moyen de paiement que vous puissiez utiliser." +#: cotisations/models.py:265 +msgid "There are no payment methods that you can use." +msgstr "Il n'y a pas de moyens de paiement que vous puissiez utiliser." -#: models.py:230 -msgid "There are no article that you can buy." -msgstr "Il n'y a pas d'article que vous puissiez acheter." +#: cotisations/models.py:271 +msgid "There are no articles that you can buy." +msgstr "Il n'y a pas d'articles que vous puissiez acheter." -#: models.py:272 +#: cotisations/models.py:347 msgid "Can view a custom invoice object" msgstr "Peut voir un objet facture personnalisée" -#: models.py:276 templates/cotisations/aff_cost_estimate.html:36 -#: templates/cotisations/aff_custom_invoice.html:36 -msgid "Recipient" -msgstr "Destinataire" +#: cotisations/models.py:349 +msgid "recipient" +msgstr "destinataire" -#: models.py:280 templates/cotisations/aff_paiement.html:33 -msgid "Payment type" -msgstr "Type de paiement" +#: cotisations/models.py:350 +msgid "payment type" +msgstr "type de paiement" -#: models.py:284 -msgid "Address" -msgstr "Adresse" +#: cotisations/models.py:351 +msgid "address" +msgstr "adresse" -#: models.py:287 templates/cotisations/aff_custom_invoice.html:54 -msgid "Paid" -msgstr "Payé" +#: cotisations/models.py:352 +msgid "paid" +msgstr "payé" -#: models.py:291 -msgid "Remark" -msgstr "Remarque" +#: cotisations/models.py:353 +msgid "remark" +msgstr "remarque" -#: models.py:300 +#: cotisations/models.py:358 msgid "Can view a cost estimate object" msgstr "Peut voir un objet devis" -#: models.py:303 -msgid "Period of validity" -msgstr "Période de validité" +#: cotisations/models.py:361 +msgid "period of validity" +msgstr "période de validité" -#: models.py:340 +#: cotisations/models.py:396 msgid "You don't have the right to delete a cost estimate." msgstr "Vous n'avez pas le droit de supprimer un devis." -#: models.py:343 +#: cotisations/models.py:402 msgid "The cost estimate has an invoice and can't be deleted." msgstr "Le devis a une facture et ne peut pas être supprimé." -#: models.py:364 models.py:585 models.py:852 +#: cotisations/models.py:424 cotisations/models.py:682 +#: cotisations/models.py:940 msgid "Connection" msgstr "Connexion" -#: models.py:365 models.py:586 models.py:853 +#: cotisations/models.py:425 cotisations/models.py:683 +#: cotisations/models.py:941 msgid "Membership" msgstr "Adhésion" -#: models.py:366 models.py:581 models.py:587 models.py:854 +#: cotisations/models.py:426 cotisations/models.py:678 +#: cotisations/models.py:684 cotisations/models.py:942 msgid "Both of them" msgstr "Les deux" -#: models.py:378 +#: cotisations/models.py:435 msgid "amount" msgstr "montant" -#: models.py:383 +#: cotisations/models.py:438 msgid "article" msgstr "article" -#: models.py:390 +#: cotisations/models.py:441 msgid "price" msgstr "prix" -#: models.py:395 models.py:604 +#: cotisations/models.py:444 cotisations/models.py:696 msgid "duration (in months)" msgstr "durée (en mois)" -#: models.py:403 models.py:618 models.py:868 +#: cotisations/models.py:450 cotisations/models.py:702 +msgid "duration (in days, will be added to duration in months)" +msgstr "durée (en jours, sera ajoutée à la durée en mois)" + +#: cotisations/models.py:458 cotisations/models.py:716 +#: cotisations/models.py:953 msgid "subscription type" msgstr "type de cotisation" -#: models.py:408 +#: cotisations/models.py:463 msgid "Can view a purchase object" msgstr "Peut voir un objet achat" -#: models.py:409 +#: cotisations/models.py:464 msgid "Can edit all the previous purchases" msgstr "Peut modifier tous les achats précédents" -#: models.py:411 models.py:862 +#: cotisations/models.py:466 cotisations/models.py:947 msgid "purchase" msgstr "achat" -#: models.py:412 +#: cotisations/models.py:467 msgid "purchases" msgstr "achats" -#: models.py:479 models.py:642 +#: cotisations/models.py:539 cotisations/models.py:736 msgid "Duration must be specified for a subscription." -msgstr "La durée de la cotisation doit être indiquée." +msgstr "La durée doit être renseignée pour une cotisation." -#: models.py:486 -msgid "You don't have the right to edit the purchases." -msgstr "Vous n'avez pas le droit de modifier les achats." +#: cotisations/models.py:550 +msgid "You don't have the right to edit a purchase." +msgstr "Vous n'avez pas le droit de modifier un achat." -#: models.py:491 +#: cotisations/models.py:556 msgid "You don't have the right to edit this user's purchases." msgstr "Vous n'avez pas le droit de modifier les achats de cet utilisateur." -#: models.py:495 +#: cotisations/models.py:565 msgid "" "You don't have the right to edit a purchase already controlled or " "invalidated." @@ -325,15 +333,15 @@ msgstr "" "Vous n'avez pas le droit de modifier un achat précédemment contrôlé ou " "invalidé." -#: models.py:502 +#: cotisations/models.py:580 msgid "You don't have the right to delete a purchase." msgstr "Vous n'avez pas le droit de supprimer un achat." -#: models.py:504 +#: cotisations/models.py:586 msgid "You don't have the right to delete this user's purchases." msgstr "Vous n'avez pas le droit de supprimer les achats de cet utilisateur." -#: models.py:507 +#: cotisations/models.py:593 msgid "" "You don't have the right to delete a purchase already controlled or " "invalidated." @@ -341,134 +349,134 @@ msgstr "" "Vous n'avez pas le droit de supprimer un achat précédemment contrôlé ou " "invalidé." -#: models.py:515 +#: cotisations/models.py:609 msgid "You don't have the right to view someone else's purchase history." msgstr "" "Vous n'avez pas le droit de voir l'historique des achats d'un autre " "utilisateur." -#: models.py:580 +#: cotisations/models.py:677 msgid "Club" msgstr "Club" -#: models.py:592 +#: cotisations/models.py:687 msgid "designation" msgstr "désignation" -#: models.py:598 +#: cotisations/models.py:690 msgid "unit price" msgstr "prix unitaire" -#: models.py:610 +#: cotisations/models.py:708 msgid "type of users concerned" msgstr "type d'utilisateurs concernés" -#: models.py:622 models.py:733 +#: cotisations/models.py:719 cotisations/models.py:820 msgid "is available for every user" msgstr "est disponible pour chaque utilisateur" -#: models.py:629 +#: cotisations/models.py:726 msgid "Can view an article object" msgstr "Peut voir un objet article" -#: models.py:630 +#: cotisations/models.py:727 msgid "Can buy every article" msgstr "Peut acheter chaque article" -#: models.py:638 -msgid "Balance is a reserved article name." +#: cotisations/models.py:734 +msgid "Solde is a reserved article name." msgstr "Solde est un nom d'article réservé." -#: models.py:663 +#: cotisations/models.py:759 msgid "You can't buy this article." msgstr "Vous ne pouvez pas acheter cet article." -#: models.py:708 +#: cotisations/models.py:800 msgid "Can view a bank object" msgstr "Peut voir un objet banque" -#: models.py:710 +#: cotisations/models.py:801 msgid "bank" msgstr "banque" -#: models.py:711 +#: cotisations/models.py:802 msgid "banks" msgstr "banques" -#: models.py:729 +#: cotisations/models.py:818 msgid "method" msgstr "moyen" -#: models.py:738 +#: cotisations/models.py:825 msgid "is user balance" msgstr "est solde utilisateur" -#: models.py:739 +#: cotisations/models.py:826 msgid "There should be only one balance payment method." msgstr "Il ne devrait y avoir qu'un moyen de paiement solde." -#: models.py:745 +#: cotisations/models.py:832 msgid "Can view a payment method object" msgstr "Peut voir un objet moyen de paiement" -#: models.py:746 +#: cotisations/models.py:833 msgid "Can use every payment method" msgstr "Peut utiliser chaque moyen de paiement" -#: models.py:748 +#: cotisations/models.py:835 msgid "payment method" msgstr "moyen de paiement" -#: models.py:749 +#: cotisations/models.py:836 msgid "payment methods" msgstr "moyens de paiement" -#: models.py:787 payment_methods/comnpay/views.py:63 +#: cotisations/models.py:875 cotisations/payment_methods/comnpay/views.py:62 #, python-format msgid "The subscription of %(member_name)s was extended to %(end_date)s." msgstr "La cotisation de %(member_name)s a été étendue au %(end_date)s." -#: models.py:797 +#: cotisations/models.py:885 msgid "The invoice was created." msgstr "La facture a été créée." -#: models.py:818 +#: cotisations/models.py:905 msgid "You can't use this payment method." msgstr "Vous ne pouvez pas utiliser ce moyen de paiement." -#: models.py:836 -msgid "No custom payment method." -msgstr "Pas de moyen de paiement personnalisé." +#: cotisations/models.py:924 +msgid "No custom payment methods." +msgstr "Pas de moyens de paiement personnalisés." -#: models.py:871 +#: cotisations/models.py:955 msgid "start date" msgstr "date de début" -#: models.py:874 +#: cotisations/models.py:956 msgid "end date" msgstr "date de fin" -#: models.py:879 +#: cotisations/models.py:960 msgid "Can view a subscription object" msgstr "Peut voir un objet cotisation" -#: models.py:880 +#: cotisations/models.py:961 msgid "Can edit the previous subscriptions" msgstr "Peut modifier les cotisations précédentes" -#: models.py:882 +#: cotisations/models.py:963 msgid "subscription" msgstr "cotisation" -#: models.py:883 +#: cotisations/models.py:964 msgid "subscriptions" msgstr "cotisations" -#: models.py:887 +#: cotisations/models.py:970 msgid "You don't have the right to edit a subscription." msgstr "Vous n'avez pas le droit de modifier une cotisation." -#: models.py:891 +#: cotisations/models.py:979 msgid "" "You don't have the right to edit a subscription already controlled or " "invalidated." @@ -476,11 +484,11 @@ msgstr "" "Vous n'avez pas le droit de modifier une cotisation précédemment contrôlée " "ou invalidée." -#: models.py:898 +#: cotisations/models.py:991 msgid "You don't have the right to delete a subscription." msgstr "Vous n'avez pas le droit de supprimer une cotisation." -#: models.py:901 +#: cotisations/models.py:998 msgid "" "You don't have the right to delete a subscription already controlled or " "invalidated." @@ -488,106 +496,108 @@ msgstr "" "Vous n'avez pas le droit de supprimer une cotisation précédemment contrôlée " "ou invalidée." -#: models.py:909 +#: cotisations/models.py:1014 msgid "You don't have the right to view someone else's subscription history." msgstr "" "Vous n'avez pas le droit de voir l'historique des cotisations d'un autre " "utilisateur." -#: payment_methods/balance/models.py:38 +#: cotisations/payment_methods/balance/models.py:38 msgid "user balance" msgstr "solde utilisateur" -#: payment_methods/balance/models.py:47 -msgid "Minimum balance" -msgstr "Solde minimum" +#: cotisations/payment_methods/balance/models.py:47 +msgid "minimum balance" +msgstr "solde minimum" -#: payment_methods/balance/models.py:48 +#: cotisations/payment_methods/balance/models.py:49 msgid "" "The minimal amount of money allowed for the balance at the end of a payment. " -"You can specify negative amount." +"You can specify a negative amount." msgstr "" "Le montant minimal d'argent autorisé pour le solde à la fin d'un paiement. " "Vous pouvez renseigner un montant négatif." -#: payment_methods/balance/models.py:57 -msgid "Maximum balance" -msgstr "Solde maximum" +#: cotisations/payment_methods/balance/models.py:57 +msgid "maximum balance" +msgstr "solde maximum" -#: payment_methods/balance/models.py:58 +#: cotisations/payment_methods/balance/models.py:58 msgid "The maximal amount of money allowed for the balance." msgstr "Le montant maximal d'argent autorisé pour le solde." -#: payment_methods/balance/models.py:66 -msgid "Allow user to credit their balance" -msgstr "Autorise l'utilisateur à créditer son solde" +#: cotisations/payment_methods/balance/models.py:66 +msgid "allow user to credit their balance" +msgstr "autoriser l'utilisateur à créditer son solde" -#: payment_methods/balance/models.py:79 payment_methods/balance/models.py:110 +#: cotisations/payment_methods/balance/models.py:76 +#: cotisations/payment_methods/balance/models.py:99 msgid "Your balance is too low for this operation." msgstr "Votre solde est trop bas pour cette opération." -#: payment_methods/balance/models.py:97 validators.py:20 +#: cotisations/payment_methods/balance/models.py:86 +#: cotisations/validators.py:20 msgid "There is already a payment method for user balance." -msgstr "Il y a déjà un moyen de paiement pour le solde utilisateur." +msgstr "Il y a déjà un moyen de paiement pour le solde utilisateur." -#: payment_methods/cheque/models.py:36 -msgid "Cheque" -msgstr "Chèque" +#: cotisations/payment_methods/cheque/models.py:36 +msgid "cheque" +msgstr "chèque" -#: payment_methods/cheque/views.py:47 +#: cotisations/payment_methods/cheque/views.py:45 msgid "You can't pay this invoice with a cheque." msgstr "Vous ne pouvez pas payer cette facture avec un chèque." -#: payment_methods/comnpay/models.py:39 +#: cotisations/payment_methods/comnpay/models.py:39 msgid "ComNpay" msgstr "ComNpay" -#: payment_methods/comnpay/models.py:51 +#: cotisations/payment_methods/comnpay/models.py:48 msgid "ComNpay VAT Number" msgstr "Numéro de TVA de ComNpay" -#: payment_methods/comnpay/models.py:57 +#: cotisations/payment_methods/comnpay/models.py:51 msgid "ComNpay secret key" msgstr "Clé secrète de ComNpay" -#: payment_methods/comnpay/models.py:60 -msgid "Minimum payment" -msgstr "Paiement minimum" +#: cotisations/payment_methods/comnpay/models.py:54 +msgid "minimum payment" +msgstr "paiement minimum" -#: payment_methods/comnpay/models.py:61 -msgid "The minimal amount of money you have to use when paying with ComNpay" +#: cotisations/payment_methods/comnpay/models.py:56 +msgid "The minimal amount of money you have to use when paying with ComNpay." msgstr "" -"Le montant minimal d'agent que vous devez utiliser en payant avec ComNpay" +"Le montant minimal d'argent que vous devez utiliser en payant avec ComNpay." -#: payment_methods/comnpay/models.py:69 -msgid "Production mode enabled (production URL, instead of homologation)" -msgstr "Mode production activé (URL de production, au lieu d'homologation)" +#: cotisations/payment_methods/comnpay/models.py:66 +msgid "production mode enabled (production URL, instead of homologation)" +msgstr "mode production activé (URL de production, au lieu d'homologation)" -#: payment_methods/comnpay/models.py:102 +#: cotisations/payment_methods/comnpay/models.py:101 msgid "Pay invoice number " msgstr "Payer la facture numéro " -#: payment_methods/comnpay/models.py:114 +#: cotisations/payment_methods/comnpay/models.py:115 msgid "" "In order to pay your invoice with ComNpay, the price must be greater than {} " "€." msgstr "" "Pour payer votre facture avec ComNpay, le prix doit être plus grand que {} €." -#: payment_methods/comnpay/views.py:53 +#: cotisations/payment_methods/comnpay/views.py:53 #, python-format msgid "The payment of %(amount)s € was accepted." msgstr "Le paiement de %(amount)s € a été accepté." -#: payment_methods/comnpay/views.py:84 +#: cotisations/payment_methods/comnpay/views.py:80 msgid "The payment was refused." msgstr "Le paiment a été refusé." -#: payment_methods/forms.py:60 +#: cotisations/payment_methods/forms.py:59 msgid "Special payment method" msgstr "Moyen de paiement spécial" -#: payment_methods/forms.py:61 +#: cotisations/payment_methods/forms.py:61 msgid "" "Warning: you will not be able to change the payment method later. But you " "will be allowed to edit the other options." @@ -595,435 +605,482 @@ msgstr "" "Attention : vous ne pourrez pas changer le moyen de paiement plus tard. Mais " "vous pourrez modifier les autres options." -#: payment_methods/forms.py:72 -msgid "no" -msgstr "non" +#: cotisations/payment_methods/forms.py:74 +msgid "No" +msgstr "Non" -#: payment_methods/note_kfet/forms.py:32 -msgid "pseudo note" -msgstr "pseudo note" +#: cotisations/payment_methods/free/models.py:36 +msgid "Free payment" +msgstr "Paiement gratuit" -#: payment_methods/note_kfet/forms.py:35 +#: cotisations/payment_methods/free/models.py:54 +msgid "You can't pay this invoice for free." +msgstr "Vous ne pouvez pas payer cette facture gratuitement." + +#: cotisations/payment_methods/note_kfet/forms.py:33 +msgid "Username" +msgstr "Nom d'utilisateur" + +#: cotisations/payment_methods/note_kfet/forms.py:34 msgid "Password" msgstr "Mot de passe" -#: payment_methods/note_kfet/models.py:40 +#: cotisations/payment_methods/note_kfet/models.py:40 msgid "NoteKfet" msgstr "NoteKfet" -#: payment_methods/note_kfet/models.py:50 +#: cotisations/payment_methods/note_kfet/models.py:48 msgid "server" msgstr "serveur" -#: payment_methods/note_kfet/views.py:60 +#: cotisations/payment_methods/note_kfet/views.py:58 msgid "Unknown error." msgstr "Erreur inconnue." -#: payment_methods/note_kfet/views.py:88 +#: cotisations/payment_methods/note_kfet/views.py:87 msgid "The payment with note was done." msgstr "Le paiement par note a été effectué." -#: templates/cotisations/aff_article.html:34 +#: cotisations/templates/cotisations/aff_article.html:34 msgid "Price" msgstr "Prix" -#: templates/cotisations/aff_article.html:35 +#: cotisations/templates/cotisations/aff_article.html:35 msgid "Subscription type" msgstr "Type de cotisation" -#: templates/cotisations/aff_article.html:36 +#: cotisations/templates/cotisations/aff_article.html:36 msgid "Duration (in months)" msgstr "Durée (en mois)" -#: templates/cotisations/aff_article.html:37 +#: cotisations/templates/cotisations/aff_article.html:37 +msgid "Duration (in days)" +msgstr "Durée (en jours)" + +#: cotisations/templates/cotisations/aff_article.html:38 msgid "Concerned users" msgstr "Utilisateurs concernés" -#: templates/cotisations/aff_article.html:38 +#: cotisations/templates/cotisations/aff_article.html:39 +#: cotisations/templates/cotisations/aff_paiement.html:34 msgid "Available for everyone" msgstr "Disponible pour tous" -#: templates/cotisations/aff_banque.html:32 +#: cotisations/templates/cotisations/aff_banque.html:32 msgid "Bank" msgstr "Banque" -#: templates/cotisations/aff_cost_estimate.html:39 -#: templates/cotisations/aff_cotisations.html:41 -#: templates/cotisations/aff_custom_invoice.html:39 -#: templates/cotisations/control.html:63 -#: templates/cotisations/edit_facture.html:49 +#: cotisations/templates/cotisations/aff_cost_estimate.html:36 +#: cotisations/templates/cotisations/aff_custom_invoice.html:36 +msgid "Recipient" +msgstr "Destinataire" + +#: cotisations/templates/cotisations/aff_cost_estimate.html:39 +#: cotisations/templates/cotisations/aff_cotisations.html:41 +#: cotisations/templates/cotisations/aff_custom_invoice.html:39 +#: cotisations/templates/cotisations/control.html:63 +#: cotisations/templates/cotisations/edit_facture.html:49 msgid "Designation" msgstr "Désignation" -#: templates/cotisations/aff_cost_estimate.html:40 -#: templates/cotisations/aff_cotisations.html:42 -#: templates/cotisations/aff_custom_invoice.html:40 -#: templates/cotisations/control.html:64 +#: cotisations/templates/cotisations/aff_cost_estimate.html:40 +#: cotisations/templates/cotisations/aff_cotisations.html:42 +#: cotisations/templates/cotisations/aff_custom_invoice.html:40 +#: cotisations/templates/cotisations/control.html:64 msgid "Total price" msgstr "Prix total" -#: templates/cotisations/aff_cost_estimate.html:50 +#: cotisations/templates/cotisations/aff_cost_estimate.html:46 +#: cotisations/templates/cotisations/aff_cotisations.html:48 +#: cotisations/templates/cotisations/aff_custom_invoice.html:46 +#: cotisations/templates/cotisations/control.html:70 +msgid "Date" +msgstr "Date" + +#: cotisations/templates/cotisations/aff_cost_estimate.html:50 msgid "Validity" msgstr "Validité" -#: templates/cotisations/aff_cost_estimate.html:54 +#: cotisations/templates/cotisations/aff_cost_estimate.html:54 msgid "Cost estimate ID" msgstr "ID devis" -#: templates/cotisations/aff_cost_estimate.html:58 +#: cotisations/templates/cotisations/aff_cost_estimate.html:58 msgid "Invoice created" msgstr "Facture créée" -#: templates/cotisations/aff_cost_estimate.html:91 -#: templates/cotisations/aff_cotisations.html:81 -#: templates/cotisations/aff_custom_invoice.html:79 +#: cotisations/templates/cotisations/aff_cost_estimate.html:91 +#: cotisations/templates/cotisations/aff_cotisations.html:81 +#: cotisations/templates/cotisations/aff_custom_invoice.html:79 msgid "PDF" msgstr "PDF" -#: templates/cotisations/aff_cotisations.html:38 +#: cotisations/templates/cotisations/aff_cotisations.html:38 msgid "User" msgstr "Utilisateur" -#: templates/cotisations/aff_cotisations.html:52 -#: templates/cotisations/aff_custom_invoice.html:50 -#: templates/cotisations/control.html:56 +#: cotisations/templates/cotisations/aff_cotisations.html:52 +#: cotisations/templates/cotisations/aff_custom_invoice.html:50 +#: cotisations/templates/cotisations/control.html:56 msgid "Invoice ID" msgstr "ID facture" -#: templates/cotisations/aff_cotisations.html:71 +#: cotisations/templates/cotisations/aff_cotisations.html:71 msgid "Controlled invoice" msgstr "Facture contrôlée" -#: templates/cotisations/aff_cotisations.html:84 +#: cotisations/templates/cotisations/aff_cotisations.html:84 msgid "Invalidated invoice" msgstr "Facture invalidée" -#: templates/cotisations/aff_paiement.html:34 -msgid "Is available for everyone" -msgstr "Est disponible pour tous" +#: cotisations/templates/cotisations/aff_cotisations.html:88 +msgid "Voucher" +msgstr "Reçu" -#: templates/cotisations/aff_paiement.html:35 +#: cotisations/templates/cotisations/aff_custom_invoice.html:54 +msgid "Paid" +msgstr "Payé" + +#: cotisations/templates/cotisations/aff_paiement.html:33 +msgid "Payment type" +msgstr "Type de paiement" + +#: cotisations/templates/cotisations/aff_paiement.html:35 msgid "Custom payment method" msgstr "Moyen de paiement personnalisé" -#: templates/cotisations/control.html:30 +#: cotisations/templates/cotisations/control.html:30 msgid "Invoice control" msgstr "Contrôle des factures" -#: templates/cotisations/control.html:34 +#: cotisations/templates/cotisations/control.html:34 msgid "Invoice control and validation" msgstr "Contrôle et validation des factures" -#: templates/cotisations/control.html:46 +#: cotisations/templates/cotisations/control.html:46 msgid "Profile" msgstr "Profil" -#: templates/cotisations/control.html:48 -msgid "Last name" -msgstr "Nom" - -#: templates/cotisations/control.html:52 +#: cotisations/templates/cotisations/control.html:48 msgid "First name" msgstr "Prénom" -#: templates/cotisations/control.html:60 +#: cotisations/templates/cotisations/control.html:52 +msgid "Surname" +msgstr "Nom" + +#: cotisations/templates/cotisations/control.html:60 msgid "User ID" msgstr "ID utilisateur" -#: templates/cotisations/control.html:74 +#: cotisations/templates/cotisations/control.html:74 msgid "Validated" msgstr "Validé" -#: templates/cotisations/control.html:78 +#: cotisations/templates/cotisations/control.html:78 msgid "Controlled" msgstr "Contrôlé" -#: templates/cotisations/control.html:107 views.py:642 views.py:729 -#: views.py:809 -msgid "Edit" -msgstr "Modifier" - -#: templates/cotisations/delete.html:29 -msgid "Deletion of subscriptions" -msgstr "Suppression de cotisations" - -#: templates/cotisations/delete.html:36 -#, python-format -msgid "" -"Warning: are you sure you really want to delete this %(object_name)s object " -"( %(objet)s )?" -msgstr "" -"Attention: voulez-vous vraiment supprimer cet objet %(object_name)s " -"( %(objet)s ) ?" - -#: templates/cotisations/delete.html:38 -#: templates/cotisations/edit_facture.html:64 views.py:178 views.py:228 -#: views.py:280 +#: cotisations/templates/cotisations/control.html:107 +#: cotisations/templates/cotisations/delete.html:38 +#: cotisations/templates/cotisations/edit_facture.html:64 +#: cotisations/views.py:168 cotisations/views.py:222 cotisations/views.py:278 msgid "Confirm" msgstr "Confirmer" -#: templates/cotisations/edit_facture.html:31 -#: templates/cotisations/facture.html:30 +#: cotisations/templates/cotisations/delete.html:29 +msgid "Deletion of subscriptions" +msgstr "Suppression de cotisations" + +#: cotisations/templates/cotisations/delete.html:36 +#, python-format +msgid "" +"Warning: are you sure you really want to delete this %(objet_name)s object " +"( %(objet)s )?" +msgstr "" +"Attention: voulez-vous vraiment supprimer cet objet %(objet_name)s " +"( %(objet)s ) ?" + +#: cotisations/templates/cotisations/edit_facture.html:31 +#: cotisations/templates/cotisations/facture.html:30 msgid "Creation and editing of invoices" msgstr "Création et modification de factures" -#: templates/cotisations/edit_facture.html:41 +#: cotisations/templates/cotisations/edit_facture.html:41 msgid "Edit invoice" msgstr "Modifier la facture" -#: templates/cotisations/edit_facture.html:45 -#: templates/cotisations/facture.html:62 -#: templates/cotisations/index_article.html:30 +#: cotisations/templates/cotisations/edit_facture.html:45 +#: cotisations/templates/cotisations/facture.html:59 +#: cotisations/templates/cotisations/index_article.html:30 +#: cotisations/templates/cotisations/sidebar.html:55 msgid "Articles" msgstr "Articles" -#: templates/cotisations/facture.html:37 +#: cotisations/templates/cotisations/facture.html:37 msgid "Buy" msgstr "Acheter" -#: templates/cotisations/facture.html:40 +#: cotisations/templates/cotisations/facture.html:40 #, python-format msgid "Maximum allowed balance: %(max_balance)s €" msgstr "Solde maximum autorisé : %(max_balance)s €" -#: templates/cotisations/facture.html:44 +#: cotisations/templates/cotisations/facture.html:44 #, python-format msgid "Current balance: %(balance)s €" msgstr "Solde actuel : %(balance)s €" -#: templates/cotisations/facture.html:76 +#: cotisations/templates/cotisations/facture.html:73 msgid "Add an extra article" msgstr "Ajouter un article supplémentaire" -#: templates/cotisations/facture.html:82 +#: cotisations/templates/cotisations/facture.html:79 msgid "Total price: 0,00 €" msgstr "Prix total : 0,00 €" -#: templates/cotisations/index.html:29 templates/cotisations/sidebar.html:40 +#: cotisations/templates/cotisations/index.html:29 +#: cotisations/templates/cotisations/sidebar.html:40 msgid "Invoices" msgstr "Factures" -#: templates/cotisations/index.html:32 +#: cotisations/templates/cotisations/index.html:32 msgid "Subscriptions" msgstr "Cotisations" -#: templates/cotisations/index_article.html:33 -msgid "List of article types" -msgstr "Liste des types d'article" +#: cotisations/templates/cotisations/index_article.html:33 +msgid "List of articles" +msgstr "Liste des articles" -#: templates/cotisations/index_article.html:36 -msgid "Add an article type" -msgstr "Ajouter un type d'article" +#: cotisations/templates/cotisations/index_article.html:36 +msgid "Add an article" +msgstr "Ajouter un article" -#: templates/cotisations/index_article.html:40 -msgid "Delete one or several article types" -msgstr "Supprimer un ou plusieurs types d'article" +#: cotisations/templates/cotisations/index_article.html:40 +msgid "Delete one or several articles" +msgstr "Supprimer un ou plusieurs articles" -#: templates/cotisations/index_banque.html:30 -#: templates/cotisations/sidebar.html:60 +#: cotisations/templates/cotisations/index_banque.html:30 +#: cotisations/templates/cotisations/sidebar.html:60 msgid "Banks" msgstr "Banques" -#: templates/cotisations/index_banque.html:33 +#: cotisations/templates/cotisations/index_banque.html:33 msgid "List of banks" msgstr "Liste des banques" -#: templates/cotisations/index_banque.html:36 +#: cotisations/templates/cotisations/index_banque.html:36 msgid "Add a bank" msgstr "Ajouter une banque" -#: templates/cotisations/index_banque.html:40 +#: cotisations/templates/cotisations/index_banque.html:40 msgid "Delete one or several banks" msgstr "Supprimer une ou plusieurs banques" -#: templates/cotisations/index_cost_estimate.html:28 -#: templates/cotisations/sidebar.html:50 +#: cotisations/templates/cotisations/index_cost_estimate.html:28 +#: cotisations/templates/cotisations/sidebar.html:50 msgid "Cost estimates" msgstr "Devis" -#: templates/cotisations/index_cost_estimate.html:31 +#: cotisations/templates/cotisations/index_cost_estimate.html:31 msgid "List of cost estimates" msgstr "Liste des devis" -#: templates/cotisations/index_custom_invoice.html:28 -#: templates/cotisations/sidebar.html:45 +#: cotisations/templates/cotisations/index_custom_invoice.html:28 +#: cotisations/templates/cotisations/sidebar.html:45 msgid "Custom invoices" msgstr "Factures personnalisées" -#: templates/cotisations/index_custom_invoice.html:31 +#: cotisations/templates/cotisations/index_custom_invoice.html:31 msgid "List of custom invoices" msgstr "Liste des factures personnalisées" -#: templates/cotisations/index_paiement.html:30 -#: templates/cotisations/sidebar.html:65 +#: cotisations/templates/cotisations/index_paiement.html:30 +#: cotisations/templates/cotisations/sidebar.html:65 msgid "Payment methods" msgstr "Moyens de paiement" -#: templates/cotisations/index_paiement.html:33 +#: cotisations/templates/cotisations/index_paiement.html:33 msgid "List of payment methods" msgstr "Liste des moyens de paiement" -#: templates/cotisations/index_paiement.html:36 +#: cotisations/templates/cotisations/index_paiement.html:36 msgid "Add a payment method" msgstr "Ajouter un moyen de paiement" -#: templates/cotisations/index_paiement.html:40 +#: cotisations/templates/cotisations/index_paiement.html:40 msgid "Delete one or several payment methods" msgstr "Supprimer un ou plusieurs moyens de paiement" -#: templates/cotisations/payment.html:30 +#: cotisations/templates/cotisations/payment.html:30 msgid "Balance refill" msgstr "Rechargement de solde" -#: templates/cotisations/payment.html:34 +#: cotisations/templates/cotisations/payment.html:34 #, python-format msgid "Pay %(amount)s €" msgstr "Payer %(amount)s €" -#: templates/cotisations/payment.html:42 views.py:1049 +#: cotisations/templates/cotisations/payment.html:42 cotisations/views.py:1028 msgid "Pay" msgstr "Payer" -#: templates/cotisations/sidebar.html:32 +#: cotisations/templates/cotisations/sidebar.html:32 msgid "Create an invoice" msgstr "Créer une facture" -#: templates/cotisations/sidebar.html:35 +#: cotisations/templates/cotisations/sidebar.html:35 msgid "Control the invoices" msgstr "Contrôler les factures" -#: views.py:164 +#: cotisations/views.py:155 msgid "You need to choose at least one article." msgstr "Vous devez choisir au moins un article." -#: views.py:222 +#: cotisations/views.py:169 +msgid "New invoice" +msgstr "Nouvelle facture" + +#: cotisations/views.py:216 msgid "The cost estimate was created." msgstr "Le devis a été créé." -#: views.py:232 views.py:534 -msgid "Cost estimate" -msgstr "Devis" +#: cotisations/views.py:226 +msgid "New cost estimate" +msgstr "Nouveau devis" -#: views.py:274 +#: cotisations/views.py:272 msgid "The custom invoice was created." msgstr "La facture personnalisée a été créée." -#: views.py:363 views.py:466 +#: cotisations/views.py:282 +msgid "New custom invoice" +msgstr "Nouvelle facture personnalisée" + +#: cotisations/views.py:357 cotisations/views.py:438 msgid "The invoice was edited." msgstr "La facture a été modifiée." -#: views.py:383 views.py:589 +#: cotisations/views.py:375 cotisations/views.py:570 msgid "The invoice was deleted." msgstr "La facture a été supprimée." -#: views.py:388 views.py:594 -msgid "Invoice" -msgstr "Facture" - -#: views.py:417 +#: cotisations/views.py:398 msgid "The cost estimate was edited." msgstr "Le devis a été modifié." -#: views.py:424 +#: cotisations/views.py:405 msgid "Edit cost estimate" msgstr "Modifier le devis" -#: views.py:436 +#: cotisations/views.py:419 msgid "An invoice was successfully created from your cost estimate." msgstr "Une facture a bien été créée à partir de votre devis." -#: views.py:529 +#: cotisations/views.py:445 +msgid "Edit custom invoice" +msgstr "Modifier la facture personnalisée" + +#: cotisations/views.py:507 msgid "The cost estimate was deleted." msgstr "Le devis a été supprimé." -#: views.py:615 +#: cotisations/views.py:510 +msgid "cost estimate" +msgstr "devis" + +#: cotisations/views.py:594 msgid "The article was created." msgstr "L'article a été créé." -#: views.py:620 views.py:693 views.py:786 +#: cotisations/views.py:599 cotisations/views.py:673 cotisations/views.py:767 msgid "Add" msgstr "Ajouter" -#: views.py:621 +#: cotisations/views.py:600 msgid "New article" msgstr "Nouvel article" -#: views.py:637 +#: cotisations/views.py:617 msgid "The article was edited." msgstr "L'article a été modifié." -#: views.py:643 +#: cotisations/views.py:622 cotisations/views.py:705 cotisations/views.py:791 +msgid "Edit" +msgstr "Modifier" + +#: cotisations/views.py:623 msgid "Edit article" msgstr "Modifier l'article" -#: views.py:659 +#: cotisations/views.py:640 msgid "The articles were deleted." msgstr "Les articles ont été supprimés." -#: views.py:664 views.py:764 views.py:844 +#: cotisations/views.py:645 cotisations/views.py:744 cotisations/views.py:829 msgid "Delete" msgstr "Supprimer" -#: views.py:665 +#: cotisations/views.py:646 msgid "Delete article" msgstr "Supprimer l'article" -#: views.py:687 +#: cotisations/views.py:667 msgid "The payment method was created." msgstr "Le moyen de paiment a été créé." -#: views.py:694 +#: cotisations/views.py:674 msgid "New payment method" msgstr "Nouveau moyen de paiement" -#: views.py:723 +#: cotisations/views.py:699 msgid "The payment method was edited." msgstr "Le moyen de paiment a été modifié." -#: views.py:730 +#: cotisations/views.py:706 msgid "Edit payment method" msgstr "Modifier le moyen de paiement" -#: views.py:749 +#: cotisations/views.py:728 #, python-format msgid "The payment method %(method_name)s was deleted." msgstr "Le moyen de paiement %(method_name)s a été supprimé." -#: views.py:756 +#: cotisations/views.py:735 #, python-format msgid "" -"The payment method %(method_name)s can't be deleted " -"because there are invoices using it." +"The payment method %(method_name)s can't be deleted because there are " +"invoices using it." msgstr "" "Le moyen de paiement %(method_name)s ne peut pas être supprimé car il y a " "des factures qui l'utilisent." -#: views.py:765 +#: cotisations/views.py:745 msgid "Delete payment method" msgstr "Supprimer le moyen de paiement" -#: views.py:781 +#: cotisations/views.py:762 msgid "The bank was created." msgstr "La banque a été créée." -#: views.py:787 +#: cotisations/views.py:768 msgid "New bank" msgstr "Nouvelle banque" -#: views.py:804 +#: cotisations/views.py:786 msgid "The bank was edited." msgstr "La banque a été modifiée." -#: views.py:810 +#: cotisations/views.py:792 msgid "Edit bank" msgstr "Modifier la banque" -#: views.py:829 +#: cotisations/views.py:814 #, python-format msgid "The bank %(bank_name)s was deleted." msgstr "La banque %(bank_name)s a été supprimée." -#: views.py:836 +#: cotisations/views.py:820 #, python-format msgid "" "The bank %(bank_name)s can't be deleted because there are invoices using it." @@ -1031,18 +1088,22 @@ msgstr "" "La banque %(bank_name)s ne peut pas être supprimée car il y a des factures " "qui l'utilisent." -#: views.py:845 +#: cotisations/views.py:830 msgid "Delete bank" msgstr "Supprimer la banque" -#: views.py:881 +#: cotisations/views.py:864 msgid "Your changes have been properly taken into account." msgstr "Vos modifications ont correctement été prises en compte." -#: views.py:1016 +#: cotisations/views.py:996 msgid "You are not allowed to credit your balance." -msgstr "Vous n'êtes pas autorisés à créditer votre solde." +msgstr "Vous n'êtes pas autorisé à créditer votre solde." -#: views.py:1048 +#: cotisations/views.py:1027 msgid "Refill your balance" msgstr "Recharger votre solde" + +#: cotisations/views.py:1046 +msgid "Could not find a voucher for that invoice." +msgstr "Impossible de trouver un reçu pour cette facture." diff --git a/cotisations/migrations/0001_initial.py b/cotisations/migrations/0001_initial.py index 63df0077..0ffe83c9 100644 --- a/cotisations/migrations/0001_initial.py +++ b/cotisations/migrations/0001_initial.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,54 +29,100 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('users', '0005_auto_20160702_0006'), - ] + dependencies = [("users", "0005_auto_20160702_0006")] operations = [ migrations.CreateModel( - name='Article', + name="Article", fields=[ - ('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)), - ('name', models.CharField(max_length=255)), - ('prix', models.DecimalField(decimal_places=2, max_digits=5)), + ( + "id", + models.AutoField( + verbose_name="ID", + auto_created=True, + primary_key=True, + serialize=False, + ), + ), + ("name", models.CharField(max_length=255)), + ("prix", models.DecimalField(decimal_places=2, max_digits=5)), ], ), migrations.CreateModel( - name='Banque', + name="Banque", fields=[ - ('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)), - ('name', models.CharField(max_length=255)), + ( + "id", + models.AutoField( + verbose_name="ID", + auto_created=True, + primary_key=True, + serialize=False, + ), + ), + ("name", models.CharField(max_length=255)), ], ), migrations.CreateModel( - name='Facture', + name="Facture", fields=[ - ('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)), - ('cheque', models.CharField(max_length=255)), - ('number', models.IntegerField()), - ('date', models.DateTimeField(auto_now_add=True)), - ('name', models.CharField(max_length=255)), - ('prix', models.DecimalField(decimal_places=2, max_digits=5)), - ('article', models.ForeignKey(to='cotisations.Article', on_delete=django.db.models.deletion.PROTECT)), - ('banque', models.ForeignKey(to='cotisations.Banque', on_delete=django.db.models.deletion.PROTECT)), + ( + "id", + models.AutoField( + verbose_name="ID", + auto_created=True, + primary_key=True, + serialize=False, + ), + ), + ("cheque", models.CharField(max_length=255)), + ("number", models.IntegerField()), + ("date", models.DateTimeField(auto_now_add=True)), + ("name", models.CharField(max_length=255)), + ("prix", models.DecimalField(decimal_places=2, max_digits=5)), + ( + "article", + models.ForeignKey( + to="cotisations.Article", + on_delete=django.db.models.deletion.PROTECT, + ), + ), + ( + "banque", + models.ForeignKey( + to="cotisations.Banque", + on_delete=django.db.models.deletion.PROTECT, + ), + ), ], ), migrations.CreateModel( - name='Paiement', + name="Paiement", fields=[ - ('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)), - ('moyen', models.CharField(max_length=255)), + ( + "id", + models.AutoField( + verbose_name="ID", + auto_created=True, + primary_key=True, + serialize=False, + ), + ), + ("moyen", models.CharField(max_length=255)), ], ), migrations.AddField( - model_name='facture', - name='paiement', - field=models.ForeignKey(to='cotisations.Paiement', on_delete=django.db.models.deletion.PROTECT), + model_name="facture", + name="paiement", + field=models.ForeignKey( + to="cotisations.Paiement", on_delete=django.db.models.deletion.PROTECT + ), ), migrations.AddField( - model_name='facture', - name='user', - field=models.ForeignKey(to='users.User', on_delete=django.db.models.deletion.PROTECT), + model_name="facture", + name="user", + field=models.ForeignKey( + to="users.User", on_delete=django.db.models.deletion.PROTECT + ), ), ] diff --git a/cotisations/migrations/0002_remove_facture_article.py b/cotisations/migrations/0002_remove_facture_article.py index 1122ac5f..5a4e68f1 100644 --- a/cotisations/migrations/0002_remove_facture_article.py +++ b/cotisations/migrations/0002_remove_facture_article.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,13 +28,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0001_initial'), - ] + dependencies = [("cotisations", "0001_initial")] - operations = [ - migrations.RemoveField( - model_name='facture', - name='article', - ), - ] + operations = [migrations.RemoveField(model_name="facture", name="article")] diff --git a/cotisations/migrations/0003_auto_20160702_1448.py b/cotisations/migrations/0003_auto_20160702_1448.py index dd611edc..29c4f180 100644 --- a/cotisations/migrations/0003_auto_20160702_1448.py +++ b/cotisations/migrations/0003_auto_20160702_1448.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,14 +29,17 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0002_remove_facture_article'), - ] + dependencies = [("cotisations", "0002_remove_facture_article")] operations = [ migrations.AlterField( - model_name='facture', - name='banque', - field=models.ForeignKey(blank=True, to='cotisations.Banque', on_delete=django.db.models.deletion.PROTECT, null=True), - ), + model_name="facture", + name="banque", + field=models.ForeignKey( + blank=True, + to="cotisations.Banque", + on_delete=django.db.models.deletion.PROTECT, + null=True, + ), + ) ] diff --git a/cotisations/migrations/0004_auto_20160702_1528.py b/cotisations/migrations/0004_auto_20160702_1528.py index 4489008c..d0181a64 100644 --- a/cotisations/migrations/0004_auto_20160702_1528.py +++ b/cotisations/migrations/0004_auto_20160702_1528.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,19 +28,17 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0003_auto_20160702_1448'), - ] + dependencies = [("cotisations", "0003_auto_20160702_1448")] operations = [ migrations.AlterField( - model_name='facture', - name='name', + model_name="facture", + name="name", field=models.CharField(null=True, max_length=255), ), migrations.AlterField( - model_name='facture', - name='prix', + model_name="facture", + name="prix", field=models.DecimalField(max_digits=5, null=True, decimal_places=2), ), ] diff --git a/cotisations/migrations/0005_auto_20160702_1532.py b/cotisations/migrations/0005_auto_20160702_1532.py index 4042fe9c..6017ec59 100644 --- a/cotisations/migrations/0005_auto_20160702_1532.py +++ b/cotisations/migrations/0005_auto_20160702_1532.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0004_auto_20160702_1528'), - ] + dependencies = [("cotisations", "0004_auto_20160702_1528")] operations = [ migrations.AlterField( - model_name='facture', - name='cheque', + model_name="facture", + name="cheque", field=models.CharField(max_length=255, blank=True), - ), + ) ] diff --git a/cotisations/migrations/0006_auto_20160702_1534.py b/cotisations/migrations/0006_auto_20160702_1534.py index b55edebd..750fd5b2 100644 --- a/cotisations/migrations/0006_auto_20160702_1534.py +++ b/cotisations/migrations/0006_auto_20160702_1534.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,19 +28,19 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0005_auto_20160702_1532'), - ] + dependencies = [("cotisations", "0005_auto_20160702_1532")] operations = [ migrations.AlterField( - model_name='facture', - name='name', - field=models.CharField(null=True, default='plop', max_length=255), + model_name="facture", + name="name", + field=models.CharField(null=True, default="plop", max_length=255), ), migrations.AlterField( - model_name='facture', - name='prix', - field=models.DecimalField(null=True, decimal_places=2, default=1, max_digits=5), + model_name="facture", + name="prix", + field=models.DecimalField( + null=True, decimal_places=2, default=1, max_digits=5 + ), ), ] diff --git a/cotisations/migrations/0007_auto_20160702_1543.py b/cotisations/migrations/0007_auto_20160702_1543.py index ca0507ec..0755befb 100644 --- a/cotisations/migrations/0007_auto_20160702_1543.py +++ b/cotisations/migrations/0007_auto_20160702_1543.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,20 +28,18 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0006_auto_20160702_1534'), - ] + dependencies = [("cotisations", "0006_auto_20160702_1534")] operations = [ migrations.AlterField( - model_name='facture', - name='name', - field=models.CharField(default='plop', max_length=255), + model_name="facture", + name="name", + field=models.CharField(default="plop", max_length=255), preserve_default=False, ), migrations.AlterField( - model_name='facture', - name='prix', + model_name="facture", + name="prix", field=models.DecimalField(default=1, max_digits=5, decimal_places=2), preserve_default=False, ), diff --git a/cotisations/migrations/0008_auto_20160702_1614.py b/cotisations/migrations/0008_auto_20160702_1614.py index 63ae27e9..c3490ceb 100644 --- a/cotisations/migrations/0008_auto_20160702_1614.py +++ b/cotisations/migrations/0008_auto_20160702_1614.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -30,43 +30,53 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('users', '0005_auto_20160702_0006'), - ('cotisations', '0007_auto_20160702_1543'), + ("users", "0005_auto_20160702_0006"), + ("cotisations", "0007_auto_20160702_1543"), ] operations = [ migrations.CreateModel( - name='Cotisation', + name="Cotisation", fields=[ - ('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)), - ('date_start', models.DateTimeField(auto_now_add=True)), - ('date_end', models.DateTimeField()), + ( + "id", + models.AutoField( + verbose_name="ID", + primary_key=True, + serialize=False, + auto_created=True, + ), + ), + ("date_start", models.DateTimeField(auto_now_add=True)), + ("date_end", models.DateTimeField()), ], ), migrations.AddField( - model_name='article', - name='cotisation', + model_name="article", + name="cotisation", field=models.BooleanField(default=True), preserve_default=False, ), migrations.AddField( - model_name='article', - name='duration', + model_name="article", + name="duration", field=models.DurationField(blank=True, null=True), ), migrations.AddField( - model_name='facture', - name='valid', - field=models.BooleanField(default=True), + model_name="facture", name="valid", field=models.BooleanField(default=True) ), migrations.AddField( - model_name='cotisation', - name='facture', - field=models.ForeignKey(to='cotisations.Facture', on_delete=django.db.models.deletion.PROTECT), + model_name="cotisation", + name="facture", + field=models.ForeignKey( + to="cotisations.Facture", on_delete=django.db.models.deletion.PROTECT + ), ), migrations.AddField( - model_name='cotisation', - name='user', - field=models.ForeignKey(to='users.User', on_delete=django.db.models.deletion.PROTECT), + model_name="cotisation", + name="user", + field=models.ForeignKey( + to="users.User", on_delete=django.db.models.deletion.PROTECT + ), ), ] diff --git a/cotisations/migrations/0009_remove_cotisation_user.py b/cotisations/migrations/0009_remove_cotisation_user.py index b93f9f70..784e6205 100644 --- a/cotisations/migrations/0009_remove_cotisation_user.py +++ b/cotisations/migrations/0009_remove_cotisation_user.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,13 +28,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0008_auto_20160702_1614'), - ] + dependencies = [("cotisations", "0008_auto_20160702_1614")] - operations = [ - migrations.RemoveField( - model_name='cotisation', - name='user', - ), - ] + operations = [migrations.RemoveField(model_name="cotisation", name="user")] diff --git a/cotisations/migrations/0010_auto_20160702_1840.py b/cotisations/migrations/0010_auto_20160702_1840.py index 76780e28..cdae642e 100644 --- a/cotisations/migrations/0010_auto_20160702_1840.py +++ b/cotisations/migrations/0010_auto_20160702_1840.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,18 +28,15 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0009_remove_cotisation_user'), - ] + dependencies = [("cotisations", "0009_remove_cotisation_user")] operations = [ - migrations.RemoveField( - model_name='article', - name='duration', - ), + migrations.RemoveField(model_name="article", name="duration"), migrations.AddField( - model_name='article', - name='duration', - field=models.IntegerField(null=True, help_text='Durée exprimée en mois entiers', blank=True), + model_name="article", + name="duration", + field=models.IntegerField( + null=True, help_text="Durée exprimée en mois entiers", blank=True + ), ), ] diff --git a/cotisations/migrations/0011_auto_20160702_1911.py b/cotisations/migrations/0011_auto_20160702_1911.py index 250ef78c..fec46c36 100644 --- a/cotisations/migrations/0011_auto_20160702_1911.py +++ b/cotisations/migrations/0011_auto_20160702_1911.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,10 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0010_auto_20160702_1840'), - ] + dependencies = [("cotisations", "0010_auto_20160702_1840")] operations = [ migrations.AlterField( - model_name='cotisation', - name='date_start', - field=models.DateTimeField(), - ), + model_name="cotisation", name="date_start", field=models.DateTimeField() + ) ] diff --git a/cotisations/migrations/0012_auto_20160704_0118.py b/cotisations/migrations/0012_auto_20160704_0118.py index 056f21fd..0e2ac7ae 100644 --- a/cotisations/migrations/0012_auto_20160704_0118.py +++ b/cotisations/migrations/0012_auto_20160704_0118.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,14 +29,14 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0011_auto_20160702_1911'), - ] + dependencies = [("cotisations", "0011_auto_20160702_1911")] operations = [ migrations.AlterField( - model_name='cotisation', - name='facture', - field=models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to='cotisations.Facture'), - ), + model_name="cotisation", + name="facture", + field=models.OneToOneField( + on_delete=django.db.models.deletion.PROTECT, to="cotisations.Facture" + ), + ) ] diff --git a/cotisations/migrations/0013_auto_20160711_2240.py b/cotisations/migrations/0013_auto_20160711_2240.py index c4a636c4..cfff6a7a 100644 --- a/cotisations/migrations/0013_auto_20160711_2240.py +++ b/cotisations/migrations/0013_auto_20160711_2240.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,32 +29,41 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0012_auto_20160704_0118'), - ] + dependencies = [("cotisations", "0012_auto_20160704_0118")] operations = [ migrations.CreateModel( - name='Vente', + name="Vente", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('prix', models.DecimalField(decimal_places=2, max_digits=5)), - ('cotisation', models.BooleanField()), - ('duration', models.IntegerField(null=True, blank=True, help_text='Durée exprimée en mois entiers')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)), + ("prix", models.DecimalField(decimal_places=2, max_digits=5)), + ("cotisation", models.BooleanField()), + ( + "duration", + models.IntegerField( + null=True, + blank=True, + help_text="Durée exprimée en mois entiers", + ), + ), ], ), - migrations.RemoveField( - model_name='facture', - name='name', - ), - migrations.RemoveField( - model_name='facture', - name='prix', - ), + migrations.RemoveField(model_name="facture", name="name"), + migrations.RemoveField(model_name="facture", name="prix"), migrations.AddField( - model_name='vente', - name='facture', - field=models.ForeignKey(to='cotisations.Facture', on_delete=django.db.models.deletion.PROTECT), + model_name="vente", + name="facture", + field=models.ForeignKey( + to="cotisations.Facture", on_delete=django.db.models.deletion.PROTECT + ), ), ] diff --git a/cotisations/migrations/0014_auto_20160712_0245.py b/cotisations/migrations/0014_auto_20160712_0245.py index fa4c3af7..1a7bd48a 100644 --- a/cotisations/migrations/0014_auto_20160712_0245.py +++ b/cotisations/migrations/0014_auto_20160712_0245.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,18 +28,13 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0013_auto_20160711_2240'), - ] + dependencies = [("cotisations", "0013_auto_20160711_2240")] operations = [ - migrations.RemoveField( - model_name='facture', - name='number', - ), + migrations.RemoveField(model_name="facture", name="number"), migrations.AddField( - model_name='vente', - name='number', + model_name="vente", + name="number", field=models.IntegerField(default=1), preserve_default=False, ), diff --git a/cotisations/migrations/0015_auto_20160714_2142.py b/cotisations/migrations/0015_auto_20160714_2142.py index 7e666e4d..636f138f 100644 --- a/cotisations/migrations/0015_auto_20160714_2142.py +++ b/cotisations/migrations/0015_auto_20160714_2142.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,29 +29,29 @@ import django.core.validators class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0014_auto_20160712_0245'), - ] + dependencies = [("cotisations", "0014_auto_20160712_0245")] operations = [ migrations.AddField( - model_name='facture', - name='control', + model_name="facture", + name="control", field=models.BooleanField(default=False), ), migrations.AlterField( - model_name='cotisation', - name='facture', - field=models.OneToOneField(to='cotisations.Facture'), + model_name="cotisation", + name="facture", + field=models.OneToOneField(to="cotisations.Facture"), ), migrations.AlterField( - model_name='vente', - name='facture', - field=models.ForeignKey(to='cotisations.Facture'), + model_name="vente", + name="facture", + field=models.ForeignKey(to="cotisations.Facture"), ), migrations.AlterField( - model_name='vente', - name='number', - field=models.IntegerField(validators=[django.core.validators.MinValueValidator(1)]), + model_name="vente", + name="number", + field=models.IntegerField( + validators=[django.core.validators.MinValueValidator(1)] + ), ), ] diff --git a/cotisations/migrations/0016_auto_20160715_0110.py b/cotisations/migrations/0016_auto_20160715_0110.py index 205cdb3d..f7e00396 100644 --- a/cotisations/migrations/0016_auto_20160715_0110.py +++ b/cotisations/migrations/0016_auto_20160715_0110.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,29 +28,20 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0015_auto_20160714_2142'), - ] + dependencies = [("cotisations", "0015_auto_20160714_2142")] operations = [ migrations.RenameField( - model_name='article', - old_name='cotisation', - new_name='iscotisation', + model_name="article", old_name="cotisation", new_name="iscotisation" ), migrations.RenameField( - model_name='vente', - old_name='cotisation', - new_name='iscotisation', - ), - migrations.RemoveField( - model_name='cotisation', - name='facture', + model_name="vente", old_name="cotisation", new_name="iscotisation" ), + migrations.RemoveField(model_name="cotisation", name="facture"), migrations.AddField( - model_name='cotisation', - name='vente', - field=models.OneToOneField(to='cotisations.Vente', null=True), + model_name="cotisation", + name="vente", + field=models.OneToOneField(to="cotisations.Vente", null=True), preserve_default=False, ), ] diff --git a/cotisations/migrations/0017_auto_20170718_2329.py b/cotisations/migrations/0017_auto_20170718_2329.py index 4980bb1e..585a3ea0 100644 --- a/cotisations/migrations/0017_auto_20170718_2329.py +++ b/cotisations/migrations/0017_auto_20170718_2329.py @@ -8,19 +8,22 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0016_auto_20160715_0110'), - ] + dependencies = [("cotisations", "0016_auto_20160715_0110")] operations = [ migrations.AlterField( - model_name='article', - name='duration', - field=models.IntegerField(blank=True, help_text='Durée exprimée en mois entiers', null=True, validators=[django.core.validators.MinValueValidator(0)]), + model_name="article", + name="duration", + field=models.IntegerField( + blank=True, + help_text="Durée exprimée en mois entiers", + null=True, + validators=[django.core.validators.MinValueValidator(0)], + ), ), migrations.AlterField( - model_name='article', - name='name', + model_name="article", + name="name", field=models.CharField(max_length=255, unique=True), ), ] diff --git a/cotisations/migrations/0018_paiement_type_paiement.py b/cotisations/migrations/0018_paiement_type_paiement.py index 488fcf47..8f23742f 100644 --- a/cotisations/migrations/0018_paiement_type_paiement.py +++ b/cotisations/migrations/0018_paiement_type_paiement.py @@ -7,15 +7,17 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0017_auto_20170718_2329'), - ] + dependencies = [("cotisations", "0017_auto_20170718_2329")] operations = [ migrations.AddField( - model_name='paiement', - name='type_paiement', - field=models.CharField(choices=[('check', 'Chèque'), (None, 'Autre')], default=None, max_length=255), + model_name="paiement", + name="type_paiement", + field=models.CharField( + choices=[("check", "Chèque"), (None, "Autre")], + default=None, + max_length=255, + ), preserve_default=False, - ), + ) ] diff --git a/cotisations/migrations/0019_auto_20170819_0055.py b/cotisations/migrations/0019_auto_20170819_0055.py index e4794a01..66a35e3a 100644 --- a/cotisations/migrations/0019_auto_20170819_0055.py +++ b/cotisations/migrations/0019_auto_20170819_0055.py @@ -7,14 +7,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0018_paiement_type_paiement'), - ] + dependencies = [("cotisations", "0018_paiement_type_paiement")] operations = [ migrations.AlterField( - model_name='paiement', - name='type_paiement', - field=models.CharField(choices=[(0, 'Autre'), (1, 'Chèque')], default=0, max_length=255), - ), + model_name="paiement", + name="type_paiement", + field=models.CharField( + choices=[(0, "Autre"), (1, "Chèque")], default=0, max_length=255 + ), + ) ] diff --git a/cotisations/migrations/0020_auto_20170819_0057.py b/cotisations/migrations/0020_auto_20170819_0057.py index c0a319e8..9e52adaf 100644 --- a/cotisations/migrations/0020_auto_20170819_0057.py +++ b/cotisations/migrations/0020_auto_20170819_0057.py @@ -7,14 +7,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0019_auto_20170819_0055'), - ] + dependencies = [("cotisations", "0019_auto_20170819_0055")] operations = [ migrations.AlterField( - model_name='paiement', - name='type_paiement', - field=models.IntegerField(choices=[(0, 'Autre'), (1, 'Chèque')], default=0, max_length=255), - ), + model_name="paiement", + name="type_paiement", + field=models.IntegerField( + choices=[(0, "Autre"), (1, "Chèque")], default=0, max_length=255 + ), + ) ] diff --git a/cotisations/migrations/0021_auto_20170819_0104.py b/cotisations/migrations/0021_auto_20170819_0104.py index 1f165a44..f6b95642 100644 --- a/cotisations/migrations/0021_auto_20170819_0104.py +++ b/cotisations/migrations/0021_auto_20170819_0104.py @@ -7,14 +7,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0020_auto_20170819_0057'), - ] + dependencies = [("cotisations", "0020_auto_20170819_0057")] operations = [ migrations.AlterField( - model_name='paiement', - name='type_paiement', - field=models.IntegerField(choices=[(0, 'Autre'), (1, 'Chèque')], default=0), - ), + model_name="paiement", + name="type_paiement", + field=models.IntegerField(choices=[(0, "Autre"), (1, "Chèque")], default=0), + ) ] diff --git a/cotisations/migrations/0022_auto_20170824_0128.py b/cotisations/migrations/0022_auto_20170824_0128.py index bd605aee..be932b14 100644 --- a/cotisations/migrations/0022_auto_20170824_0128.py +++ b/cotisations/migrations/0022_auto_20170824_0128.py @@ -7,14 +7,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0021_auto_20170819_0104'), - ] + dependencies = [("cotisations", "0021_auto_20170819_0104")] operations = [ migrations.AlterField( - model_name='paiement', - name='type_paiement', - field=models.IntegerField(choices=[(0, 'Autre'), (1, 'Chèque')], default=0, max_length=255), - ), + model_name="paiement", + name="type_paiement", + field=models.IntegerField( + choices=[(0, "Autre"), (1, "Chèque")], default=0, max_length=255 + ), + ) ] diff --git a/cotisations/migrations/0023_auto_20170902_1303.py b/cotisations/migrations/0023_auto_20170902_1303.py index 04453c18..f9a60526 100644 --- a/cotisations/migrations/0023_auto_20170902_1303.py +++ b/cotisations/migrations/0023_auto_20170902_1303.py @@ -7,14 +7,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0022_auto_20170824_0128'), - ] + dependencies = [("cotisations", "0022_auto_20170824_0128")] operations = [ migrations.AlterField( - model_name='paiement', - name='type_paiement', - field=models.IntegerField(choices=[(0, 'Autre'), (1, 'Chèque')], default=0), - ), + model_name="paiement", + name="type_paiement", + field=models.IntegerField(choices=[(0, "Autre"), (1, "Chèque")], default=0), + ) ] diff --git a/cotisations/migrations/0024_auto_20171015_2033.py b/cotisations/migrations/0024_auto_20171015_2033.py index b52dad62..c9900946 100644 --- a/cotisations/migrations/0024_auto_20171015_2033.py +++ b/cotisations/migrations/0024_auto_20171015_2033.py @@ -8,19 +8,24 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0023_auto_20170902_1303'), - ] + dependencies = [("cotisations", "0023_auto_20170902_1303")] operations = [ migrations.AlterField( - model_name='article', - name='duration', - field=models.PositiveIntegerField(blank=True, help_text='Durée exprimée en mois entiers', null=True, validators=[django.core.validators.MinValueValidator(0)]), + model_name="article", + name="duration", + field=models.PositiveIntegerField( + blank=True, + help_text="Durée exprimée en mois entiers", + null=True, + validators=[django.core.validators.MinValueValidator(0)], + ), ), migrations.AlterField( - model_name='vente', - name='duration', - field=models.PositiveIntegerField(blank=True, help_text='Durée exprimée en mois entiers', null=True), + model_name="vente", + name="duration", + field=models.PositiveIntegerField( + blank=True, help_text="Durée exprimée en mois entiers", null=True + ), ), ] diff --git a/cotisations/migrations/0025_article_type_user.py b/cotisations/migrations/0025_article_type_user.py index 5ad329fa..86e5567b 100644 --- a/cotisations/migrations/0025_article_type_user.py +++ b/cotisations/migrations/0025_article_type_user.py @@ -7,14 +7,16 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0024_auto_20171015_2033'), - ] + dependencies = [("cotisations", "0024_auto_20171015_2033")] operations = [ migrations.AddField( - model_name='article', - name='type_user', - field=models.CharField(choices=[('Adherent', 'Adherent'), ('Club', 'Club'), ('All', 'All')], default='All', max_length=255), - ), + model_name="article", + name="type_user", + field=models.CharField( + choices=[("Adherent", "Adherent"), ("Club", "Club"), ("All", "All")], + default="All", + max_length=255, + ), + ) ] diff --git a/cotisations/migrations/0026_auto_20171028_0126.py b/cotisations/migrations/0026_auto_20171028_0126.py index df44bb89..d9a2e8db 100644 --- a/cotisations/migrations/0026_auto_20171028_0126.py +++ b/cotisations/migrations/0026_auto_20171028_0126.py @@ -6,73 +6,94 @@ from django.db import migrations, models def create_type(apps, schema_editor): - Cotisation = apps.get_model('cotisations', 'Cotisation') - Vente = apps.get_model('cotisations', 'Vente') - Article = apps.get_model('cotisations', 'Article') + Cotisation = apps.get_model("cotisations", "Cotisation") + Vente = apps.get_model("cotisations", "Vente") + Article = apps.get_model("cotisations", "Article") db_alias = schema_editor.connection.alias articles = Article.objects.using(db_alias).all() ventes = Vente.objects.using(db_alias).all() cotisations = Cotisation.objects.using(db_alias).all() for article in articles: if article.iscotisation: - article.type_cotisation='All' + article.type_cotisation = "All" article.save(using=db_alias) for vente in ventes: if vente.iscotisation: - vente.type_cotisation='All' + vente.type_cotisation = "All" vente.save(using=db_alias) for cotisation in cotisations: - cotisation.type_cotisation='All' + cotisation.type_cotisation = "All" cotisation.save(using=db_alias) + def delete_type(apps, schema_editor): - Vente = apps.get_model('cotisations', 'Vente') - Article = apps.get_model('cotisations', 'Article') + Vente = apps.get_model("cotisations", "Vente") + Article = apps.get_model("cotisations", "Article") db_alias = schema_editor.connection.alias - articles = Articles.objects.using(db_alias).all() + articles = Article.objects.using(db_alias).all() ventes = Vente.objects.using(db_alias).all() for article in articles: if article.type_cotisation: - article.iscotisation=True + article.iscotisation = True else: - article.iscotisation=False + article.iscotisation = False article.save(using=db_alias) for vente in ventes: if vente.iscotisation: - vente.iscotisation=True + vente.iscotisation = True else: - vente.iscotisation=False + vente.iscotisation = False vente.save(using=db_alias) + class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0025_article_type_user'), - ] + dependencies = [("cotisations", "0025_article_type_user")] operations = [ migrations.AddField( - model_name='article', - name='type_cotisation', - field=models.CharField(blank=True, choices=[('Connexion', 'Connexion'), ('Adhesion', 'Adhesion'), ('All', 'All')], default=None, max_length=255, null=True), + model_name="article", + name="type_cotisation", + field=models.CharField( + blank=True, + choices=[ + ("Connexion", "Connexion"), + ("Adhesion", "Adhesion"), + ("All", "All"), + ], + default=None, + max_length=255, + null=True, + ), ), migrations.AddField( - model_name='cotisation', - name='type_cotisation', - field=models.CharField(choices=[('Connexion', 'Connexion'), ('Adhesion', 'Adhesion'), ('All', 'All')], max_length=255, default='All'), + model_name="cotisation", + name="type_cotisation", + field=models.CharField( + choices=[ + ("Connexion", "Connexion"), + ("Adhesion", "Adhesion"), + ("All", "All"), + ], + max_length=255, + default="All", + ), ), migrations.AddField( - model_name='vente', - name='type_cotisation', - field=models.CharField(blank=True, choices=[('Connexion', 'Connexion'), ('Adhesion', 'Adhesion'), ('All', 'All')], max_length=255, null=True), + model_name="vente", + name="type_cotisation", + field=models.CharField( + blank=True, + choices=[ + ("Connexion", "Connexion"), + ("Adhesion", "Adhesion"), + ("All", "All"), + ], + max_length=255, + null=True, + ), ), migrations.RunPython(create_type, delete_type), - migrations.RemoveField( - model_name='article', - name='iscotisation', - ), - migrations.RemoveField( - model_name='vente', - name='iscotisation', - ), + migrations.RemoveField(model_name="article", name="iscotisation"), + migrations.RemoveField(model_name="vente", name="iscotisation"), ] diff --git a/cotisations/migrations/0027_auto_20171029_1156.py b/cotisations/migrations/0027_auto_20171029_1156.py index 8a9a4f0c..fa66698b 100644 --- a/cotisations/migrations/0027_auto_20171029_1156.py +++ b/cotisations/migrations/0027_auto_20171029_1156.py @@ -7,14 +7,10 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0026_auto_20171028_0126'), - ] + dependencies = [("cotisations", "0026_auto_20171028_0126")] operations = [ migrations.AlterField( - model_name='article', - name='name', - field=models.CharField(max_length=255), - ), + model_name="article", name="name", field=models.CharField(max_length=255) + ) ] diff --git a/cotisations/migrations/0028_auto_20171231_0007.py b/cotisations/migrations/0028_auto_20171231_0007.py index 0d492489..b968072d 100644 --- a/cotisations/migrations/0028_auto_20171231_0007.py +++ b/cotisations/migrations/0028_auto_20171231_0007.py @@ -7,33 +7,56 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0027_auto_20171029_1156'), - ] + dependencies = [("cotisations", "0027_auto_20171029_1156")] operations = [ migrations.AlterModelOptions( - name='article', - options={'permissions': (('view_article', 'Peut voir un objet article'),)}, + name="article", + options={"permissions": (("view_article", "Peut voir un objet article"),)}, ), migrations.AlterModelOptions( - name='banque', - options={'permissions': (('view_banque', 'Peut voir un objet banque'),)}, + 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'))}, + 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'))}, + 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'),)}, + 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'))}, + name="vente", + options={ + "permissions": ( + ("view_vente", "Peut voir un objet vente"), + ("change_all_vente", "Superdroit, peut modifier toutes les ventes"), + ) + }, ), ] diff --git a/cotisations/migrations/0029_auto_20180414_2056.py b/cotisations/migrations/0029_auto_20180414_2056.py index f0cb63fd..cd13ecad 100644 --- a/cotisations/migrations/0029_auto_20180414_2056.py +++ b/cotisations/migrations/0029_auto_20180414_2056.py @@ -9,143 +9,242 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0028_auto_20171231_0007'), - ] + dependencies = [("cotisations", "0028_auto_20171231_0007")] operations = [ migrations.AlterModelOptions( - name='article', - options={'permissions': (('view_article', "Can see an article's details"),), 'verbose_name': 'Article', 'verbose_name_plural': 'Articles'}, + name="article", + options={ + "permissions": (("view_article", "Can see an article's details"),), + "verbose_name": "Article", + "verbose_name_plural": "Articles", + }, ), migrations.AlterModelOptions( - name='banque', - options={'permissions': (('view_banque', "Can see a bank's details"),), 'verbose_name': 'Bank', 'verbose_name_plural': 'Banks'}, + name="banque", + options={ + "permissions": (("view_banque", "Can see a bank's details"),), + "verbose_name": "Bank", + "verbose_name_plural": "Banks", + }, ), migrations.AlterModelOptions( - name='cotisation', - options={'permissions': (('view_cotisation', "Can see a cotisation's details"), ('change_all_cotisation', 'Can edit the previous cotisations'))}, + name="cotisation", + options={ + "permissions": ( + ("view_cotisation", "Can see a cotisation's details"), + ("change_all_cotisation", "Can edit the previous cotisations"), + ) + }, ), migrations.AlterModelOptions( - name='facture', - options={'permissions': (('change_facture_control', 'Can change the "controlled" state'), ('change_facture_pdf', 'Can create a custom PDF invoice'), ('view_facture', "Can see an invoice's details"), ('change_all_facture', 'Can edit all the previous invoices')), 'verbose_name': 'Invoice', 'verbose_name_plural': 'Invoices'}, + name="facture", + options={ + "permissions": ( + ("change_facture_control", 'Can change the "controlled" state'), + ("change_facture_pdf", "Can create a custom PDF invoice"), + ("view_facture", "Can see an invoice's details"), + ("change_all_facture", "Can edit all the previous invoices"), + ), + "verbose_name": "Invoice", + "verbose_name_plural": "Invoices", + }, ), migrations.AlterModelOptions( - name='paiement', - options={'permissions': (('view_paiement', "Can see a payement's details"),), 'verbose_name': 'Payment method', 'verbose_name_plural': 'Payment methods'}, + name="paiement", + options={ + "permissions": (("view_paiement", "Can see a payement's details"),), + "verbose_name": "Payment method", + "verbose_name_plural": "Payment methods", + }, ), migrations.AlterModelOptions( - name='vente', - options={'permissions': (('view_vente', "Can see a purchase's details"), ('change_all_vente', 'Can edit all the previous purchases')), 'verbose_name': 'Purchase', 'verbose_name_plural': 'Purchases'}, + name="vente", + options={ + "permissions": ( + ("view_vente", "Can see a purchase's details"), + ("change_all_vente", "Can edit all the previous purchases"), + ), + "verbose_name": "Purchase", + "verbose_name_plural": "Purchases", + }, ), migrations.AlterField( - model_name='article', - name='duration', - field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Duration (in whole month)'), + model_name="article", + name="duration", + field=models.PositiveIntegerField( + blank=True, + null=True, + validators=[django.core.validators.MinValueValidator(0)], + verbose_name="Duration (in whole month)", + ), ), migrations.AlterField( - model_name='article', - name='name', - field=models.CharField(max_length=255, verbose_name='Designation'), + model_name="article", + name="name", + field=models.CharField(max_length=255, verbose_name="Designation"), ), migrations.AlterField( - model_name='article', - name='prix', - field=models.DecimalField(decimal_places=2, max_digits=5, verbose_name='Unitary price'), + model_name="article", + name="prix", + field=models.DecimalField( + decimal_places=2, max_digits=5, verbose_name="Unitary price" + ), ), migrations.AlterField( - model_name='article', - name='type_cotisation', - field=models.CharField(blank=True, choices=[('Connexion', 'Connexion'), ('Adhesion', 'Membership'), ('All', 'Both of them')], default=None, max_length=255, null=True, verbose_name='Type of cotisation'), + model_name="article", + name="type_cotisation", + field=models.CharField( + blank=True, + choices=[ + ("Connexion", "Connexion"), + ("Adhesion", "Membership"), + ("All", "Both of them"), + ], + default=None, + max_length=255, + null=True, + verbose_name="Type of cotisation", + ), ), migrations.AlterField( - model_name='article', - name='type_user', - field=models.CharField(choices=[('Adherent', 'Member'), ('Club', 'Club'), ('All', 'Both of them')], default='All', max_length=255, verbose_name='Type of users concerned'), + model_name="article", + name="type_user", + field=models.CharField( + choices=[ + ("Adherent", "Member"), + ("Club", "Club"), + ("All", "Both of them"), + ], + default="All", + max_length=255, + verbose_name="Type of users concerned", + ), ), migrations.AlterField( - model_name='banque', - name='name', - field=models.CharField(max_length=255, verbose_name='Name'), + model_name="banque", + name="name", + field=models.CharField(max_length=255, verbose_name="Name"), ), migrations.AlterField( - model_name='cotisation', - name='date_end', - field=models.DateTimeField(verbose_name='Ending date'), + model_name="cotisation", + name="date_end", + field=models.DateTimeField(verbose_name="Ending date"), ), migrations.AlterField( - model_name='cotisation', - name='date_start', - field=models.DateTimeField(verbose_name='Starting date'), + model_name="cotisation", + name="date_start", + field=models.DateTimeField(verbose_name="Starting date"), ), migrations.AlterField( - model_name='cotisation', - name='type_cotisation', - field=models.CharField(choices=[('Connexion', 'Connexion'), ('Adhesion', 'Membership'), ('All', 'Both of them')], default='All', max_length=255, verbose_name='Type of cotisation'), + model_name="cotisation", + name="type_cotisation", + field=models.CharField( + choices=[ + ("Connexion", "Connexion"), + ("Adhesion", "Membership"), + ("All", "Both of them"), + ], + default="All", + max_length=255, + verbose_name="Type of cotisation", + ), ), migrations.AlterField( - model_name='cotisation', - name='vente', - field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='cotisations.Vente', verbose_name='Purchase'), + model_name="cotisation", + name="vente", + field=models.OneToOneField( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="cotisations.Vente", + verbose_name="Purchase", + ), ), migrations.AlterField( - model_name='facture', - name='cheque', - field=models.CharField(blank=True, max_length=255, verbose_name='Cheque number'), + model_name="facture", + name="cheque", + field=models.CharField( + blank=True, max_length=255, verbose_name="Cheque number" + ), ), migrations.AlterField( - model_name='facture', - name='control', - field=models.BooleanField(default=False, verbose_name='Controlled'), + model_name="facture", + name="control", + field=models.BooleanField(default=False, verbose_name="Controlled"), ), migrations.AlterField( - model_name='facture', - name='date', - field=models.DateTimeField(auto_now_add=True, verbose_name='Date'), + model_name="facture", + name="date", + field=models.DateTimeField(auto_now_add=True, verbose_name="Date"), ), migrations.AlterField( - model_name='facture', - name='valid', - field=models.BooleanField(default=True, verbose_name='Validated'), + model_name="facture", + name="valid", + field=models.BooleanField(default=True, verbose_name="Validated"), ), migrations.AlterField( - model_name='paiement', - name='moyen', - field=models.CharField(max_length=255, verbose_name='Method'), + model_name="paiement", + name="moyen", + field=models.CharField(max_length=255, verbose_name="Method"), ), migrations.AlterField( - model_name='paiement', - name='type_paiement', - field=models.IntegerField(choices=[(0, 'Standard'), (1, 'Cheque')], default=0, verbose_name='Payment type'), + model_name="paiement", + name="type_paiement", + field=models.IntegerField( + choices=[(0, "Standard"), (1, "Cheque")], + default=0, + verbose_name="Payment type", + ), ), migrations.AlterField( - model_name='vente', - name='duration', - field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Duration (in whole month)'), + model_name="vente", + name="duration", + field=models.PositiveIntegerField( + blank=True, null=True, verbose_name="Duration (in whole month)" + ), ), migrations.AlterField( - model_name='vente', - name='facture', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cotisations.Facture', verbose_name='Invoice'), + model_name="vente", + name="facture", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="cotisations.Facture", + verbose_name="Invoice", + ), ), migrations.AlterField( - model_name='vente', - name='name', - field=models.CharField(max_length=255, verbose_name='Article'), + model_name="vente", + name="name", + field=models.CharField(max_length=255, verbose_name="Article"), ), migrations.AlterField( - model_name='vente', - name='number', - field=models.IntegerField(validators=[django.core.validators.MinValueValidator(1)], verbose_name='Amount'), + model_name="vente", + name="number", + field=models.IntegerField( + validators=[django.core.validators.MinValueValidator(1)], + verbose_name="Amount", + ), ), migrations.AlterField( - model_name='vente', - name='prix', - field=models.DecimalField(decimal_places=2, max_digits=5, verbose_name='Price'), + model_name="vente", + name="prix", + field=models.DecimalField( + decimal_places=2, max_digits=5, verbose_name="Price" + ), ), migrations.AlterField( - model_name='vente', - name='type_cotisation', - field=models.CharField(blank=True, choices=[('Connexion', 'Connexion'), ('Adhesion', 'Membership'), ('All', 'Both of them')], max_length=255, null=True, verbose_name='Type of cotisation'), + model_name="vente", + name="type_cotisation", + field=models.CharField( + blank=True, + choices=[ + ("Connexion", "Connexion"), + ("Adhesion", "Membership"), + ("All", "Both of them"), + ], + max_length=255, + null=True, + verbose_name="Type of cotisation", + ), ), ] diff --git a/cotisations/migrations/0030_custom_payment.py b/cotisations/migrations/0030_custom_payment.py index ec6f08d1..5a528434 100644 --- a/cotisations/migrations/0030_custom_payment.py +++ b/cotisations/migrations/0030_custom_payment.py @@ -9,8 +9,8 @@ import django.db.models.deletion def add_cheque(apps, schema_editor): - ChequePayment = apps.get_model('cotisations', 'ChequePayment') - Payment = apps.get_model('cotisations', 'Paiement') + ChequePayment = apps.get_model("cotisations", "ChequePayment") + Payment = apps.get_model("cotisations", "Paiement") for p in Payment.objects.filter(type_paiement=1): cheque = ChequePayment() cheque.payment = p @@ -18,14 +18,12 @@ def add_cheque(apps, schema_editor): def add_comnpay(apps, schema_editor): - ComnpayPayment = apps.get_model('cotisations', 'ComnpayPayment') - Payment = apps.get_model('cotisations', 'Paiement') - AssoOption = apps.get_model('preferences', 'AssoOption') + ComnpayPayment = apps.get_model("cotisations", "ComnpayPayment") + Payment = apps.get_model("cotisations", "Paiement") + AssoOption = apps.get_model("preferences", "AssoOption") options, _created = AssoOption.objects.get_or_create() try: - payment = Payment.objects.get( - moyen='Rechargement en ligne' - ) + payment = Payment.objects.get(moyen="Rechargement en ligne") except Payment.DoesNotExist: return comnpay = ComnpayPayment() @@ -38,11 +36,11 @@ def add_comnpay(apps, schema_editor): def add_solde(apps, schema_editor): - OptionalUser = apps.get_model('preferences', 'OptionalUser') + OptionalUser = apps.get_model("preferences", "OptionalUser") options, _created = OptionalUser.objects.get_or_create() - Payment = apps.get_model('cotisations', 'Paiement') - BalancePayment = apps.get_model('cotisations', 'BalancePayment') + Payment = apps.get_model("cotisations", "Paiement") + BalancePayment = apps.get_model("cotisations", "BalancePayment") try: solde = Payment.objects.get(moyen="solde") @@ -60,73 +58,191 @@ def add_solde(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ('preferences', '0044_remove_payment_pass'), - ('cotisations', '0029_auto_20180414_2056'), + ("preferences", "0044_remove_payment_pass"), + ("cotisations", "0029_auto_20180414_2056"), ] operations = [ migrations.AlterModelOptions( - name='paiement', - options={'permissions': (('view_paiement', "Can see a payement's details"), ('use_every_payment', 'Can use every payement')), 'verbose_name': 'Payment method', 'verbose_name_plural': 'Payment methods'}, + name="paiement", + options={ + "permissions": ( + ("view_paiement", "Can see a payement's details"), + ("use_every_payment", "Can use every payement"), + ), + "verbose_name": "Payment method", + "verbose_name_plural": "Payment methods", + }, ), migrations.AlterModelOptions( - name='article', - options={'permissions': (('view_article', "Can see an article's details"), ('buy_every_article', 'Can buy every_article')), 'verbose_name': 'Article', 'verbose_name_plural': 'Articles'}, + name="article", + options={ + "permissions": ( + ("view_article", "Can see an article's details"), + ("buy_every_article", "Can buy every_article"), + ), + "verbose_name": "Article", + "verbose_name_plural": "Articles", + }, ), migrations.AddField( - model_name='paiement', - name='available_for_everyone', - field=models.BooleanField(default=False, verbose_name='Is available for every user'), + model_name="paiement", + name="available_for_everyone", + field=models.BooleanField( + default=False, verbose_name="Is available for every user" + ), ), migrations.AddField( - model_name='paiement', - name='is_balance', - field=models.BooleanField(default=False, editable=False, help_text='There should be only one balance payment method.', verbose_name='Is user balance', validators=[cotisations.models.check_no_balance]), + model_name="paiement", + name="is_balance", + field=models.BooleanField( + default=False, + editable=False, + help_text="There should be only one balance payment method.", + verbose_name="Is user balance", + validators=[cotisations.models.check_no_balance], + ), ), migrations.AddField( - model_name='article', - name='available_for_everyone', - field=models.BooleanField(default=False, verbose_name='Is available for every user'), + model_name="article", + name="available_for_everyone", + field=models.BooleanField( + default=False, verbose_name="Is available for every user" + ), ), migrations.CreateModel( - name='ChequePayment', + name="ChequePayment", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('payment', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method', to='cotisations.Paiement')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "payment", + models.OneToOneField( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + related_name="payment_method", + to="cotisations.Paiement", + ), + ), ], bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), - options={'verbose_name': 'Cheque'}, + options={"verbose_name": "Cheque"}, ), migrations.CreateModel( - name='ComnpayPayment', + name="ComnpayPayment", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('payment_credential', models.CharField(blank=True, default='', max_length=255, verbose_name='ComNpay VAD Number')), - ('payment_pass', re2o.aes_field.AESEncryptedField(blank=True, max_length=255, null=True, verbose_name='ComNpay Secret Key')), - ('payment', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method', to='cotisations.Paiement')), - ('minimum_payment', models.DecimalField(decimal_places=2, default=1, help_text='The minimal amount of money you have to use when paying with ComNpay', max_digits=5, verbose_name='Minimum payment')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "payment_credential", + models.CharField( + blank=True, + default="", + max_length=255, + verbose_name="ComNpay VAD Number", + ), + ), + ( + "payment_pass", + re2o.aes_field.AESEncryptedField( + blank=True, + max_length=255, + null=True, + verbose_name="ComNpay Secret Key", + ), + ), + ( + "payment", + models.OneToOneField( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + related_name="payment_method", + to="cotisations.Paiement", + ), + ), + ( + "minimum_payment", + models.DecimalField( + decimal_places=2, + default=1, + help_text="The minimal amount of money you have to use when paying with ComNpay", + max_digits=5, + verbose_name="Minimum payment", + ), + ), ], bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), - options={'verbose_name': 'ComNpay'}, + options={"verbose_name": "ComNpay"}, ), migrations.CreateModel( - name='BalancePayment', + name="BalancePayment", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('minimum_balance', models.DecimalField(decimal_places=2, default=0, help_text='The minimal amount of money allowed for the balance at the end of a payment. You can specify negative amount.', max_digits=5, verbose_name='Minimum balance')), - ('payment', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method', to='cotisations.Paiement')), - ('maximum_balance', models.DecimalField(decimal_places=2, default=50, help_text='The maximal amount of money allowed for the balance.', max_digits=5, verbose_name='Maximum balance', null=True, blank=True)), - ('credit_balance_allowed', models.BooleanField(default=False, verbose_name='Allow user to credit their balance')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "minimum_balance", + models.DecimalField( + decimal_places=2, + default=0, + help_text="The minimal amount of money allowed for the balance at the end of a payment. You can specify negative amount.", + max_digits=5, + verbose_name="Minimum balance", + ), + ), + ( + "payment", + models.OneToOneField( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + related_name="payment_method", + to="cotisations.Paiement", + ), + ), + ( + "maximum_balance", + models.DecimalField( + decimal_places=2, + default=50, + help_text="The maximal amount of money allowed for the balance.", + max_digits=5, + verbose_name="Maximum balance", + null=True, + blank=True, + ), + ), + ( + "credit_balance_allowed", + models.BooleanField( + default=False, verbose_name="Allow user to credit their balance" + ), + ), ], bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), - options={'verbose_name': 'User Balance'}, + options={"verbose_name": "User Balance"}, ), migrations.RunPython(add_comnpay), migrations.RunPython(add_cheque), migrations.RunPython(add_solde), - migrations.RemoveField( - model_name='paiement', - name='type_paiement', - ), - + migrations.RemoveField(model_name="paiement", name="type_paiement"), ] diff --git a/cotisations/migrations/0031_comnpaypayment_production.py b/cotisations/migrations/0031_comnpaypayment_production.py index 25ec7fb8..3f0f48ce 100644 --- a/cotisations/migrations/0031_comnpaypayment_production.py +++ b/cotisations/migrations/0031_comnpaypayment_production.py @@ -7,14 +7,15 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0030_custom_payment'), - ] + dependencies = [("cotisations", "0030_custom_payment")] operations = [ migrations.AddField( - model_name='comnpaypayment', - name='production', - field=models.BooleanField(default=True, verbose_name='Production mode enabled (production url, instead of homologation)'), - ), + model_name="comnpaypayment", + name="production", + field=models.BooleanField( + default=True, + verbose_name="Production mode enabled (production url, instead of homologation)", + ), + ) ] diff --git a/cotisations/migrations/0032_custom_invoice.py b/cotisations/migrations/0032_custom_invoice.py index 39c23e8b..38b28d23 100644 --- a/cotisations/migrations/0032_custom_invoice.py +++ b/cotisations/migrations/0032_custom_invoice.py @@ -10,8 +10,8 @@ import re2o.mixins def reattribute_ids(apps, schema_editor): - Facture = apps.get_model('cotisations', 'Facture') - BaseInvoice = apps.get_model('cotisations', 'BaseInvoice') + Facture = apps.get_model("cotisations", "Facture") + BaseInvoice = apps.get_model("cotisations", "BaseInvoice") for f in Facture.objects.all(): base = BaseInvoice.objects.create(id=f.pk) @@ -22,21 +22,23 @@ def reattribute_ids(apps, schema_editor): def update_rights(apps, schema_editor): - Permission = apps.get_model('auth', 'Permission') + Permission = apps.get_model("auth", "Permission") # creates needed permissions - app = apps.get_app_config('cotisations') + app = apps.get_app_config("cotisations") app.models_module = True create_permissions(app) app.models_module = False ContentType = apps.get_model("contenttypes", "ContentType") content_type = ContentType.objects.get_for_model(Permission) - former, created = Permission.objects.get_or_create(codename='change_facture_pdf', content_type=content_type) - new_1 = Permission.objects.get(codename='add_custominvoice') - new_2 = Permission.objects.get(codename='change_custominvoice') - new_3 = Permission.objects.get(codename='view_custominvoice') - new_4 = Permission.objects.get(codename='delete_custominvoice') + former, created = Permission.objects.get_or_create( + codename="change_facture_pdf", content_type=content_type + ) + new_1 = Permission.objects.get(codename="add_custominvoice") + new_2 = Permission.objects.get(codename="change_custominvoice") + new_3 = Permission.objects.get(codename="view_custominvoice") + new_4 = Permission.objects.get(codename="delete_custominvoice") for group in former.group_set.all(): group.permissions.remove(former) group.permissions.add(new_1) @@ -48,59 +50,105 @@ def update_rights(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0031_comnpaypayment_production'), - ] + dependencies = [("cotisations", "0031_comnpaypayment_production")] operations = [ migrations.CreateModel( - name='BaseInvoice', + name="BaseInvoice", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('date', models.DateTimeField(auto_now_add=True, verbose_name='Date')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("date", models.DateTimeField(auto_now_add=True, verbose_name="Date")), ], - bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, re2o.field_permissions.FieldPermissionModelMixin, models.Model), + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + re2o.field_permissions.FieldPermissionModelMixin, + models.Model, + ), ), migrations.CreateModel( - name='CustomInvoice', + name="CustomInvoice", fields=[ - ('baseinvoice_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='cotisations.BaseInvoice')), - ('recipient', models.CharField(max_length=255, verbose_name='Recipient')), - ('payment', models.CharField(max_length=255, verbose_name='Payment type')), - ('address', models.CharField(max_length=255, verbose_name='Address')), - ('paid', models.BooleanField(verbose_name='Paid')), + ( + "baseinvoice_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="cotisations.BaseInvoice", + ), + ), + ( + "recipient", + models.CharField(max_length=255, verbose_name="Recipient"), + ), + ( + "payment", + models.CharField(max_length=255, verbose_name="Payment type"), + ), + ("address", models.CharField(max_length=255, verbose_name="Address")), + ("paid", models.BooleanField(verbose_name="Paid")), ], - bases=('cotisations.baseinvoice',), - options={'permissions': (('view_custominvoice', 'Can view a custom invoice'),)}, + bases=("cotisations.baseinvoice",), + options={ + "permissions": (("view_custominvoice", "Can view a custom invoice"),) + }, ), migrations.AddField( - model_name='facture', - name='baseinvoice_ptr', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='cotisations.BaseInvoice', null=True), + model_name="facture", + name="baseinvoice_ptr", + field=models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + to="cotisations.BaseInvoice", + null=True, + ), preserve_default=False, ), migrations.RunPython(reattribute_ids), migrations.AlterField( - model_name='vente', - name='facture', - field=models.ForeignKey(on_delete=models.CASCADE, verbose_name='Invoice', to='cotisations.BaseInvoice') - ), - migrations.RemoveField( - model_name='facture', - name='id', - ), - migrations.RemoveField( - model_name='facture', - name='date', + model_name="vente", + name="facture", + field=models.ForeignKey( + on_delete=models.CASCADE, + verbose_name="Invoice", + to="cotisations.BaseInvoice", + ), ), + migrations.RemoveField(model_name="facture", name="id"), + migrations.RemoveField(model_name="facture", name="date"), migrations.AlterField( - model_name='facture', - name='baseinvoice_ptr', - field=models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='cotisations.BaseInvoice'), + model_name="facture", + name="baseinvoice_ptr", + field=models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="cotisations.BaseInvoice", + ), ), migrations.RunPython(update_rights), migrations.AlterModelOptions( - name='facture', - options={'permissions': (('change_facture_control', 'Can change the "controlled" state'), ('view_facture', "Can see an invoice's details"), ('change_all_facture', 'Can edit all the previous invoices')), 'verbose_name': 'Invoice', 'verbose_name_plural': 'Invoices'}, + name="facture", + options={ + "permissions": ( + ("change_facture_control", 'Can change the "controlled" state'), + ("view_facture", "Can see an invoice's details"), + ("change_all_facture", "Can edit all the previous invoices"), + ), + "verbose_name": "Invoice", + "verbose_name_plural": "Invoices", + }, ), ] diff --git a/cotisations/migrations/0033_auto_20180818_1319.py b/cotisations/migrations/0033_auto_20180818_1319.py index 2e61fbb6..edf8a77f 100644 --- a/cotisations/migrations/0033_auto_20180818_1319.py +++ b/cotisations/migrations/0033_auto_20180818_1319.py @@ -11,171 +11,294 @@ import re2o.aes_field class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0032_custom_invoice'), - ] + dependencies = [("cotisations", "0032_custom_invoice")] operations = [ migrations.AlterModelOptions( - name='article', - options={'permissions': (('view_article', 'Can view an article object'), ('buy_every_article', 'Can buy every article')), 'verbose_name': 'article', 'verbose_name_plural': 'articles'}, + name="article", + options={ + "permissions": ( + ("view_article", "Can view an article object"), + ("buy_every_article", "Can buy every article"), + ), + "verbose_name": "article", + "verbose_name_plural": "articles", + }, ), migrations.AlterModelOptions( - name='balancepayment', - options={'verbose_name': 'user balance'}, + name="balancepayment", options={"verbose_name": "user balance"} ), migrations.AlterModelOptions( - name='banque', - options={'permissions': (('view_banque', 'Can view a bank object'),), 'verbose_name': 'bank', 'verbose_name_plural': 'banks'}, + name="banque", + options={ + "permissions": (("view_banque", "Can view a bank object"),), + "verbose_name": "bank", + "verbose_name_plural": "banks", + }, ), migrations.AlterModelOptions( - name='cotisation', - options={'permissions': (('view_cotisation', 'Can view a subscription object'), ('change_all_cotisation', 'Can edit the previous subscriptions')), 'verbose_name': 'subscription', 'verbose_name_plural': 'subscriptions'}, + name="cotisation", + options={ + "permissions": ( + ("view_cotisation", "Can view a subscription object"), + ("change_all_cotisation", "Can edit the previous subscriptions"), + ), + "verbose_name": "subscription", + "verbose_name_plural": "subscriptions", + }, ), migrations.AlterModelOptions( - name='custominvoice', - options={'permissions': (('view_custominvoice', 'Can view a custom invoice object'),)}, + name="custominvoice", + options={ + "permissions": ( + ("view_custominvoice", "Can view a custom invoice object"), + ) + }, ), migrations.AlterModelOptions( - name='facture', - options={'permissions': (('change_facture_control', 'Can edit the "controlled" state'), ('view_facture', 'Can view an invoice object'), ('change_all_facture', 'Can edit all the previous invoices')), 'verbose_name': 'invoice', 'verbose_name_plural': 'invoices'}, + name="facture", + options={ + "permissions": ( + ("change_facture_control", 'Can edit the "controlled" state'), + ("view_facture", "Can view an invoice object"), + ("change_all_facture", "Can edit all the previous invoices"), + ), + "verbose_name": "invoice", + "verbose_name_plural": "invoices", + }, ), migrations.AlterModelOptions( - name='paiement', - options={'permissions': (('view_paiement', 'Can view a payment method object'), ('use_every_payment', 'Can use every payment method')), 'verbose_name': 'payment method', 'verbose_name_plural': 'payment methods'}, + name="paiement", + options={ + "permissions": ( + ("view_paiement", "Can view a payment method object"), + ("use_every_payment", "Can use every payment method"), + ), + "verbose_name": "payment method", + "verbose_name_plural": "payment methods", + }, ), migrations.AlterModelOptions( - name='vente', - options={'permissions': (('view_vente', 'Can view a purchase object'), ('change_all_vente', 'Can edit all the previous purchases')), 'verbose_name': 'purchase', 'verbose_name_plural': 'purchases'}, + name="vente", + options={ + "permissions": ( + ("view_vente", "Can view a purchase object"), + ("change_all_vente", "Can edit all the previous purchases"), + ), + "verbose_name": "purchase", + "verbose_name_plural": "purchases", + }, ), migrations.AlterField( - model_name='article', - name='available_for_everyone', - field=models.BooleanField(default=False, verbose_name='is available for every user'), + model_name="article", + name="available_for_everyone", + field=models.BooleanField( + default=False, verbose_name="is available for every user" + ), ), migrations.AlterField( - model_name='article', - name='duration', - field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration (in months)'), + model_name="article", + name="duration", + field=models.PositiveIntegerField( + blank=True, + null=True, + validators=[django.core.validators.MinValueValidator(0)], + verbose_name="duration (in months)", + ), ), migrations.AlterField( - model_name='article', - name='name', - field=models.CharField(max_length=255, verbose_name='designation'), + model_name="article", + name="name", + field=models.CharField(max_length=255, verbose_name="designation"), ), migrations.AlterField( - model_name='article', - name='prix', - field=models.DecimalField(decimal_places=2, max_digits=5, verbose_name='unit price'), + model_name="article", + name="prix", + field=models.DecimalField( + decimal_places=2, max_digits=5, verbose_name="unit price" + ), ), migrations.AlterField( - model_name='article', - name='type_cotisation', - field=models.CharField(blank=True, choices=[('Connexion', 'Connection'), ('Adhesion', 'Membership'), ('All', 'Both of them')], default=None, max_length=255, null=True, verbose_name='subscription type'), + model_name="article", + name="type_cotisation", + field=models.CharField( + blank=True, + choices=[ + ("Connexion", "Connection"), + ("Adhesion", "Membership"), + ("All", "Both of them"), + ], + default=None, + max_length=255, + null=True, + verbose_name="subscription type", + ), ), migrations.AlterField( - model_name='article', - name='type_user', - field=models.CharField(choices=[('Adherent', 'Member'), ('Club', 'Club'), ('All', 'Both of them')], default='All', max_length=255, verbose_name='type of users concerned'), + model_name="article", + name="type_user", + field=models.CharField( + choices=[ + ("Adherent", "Member"), + ("Club", "Club"), + ("All", "Both of them"), + ], + default="All", + max_length=255, + verbose_name="type of users concerned", + ), ), migrations.AlterField( - model_name='banque', - name='name', - field=models.CharField(max_length=255), + model_name="banque", name="name", field=models.CharField(max_length=255) ), migrations.AlterField( - model_name='comnpaypayment', - name='payment_credential', - field=models.CharField(blank=True, default='', max_length=255, verbose_name='ComNpay VAT Number'), + model_name="comnpaypayment", + name="payment_credential", + field=models.CharField( + blank=True, + default="", + max_length=255, + verbose_name="ComNpay VAT Number", + ), ), migrations.AlterField( - model_name='comnpaypayment', - name='payment_pass', - field=re2o.aes_field.AESEncryptedField(blank=True, max_length=255, null=True, verbose_name='ComNpay secret key'), + model_name="comnpaypayment", + name="payment_pass", + field=re2o.aes_field.AESEncryptedField( + blank=True, max_length=255, null=True, verbose_name="ComNpay secret key" + ), ), migrations.AlterField( - model_name='comnpaypayment', - name='production', - field=models.BooleanField(default=True, verbose_name='Production mode enabled (production URL, instead of homologation)'), + model_name="comnpaypayment", + name="production", + field=models.BooleanField( + default=True, + verbose_name="Production mode enabled (production URL, instead of homologation)", + ), ), migrations.AlterField( - model_name='cotisation', - name='date_end', - field=models.DateTimeField(verbose_name='end date'), + model_name="cotisation", + name="date_end", + field=models.DateTimeField(verbose_name="end date"), ), migrations.AlterField( - model_name='cotisation', - name='date_start', - field=models.DateTimeField(verbose_name='start date'), + model_name="cotisation", + name="date_start", + field=models.DateTimeField(verbose_name="start date"), ), migrations.AlterField( - model_name='cotisation', - name='type_cotisation', - field=models.CharField(choices=[('Connexion', 'Connection'), ('Adhesion', 'Membership'), ('All', 'Both of them')], default='All', max_length=255, verbose_name='subscription type'), + model_name="cotisation", + name="type_cotisation", + field=models.CharField( + choices=[ + ("Connexion", "Connection"), + ("Adhesion", "Membership"), + ("All", "Both of them"), + ], + default="All", + max_length=255, + verbose_name="subscription type", + ), ), migrations.AlterField( - model_name='cotisation', - name='vente', - field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='cotisations.Vente', verbose_name='purchase'), + model_name="cotisation", + name="vente", + field=models.OneToOneField( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="cotisations.Vente", + verbose_name="purchase", + ), ), migrations.AlterField( - model_name='facture', - name='cheque', - field=models.CharField(blank=True, max_length=255, verbose_name='cheque number'), + model_name="facture", + name="cheque", + field=models.CharField( + blank=True, max_length=255, verbose_name="cheque number" + ), ), migrations.AlterField( - model_name='facture', - name='control', - field=models.BooleanField(default=False, verbose_name='controlled'), + model_name="facture", + name="control", + field=models.BooleanField(default=False, verbose_name="controlled"), ), migrations.AlterField( - model_name='facture', - name='valid', - field=models.BooleanField(default=True, verbose_name='validated'), + model_name="facture", + name="valid", + field=models.BooleanField(default=True, verbose_name="validated"), ), migrations.AlterField( - model_name='paiement', - name='available_for_everyone', - field=models.BooleanField(default=False, verbose_name='is available for every user'), + model_name="paiement", + name="available_for_everyone", + field=models.BooleanField( + default=False, verbose_name="is available for every user" + ), ), migrations.AlterField( - model_name='paiement', - name='is_balance', - field=models.BooleanField(default=False, editable=False, help_text='There should be only one balance payment method.', validators=[cotisations.validators.check_no_balance], verbose_name='is user balance'), + model_name="paiement", + name="is_balance", + field=models.BooleanField( + default=False, + editable=False, + help_text="There should be only one balance payment method.", + validators=[cotisations.validators.check_no_balance], + verbose_name="is user balance", + ), ), migrations.AlterField( - model_name='paiement', - name='moyen', - field=models.CharField(max_length=255, verbose_name='method'), + model_name="paiement", + name="moyen", + field=models.CharField(max_length=255, verbose_name="method"), ), migrations.AlterField( - model_name='vente', - name='duration', - field=models.PositiveIntegerField(blank=True, null=True, verbose_name='duration (in months)'), + model_name="vente", + name="duration", + field=models.PositiveIntegerField( + blank=True, null=True, verbose_name="duration (in months)" + ), ), migrations.AlterField( - model_name='vente', - name='facture', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cotisations.BaseInvoice', verbose_name='invoice'), + model_name="vente", + name="facture", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="cotisations.BaseInvoice", + verbose_name="invoice", + ), ), migrations.AlterField( - model_name='vente', - name='name', - field=models.CharField(max_length=255, verbose_name='article'), + model_name="vente", + name="name", + field=models.CharField(max_length=255, verbose_name="article"), ), migrations.AlterField( - model_name='vente', - name='number', - field=models.IntegerField(validators=[django.core.validators.MinValueValidator(1)], verbose_name='amount'), + model_name="vente", + name="number", + field=models.IntegerField( + validators=[django.core.validators.MinValueValidator(1)], + verbose_name="amount", + ), ), migrations.AlterField( - model_name='vente', - name='prix', - field=models.DecimalField(decimal_places=2, max_digits=5, verbose_name='price'), + model_name="vente", + name="prix", + field=models.DecimalField( + decimal_places=2, max_digits=5, verbose_name="price" + ), ), migrations.AlterField( - model_name='vente', - name='type_cotisation', - field=models.CharField(blank=True, choices=[('Connexion', 'Connection'), ('Adhesion', 'Membership'), ('All', 'Both of them')], max_length=255, null=True, verbose_name='subscription type'), + model_name="vente", + name="type_cotisation", + field=models.CharField( + blank=True, + choices=[ + ("Connexion", "Connection"), + ("Adhesion", "Membership"), + ("All", "Both of them"), + ], + max_length=255, + null=True, + verbose_name="subscription type", + ), ), ] diff --git a/cotisations/migrations/0034_auto_20180831_1532.py b/cotisations/migrations/0034_auto_20180831_1532.py index ea698374..aca2a3b6 100644 --- a/cotisations/migrations/0034_auto_20180831_1532.py +++ b/cotisations/migrations/0034_auto_20180831_1532.py @@ -7,14 +7,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0033_auto_20180818_1319'), - ] + dependencies = [("cotisations", "0033_auto_20180818_1319")] operations = [ migrations.AlterField( - model_name='facture', - name='valid', - field=models.BooleanField(default=False, verbose_name='validated'), - ), + model_name="facture", + name="valid", + field=models.BooleanField(default=False, verbose_name="validated"), + ) ] diff --git a/cotisations/migrations/0035_notepayment.py b/cotisations/migrations/0035_notepayment.py index 1d8bcd48..5de8c93a 100644 --- a/cotisations/migrations/0035_notepayment.py +++ b/cotisations/migrations/0035_notepayment.py @@ -9,23 +9,35 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0034_auto_20180831_1532'), - ] + dependencies = [("cotisations", "0034_auto_20180831_1532")] operations = [ migrations.CreateModel( - name='NotePayment', + name="NotePayment", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('server', models.CharField(max_length=255, verbose_name='server')), - ('port', models.PositiveIntegerField(blank=True, null=True)), - ('id_note', models.PositiveIntegerField(blank=True, null=True)), - ('payment', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method', to='cotisations.Paiement')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("server", models.CharField(max_length=255, verbose_name="server")), + ("port", models.PositiveIntegerField(blank=True, null=True)), + ("id_note", models.PositiveIntegerField(blank=True, null=True)), + ( + "payment", + models.OneToOneField( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + related_name="payment_method", + to="cotisations.Paiement", + ), + ), ], - options={ - 'verbose_name': 'NoteKfet', - }, + options={"verbose_name": "NoteKfet"}, bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), - ), + ) ] diff --git a/cotisations/migrations/0036_custominvoice_remark.py b/cotisations/migrations/0036_custominvoice_remark.py index 7719b31d..90b40308 100644 --- a/cotisations/migrations/0036_custominvoice_remark.py +++ b/cotisations/migrations/0036_custominvoice_remark.py @@ -7,14 +7,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0035_notepayment'), - ] + dependencies = [("cotisations", "0035_notepayment")] operations = [ migrations.AddField( - model_name='custominvoice', - name='remark', - field=models.TextField(blank=True, null=True, verbose_name='Remark'), - ), + model_name="custominvoice", + name="remark", + field=models.TextField(blank=True, null=True, verbose_name="Remark"), + ) ] diff --git a/cotisations/migrations/0037_costestimate.py b/cotisations/migrations/0037_costestimate.py index 3d97f3f3..4cf72564 100644 --- a/cotisations/migrations/0037_costestimate.py +++ b/cotisations/migrations/0037_costestimate.py @@ -8,21 +8,40 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0036_custominvoice_remark'), - ] + dependencies = [("cotisations", "0036_custominvoice_remark")] operations = [ migrations.CreateModel( - name='CostEstimate', + name="CostEstimate", fields=[ - ('custominvoice_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='cotisations.CustomInvoice')), - ('validity', models.DurationField(verbose_name='Period of validity')), - ('final_invoice', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='origin_cost_estimate', to='cotisations.CustomInvoice')), + ( + "custominvoice_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="cotisations.CustomInvoice", + ), + ), + ("validity", models.DurationField(verbose_name="Period of validity")), + ( + "final_invoice", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="origin_cost_estimate", + to="cotisations.CustomInvoice", + ), + ), ], options={ - 'permissions': (('view_costestimate', 'Can view a cost estimate object'),), + "permissions": ( + ("view_costestimate", "Can view a cost estimate object"), + ) }, - bases=('cotisations.custominvoice',), - ), + bases=("cotisations.custominvoice",), + ) ] diff --git a/cotisations/migrations/0038_auto_20181231_1657.py b/cotisations/migrations/0038_auto_20181231_1657.py index a9415bf0..9d6c67cd 100644 --- a/cotisations/migrations/0038_auto_20181231_1657.py +++ b/cotisations/migrations/0038_auto_20181231_1657.py @@ -8,24 +8,30 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('cotisations', '0037_costestimate'), - ] + dependencies = [("cotisations", "0037_costestimate")] operations = [ migrations.AlterField( - model_name='costestimate', - name='final_invoice', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='origin_cost_estimate', to='cotisations.CustomInvoice'), + model_name="costestimate", + name="final_invoice", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="origin_cost_estimate", + to="cotisations.CustomInvoice", + ), ), migrations.AlterField( - model_name='costestimate', - name='validity', - field=models.DurationField(help_text='DD HH:MM:SS', verbose_name='Period of validity'), + model_name="costestimate", + name="validity", + field=models.DurationField( + help_text="DD HH:MM:SS", verbose_name="Period of validity" + ), ), migrations.AlterField( - model_name='custominvoice', - name='paid', - field=models.BooleanField(default=False, verbose_name='Paid'), + model_name="custominvoice", + name="paid", + field=models.BooleanField(default=False, verbose_name="Paid"), ), ] diff --git a/cotisations/migrations/0039_freepayment.py b/cotisations/migrations/0039_freepayment.py new file mode 100644 index 00000000..9dc0a66f --- /dev/null +++ b/cotisations/migrations/0039_freepayment.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.23 on 2019-09-30 09:19 +from __future__ import unicode_literals + +import cotisations.payment_methods.mixins +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [("cotisations", "0038_auto_20181231_1657")] + + operations = [ + migrations.CreateModel( + name="FreePayment", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "payment", + models.OneToOneField( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + related_name="payment_method", + to="cotisations.Paiement", + ), + ), + ], + options={"verbose_name": "Free payment"}, + bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), + ) + ] diff --git a/cotisations/migrations/0040_auto_20191002_2335.py b/cotisations/migrations/0040_auto_20191002_2335.py new file mode 100644 index 00000000..78e5ef2c --- /dev/null +++ b/cotisations/migrations/0040_auto_20191002_2335.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.23 on 2019-10-02 21:35 +from __future__ import unicode_literals + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [("cotisations", "0039_freepayment")] + + operations = [ + migrations.AddField( + model_name="article", + name="duration_days", + field=models.PositiveIntegerField( + blank=True, + null=True, + validators=[django.core.validators.MinValueValidator(0)], + verbose_name="duration (in days, will be added to duration in months)", + ), + ), + migrations.AddField( + model_name="vente", + name="duration_days", + field=models.PositiveIntegerField( + blank=True, + null=True, + validators=[django.core.validators.MinValueValidator(0)], + verbose_name="duration (in days, will be added to duration in months)", + ), + ), + ] diff --git a/cotisations/migrations/0041_auto_20191103_2131.py b/cotisations/migrations/0041_auto_20191103_2131.py new file mode 100644 index 00000000..5ce1e767 --- /dev/null +++ b/cotisations/migrations/0041_auto_20191103_2131.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.23 on 2019-11-03 20:31 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [("cotisations", "0040_auto_20191002_2335")] + + operations = [ + migrations.AlterField( + model_name="balancepayment", + name="payment", + field=models.OneToOneField( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + related_name="payment_method_balance", + to="cotisations.Paiement", + ), + ), + migrations.AlterField( + model_name="chequepayment", + name="payment", + field=models.OneToOneField( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + related_name="payment_method_cheque", + to="cotisations.Paiement", + ), + ), + migrations.AlterField( + model_name="comnpaypayment", + name="payment", + field=models.OneToOneField( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + related_name="payment_method_comnpay", + to="cotisations.Paiement", + ), + ), + migrations.AlterField( + model_name="freepayment", + name="payment", + field=models.OneToOneField( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + related_name="payment_method_free", + to="cotisations.Paiement", + ), + ), + migrations.AlterField( + model_name="notepayment", + name="payment", + field=models.OneToOneField( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + related_name="payment_method_note", + to="cotisations.Paiement", + ), + ), + ] diff --git a/cotisations/migrations/0042_auto_20191120_0159.py b/cotisations/migrations/0042_auto_20191120_0159.py new file mode 100644 index 00000000..b9e5b356 --- /dev/null +++ b/cotisations/migrations/0042_auto_20191120_0159.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.23 on 2019-11-20 00:59 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cotisations', '0041_auto_20191103_2131'), + ] + + operations = [ + migrations.AlterModelOptions( + name='chequepayment', + options={'verbose_name': 'cheque'}, + ), + migrations.AlterField( + model_name='balancepayment', + name='credit_balance_allowed', + field=models.BooleanField(default=False, verbose_name='allow user to credit their balance'), + ), + migrations.AlterField( + model_name='balancepayment', + name='maximum_balance', + field=models.DecimalField(blank=True, decimal_places=2, default=50, help_text='The maximal amount of money allowed for the balance.', max_digits=5, null=True, verbose_name='maximum balance'), + ), + migrations.AlterField( + model_name='balancepayment', + name='minimum_balance', + field=models.DecimalField(decimal_places=2, default=0, help_text='The minimal amount of money allowed for the balance at the end of a payment. You can specify a negative amount.', max_digits=5, verbose_name='minimum balance'), + ), + migrations.AlterField( + model_name='baseinvoice', + name='date', + field=models.DateTimeField(auto_now_add=True, verbose_name='date'), + ), + migrations.AlterField( + model_name='comnpaypayment', + name='minimum_payment', + field=models.DecimalField(decimal_places=2, default=1, help_text='The minimal amount of money you have to use when paying with ComNpay.', max_digits=5, verbose_name='minimum payment'), + ), + migrations.AlterField( + model_name='comnpaypayment', + name='production', + field=models.BooleanField(default=True, verbose_name='production mode enabled (production URL, instead of homologation)'), + ), + migrations.AlterField( + model_name='costestimate', + name='validity', + field=models.DurationField(help_text='DD HH:MM:SS', verbose_name='period of validity'), + ), + migrations.AlterField( + model_name='custominvoice', + name='address', + field=models.CharField(max_length=255, verbose_name='address'), + ), + migrations.AlterField( + model_name='custominvoice', + name='paid', + field=models.BooleanField(default=False, verbose_name='paid'), + ), + migrations.AlterField( + model_name='custominvoice', + name='payment', + field=models.CharField(max_length=255, verbose_name='payment type'), + ), + migrations.AlterField( + model_name='custominvoice', + name='recipient', + field=models.CharField(max_length=255, verbose_name='recipient'), + ), + migrations.AlterField( + model_name='custominvoice', + name='remark', + field=models.TextField(blank=True, null=True, verbose_name='remark'), + ), + ] diff --git a/cotisations/migrations/__init__.py b/cotisations/migrations/__init__.py index 20abb0d2..b409e525 100644 --- a/cotisations/migrations/__init__.py +++ b/cotisations/migrations/__init__.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -19,4 +19,3 @@ # 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. - diff --git a/cotisations/models.py b/cotisations/models.py index c6b7cd1c..215fcdb5 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # Copyright © 2018 Hugo Levy-Falk # @@ -51,17 +51,12 @@ from machines.models import regen from re2o.field_permissions import FieldPermissionModelMixin from re2o.mixins import AclMixin, RevMixin -from cotisations.utils import ( - find_payment_method, send_mail_invoice, send_mail_voucher -) +from cotisations.utils import find_payment_method, send_mail_invoice, send_mail_voucher from cotisations.validators import check_no_balance class BaseInvoice(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): - date = models.DateTimeField( - auto_now_add=True, - verbose_name=_("Date") - ) + date = models.DateTimeField(auto_now_add=True, verbose_name=_("date")) # TODO : change prix to price def prix(self): @@ -69,9 +64,9 @@ class BaseInvoice(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): Returns: the raw price without the quantities. Deprecated, use :total_price instead. """ - price = Vente.objects.filter( - facture=self - ).aggregate(models.Sum('prix'))['prix__sum'] + price = Vente.objects.filter(facture=self).aggregate(models.Sum("prix"))[ + "prix__sum" + ] return price # TODO : change prix to price @@ -81,23 +76,24 @@ class BaseInvoice(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): and take the quantities into account. """ # TODO : change Vente to somethingelse - return Vente.objects.filter( - facture=self - ).aggregate( - total=models.Sum( - models.F('prix')*models.F('number'), - output_field=models.DecimalField() - ) - )['total'] or 0 + return ( + Vente.objects.filter(facture=self).aggregate( + total=models.Sum( + models.F("prix") * models.F("number"), + output_field=models.DecimalField(), + ) + )["total"] + or 0 + ) def name(self): """ Returns : a string with the name of all the articles in the invoice. Used for reprensenting the invoice with a string. """ - name = ' - '.join(Vente.objects.filter( - facture=self - ).values_list('name', flat=True)) + name = " - ".join( + Vente.objects.filter(facture=self).values_list("name", flat=True) + ) return name @@ -122,43 +118,29 @@ class Facture(BaseInvoice): necessary in case of non-payment. """ - user = models.ForeignKey('users.User', on_delete=models.PROTECT) + user = models.ForeignKey("users.User", on_delete=models.PROTECT) # TODO : change paiement to payment - paiement = models.ForeignKey('Paiement', on_delete=models.PROTECT) + paiement = models.ForeignKey("Paiement", on_delete=models.PROTECT) # TODO : change banque to bank banque = models.ForeignKey( - 'Banque', - on_delete=models.PROTECT, - blank=True, - null=True + "Banque", on_delete=models.PROTECT, blank=True, null=True ) # TODO : maybe change to cheque nummber because not evident cheque = models.CharField( - max_length=255, - blank=True, - verbose_name=_("cheque number") + max_length=255, blank=True, verbose_name=_("cheque number") ) # TODO : change name to validity for clarity - valid = models.BooleanField( - default=False, - verbose_name=_("validated") - ) + valid = models.BooleanField(default=False, verbose_name=_("validated")) # TODO : changed name to controlled for clarity - control = models.BooleanField( - default=False, - verbose_name=_("controlled") - ) + control = models.BooleanField(default=False, verbose_name=_("controlled")) class Meta: abstract = False permissions = ( # TODO : change facture to invoice - ('change_facture_control', - _("Can edit the \"controlled\" state")), - ('view_facture', - _("Can view an invoice object")), - ('change_all_facture', - _("Can edit all the previous invoices")), + ("change_facture_control", _("Can edit the \"controlled\" state")), + ("view_facture", _("Can view an invoice object")), + ("change_all_facture", _("Can edit all the previous invoices")), ) verbose_name = _("invoice") verbose_name_plural = _("invoices") @@ -169,52 +151,102 @@ class Facture(BaseInvoice): return self.vente_set.all() def can_edit(self, user_request, *args, **kwargs): - if not user_request.has_perm('cotisations.change_facture'): - return False, _("You don't have the right to edit an invoice.") - elif not user_request.has_perm('cotisations.change_all_facture') and \ - not self.user.can_edit(user_request, *args, **kwargs)[0]: - return False, _("You don't have the right to edit this user's " - "invoices.") - elif not user_request.has_perm('cotisations.change_all_facture') and \ - (self.control or not self.valid): - return False, _("You don't have the right to edit an invoice " - "already controlled or invalidated.") + user_can, _message, permissions = self.user.can_edit( + user_request, *args, **kwargs + ) + if not user_request.has_perm("cotisations.change_facture"): + return ( + False, + _("You don't have the right to edit an invoice."), + ("cotisations.change_facture",), + ) + elif ( + not user_request.has_perm("cotisations.change_all_facture") and not user_can + ): + return ( + False, + _("You don't have the right to edit this user's invoices."), + ("cotisations.change_all_facture",) + permissions, + ) + elif not user_request.has_perm("cotisations.change_all_facture") and ( + self.control or not self.valid + ): + return ( + False, + _( + "You don't have the right to edit an invoice" + " already controlled or invalidated." + ), + ("cotisations.change_all_facture",), + ) else: - return True, None + return True, None, None def can_delete(self, user_request, *args, **kwargs): - if not user_request.has_perm('cotisations.delete_facture'): - return False, _("You don't have the right to delete an invoice.") - elif not user_request.has_perm('cotisations.change_all_facture') and \ - not self.user.can_edit(user_request, *args, **kwargs)[0]: - return False, _("You don't have the right to delete this user's " - "invoices.") - elif not user_request.has_perm('cotisations.change_all_facture') and \ - (self.control or not self.valid): - return False, _("You don't have the right to delete an invoice " - "already controlled or invalidated.") + user_can, _message, permissions = self.user.can_edit( + user_request, *args, **kwargs + ) + if not user_request.has_perm("cotisations.delete_facture"): + return ( + False, + _("You don't have the right to delete an invoice."), + ("cotisations.delete_facture",), + ) + elif ( + not user_request.has_perm("cotisations.change_all_facture") and not user_can + ): + return ( + False, + _("You don't have the right to delete this user's invoices."), + ("cotisations.change_all_facture",) + permissions, + ) + elif not user_request.has_perm("cotisations.change_all_facture") and ( + self.control or not self.valid + ): + return ( + False, + _( + "You don't have the right to delete an invoice" + " already controlled or invalidated." + ), + ("cotisations.change_all_facture",), + ) else: - return True, None + return True, None, None def can_view(self, user_request, *_args, **_kwargs): - if not user_request.has_perm('cotisations.view_facture'): + if not user_request.has_perm("cotisations.view_facture"): if self.user != user_request: - return False, _("You don't have the right to view someone else's " - "invoices history.") + return ( + False, + _( + "You don't have the right to view someone else's" + " invoices history." + ), + ("cotisations.view_facture",), + ) elif not self.valid: - return False, _("The invoice has been invalidated.") + return ( + False, + _("The invoice has been invalidated."), + ("cotisations.view_facture",), + ) else: - return True, None + return True, None, None else: - return True, None + return True, None, None @staticmethod def can_change_control(user_request, *_args, **_kwargs): """ Returns True if the user can change the 'controlled' status of this invoice """ + can = user_request.has_perm("cotisations.change_facture_control") return ( - user_request.has_perm('cotisations.change_facture_control'), + can, _("You don't have the right to edit the \"controlled\" state.") + if not can + else None, + ("cotisations.change_facture_control",), ) @staticmethod @@ -225,19 +257,25 @@ class Facture(BaseInvoice): :return: a message and a boolean which is True if the user can create an invoice or if the `options.allow_self_subscription` is set. """ - if user_request.has_perm('cotisations.add_facture'): - return True, None + if user_request.has_perm("cotisations.add_facture"): + return True, None, None if len(Paiement.find_allowed_payments(user_request)) <= 0: - return False, _("There are no payment method which you can use.") + return ( + False, + _("There are no payment methods that you can use."), + ("cotisations.add_facture",), + ) if len(Article.find_allowed_articles(user_request, user_request)) <= 0: - return False, _("There are no article that you can buy.") - return True, None + return ( + False, + _("There are no articles that you can buy."), + ("cotisations.add_facture",), + ) + return True, None, None def __init__(self, *args, **kwargs): super(Facture, self).__init__(*args, **kwargs) - self.field_permissions = { - 'control': self.can_change_control, - } + self.field_permissions = {"control": self.can_change_control} self.__original_valid = self.valid self.__original_control = self.control @@ -245,8 +283,7 @@ class Facture(BaseInvoice): """Returns every subscription associated with this invoice.""" return Cotisation.objects.filter( vente__in=self.vente_set.filter( - Q(type_cotisation='All') | - Q(type_cotisation='Adhesion') + Q(type_cotisation="All") | Q(type_cotisation="Adhesion") ) ) @@ -254,18 +291,59 @@ class Facture(BaseInvoice): """Returns True if this invoice contains at least one subscribtion.""" return bool(self.get_subscription()) + def reorder_purchases(self): + date_adh = max(self.date, self.user.end_adhesion()) + date_con = max(self.date, self.user.end_connexion()) + for purchase in self.vente_set.all(): + if hasattr(purchase, "cotisation"): + cotisation = purchase.cotisation + if cotisation.type_cotisation == 'Connexion': + cotisation.date_start = date_con + date_con += relativedelta( + months=(purchase.duration or 0) * purchase.number, + days=(purchase.duration_days or 0) * purchase.number, + ) + cotisation.date_end = date_con + elif cotisation.type_cotisation == 'Adhesion': + cotisation.date_start = date_adh + date_adh += relativedelta( + months=(purchase.duration or 0) * purchase.number, + days=(purchase.duration_days or 0) * purchase.number, + ) + cotisation.date_end = date_adh + else: # it is assumed that adhesion is required for a connexion + date = min(date_adh, date_con) + cotisation.date_start = date + date_adh += relativedelta( + months=(purchase.duration or 0) * purchase.number, + days=(purchase.duration_days or 0) * purchase.number, + ) + date_con += relativedelta( + months=(purchase.duration or 0) * purchase.number, + days=(purchase.duration_days or 0) * purchase.number, + ) + date = max(date_adh, date_con) + cotisation.date_end = date + cotisation.save() + purchase.facture = self + purchase.save() + def save(self, *args, **kwargs): super(Facture, self).save(*args, **kwargs) if not self.__original_valid and self.valid: + self.reorder_purchases() send_mail_invoice(self) - if self.is_subscription() \ - and not self.__original_control \ - and self.control \ - and CotisationsOption.get_cached_value('send_voucher_mail'): + if ( + self.is_subscription() + and not self.__original_control + and self.control + and CotisationsOption.get_cached_value("send_voucher_mail") + and self.user.is_adherent() + ): send_mail_voucher(self) def __str__(self): - return str(self.user) + ' ' + str(self.date) + return str(self.user) + " " + str(self.date) @receiver(post_save, sender=Facture) @@ -273,7 +351,7 @@ def facture_post_save(**kwargs): """ Synchronise the LDAP user after an invoice has been saved. """ - facture = kwargs['instance'] + facture = kwargs["instance"] if facture.valid: user = facture.user user.set_active() @@ -285,46 +363,27 @@ def facture_post_delete(**kwargs): """ Synchronise the LDAP user after an invoice has been deleted. """ - user = kwargs['instance'].user + user = kwargs["instance"].user user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) class CustomInvoice(BaseInvoice): class Meta: - permissions = ( - ('view_custominvoice', _("Can view a custom invoice object")), - ) - recipient = models.CharField( - max_length=255, - verbose_name=_("Recipient") - ) - payment = models.CharField( - max_length=255, - verbose_name=_("Payment type") - ) - address = models.CharField( - max_length=255, - verbose_name=_("Address") - ) - paid = models.BooleanField( - verbose_name=_("Paid"), - default=False - ) - remark = models.TextField( - verbose_name=_("Remark"), - blank=True, - null=True - ) + permissions = (("view_custominvoice", _("Can view a custom invoice object")),) + + recipient = models.CharField(max_length=255, verbose_name=_("recipient")) + payment = models.CharField(max_length=255, verbose_name=_("payment type")) + address = models.CharField(max_length=255, verbose_name=_("address")) + paid = models.BooleanField(verbose_name=_("paid"), default=False) + remark = models.TextField(verbose_name=_("remark"), blank=True, null=True) class CostEstimate(CustomInvoice): class Meta: - permissions = ( - ('view_costestimate', _("Can view a cost estimate object")), - ) + permissions = (("view_costestimate", _("Can view a cost estimate object")),) + validity = models.DurationField( - verbose_name=_("Period of validity"), - help_text="DD HH:MM:SS" + verbose_name=_("period of validity"), help_text="DD HH:MM:SS" ) final_invoice = models.ForeignKey( CustomInvoice, @@ -332,7 +391,7 @@ class CostEstimate(CustomInvoice): null=True, blank=True, related_name="origin_cost_estimate", - primary_key=False + primary_key=False, ) def create_invoice(self): @@ -351,21 +410,24 @@ class CostEstimate(CustomInvoice): self.save() for sale in self.vente_set.all(): Vente.objects.create( - facture=invoice, - name=sale.name, - prix=sale.prix, - number=sale.number, + facture=invoice, name=sale.name, prix=sale.prix, number=sale.number ) return invoice def can_delete(self, user_request, *args, **kwargs): - if not user_request.has_perm('cotisations.delete_costestimate'): - return False, _("You don't have the right " - "to delete a cost estimate.") + if not user_request.has_perm("cotisations.delete_costestimate"): + return ( + False, + _("You don't have the right to delete a cost estimate."), + ("cotisations.delete_costestimate",), + ) if self.final_invoice is not None: - return False, _("The cost estimate has an " - "invoice and can't be deleted.") - return True, None + return ( + False, + _("The cost estimate has an invoice and can't be deleted."), + None, + ) + return True, None, None # TODO : change Vente to Purchase @@ -384,38 +446,33 @@ class Vente(RevMixin, AclMixin, models.Model): # TODO : change this to English COTISATION_TYPE = ( - ('Connexion', _("Connection")), - ('Adhesion', _("Membership")), - ('All', _("Both of them")), + ("Connexion", _("Connection")), + ("Adhesion", _("Membership")), + ("All", _("Both of them")), ) # TODO : change facture to invoice facture = models.ForeignKey( - 'BaseInvoice', - on_delete=models.CASCADE, - verbose_name=_("invoice") + "BaseInvoice", on_delete=models.CASCADE, verbose_name=_("invoice") ) # TODO : change number to amount for clarity number = models.IntegerField( - validators=[MinValueValidator(1)], - verbose_name=_("amount") + validators=[MinValueValidator(1)], verbose_name=_("amount") ) # TODO : change this field for a ForeinKey to Article - name = models.CharField( - max_length=255, - verbose_name=_("article") - ) + name = models.CharField(max_length=255, verbose_name=_("article")) # TODO : change prix to price # TODO : this field is not needed if you use Article ForeignKey - prix = models.DecimalField( - max_digits=5, - decimal_places=2, - verbose_name=_("price")) + prix = models.DecimalField(max_digits=5, decimal_places=2, verbose_name=_("price")) # TODO : this field is not needed if you use Article ForeignKey duration = models.PositiveIntegerField( + blank=True, null=True, verbose_name=_("duration (in months)") + ) + duration_days = models.PositiveIntegerField( blank=True, null=True, - verbose_name=_("duration (in months)") + validators=[MinValueValidator(0)], + verbose_name=_("duration (in days, will be added to duration in months)"), ) # TODO : this field is not needed if you use Article ForeignKey type_cotisation = models.CharField( @@ -423,13 +480,13 @@ class Vente(RevMixin, AclMixin, models.Model): blank=True, null=True, max_length=255, - verbose_name=_("subscription type") + verbose_name=_("subscription type"), ) class Meta: permissions = ( - ('view_vente', _("Can view a purchase object")), - ('change_all_vente', _("Can edit all the previous purchases")), + ("view_vente", _("Can view a purchase object")), + ("change_all_vente", _("Can edit all the previous purchases")), ) verbose_name = _("purchase") verbose_name_plural = _("purchases") @@ -439,56 +496,43 @@ class Vente(RevMixin, AclMixin, models.Model): """ Returns: the total of price for this amount of items. """ - return self.prix*self.number + return self.prix * self.number def update_cotisation(self): """ Update the related object 'cotisation' if there is one. Based on the duration of the purchase. """ - if hasattr(self, 'cotisation'): + if hasattr(self, "cotisation"): cotisation = self.cotisation cotisation.date_end = cotisation.date_start + relativedelta( - months=self.duration*self.number) + months=(self.duration or 0) * self.number, + days=(self.duration_days or 0) * self.number, + ) return def create_cotis(self, date_start=False): """ - Update and create a 'cotisation' related object if there is a - cotisation_type defined (which means the article sold represents - a cotisation) + Creates a cotisation without initializing the dates (start and end ar set to self.facture.facture.date) and without saving it. You should use Facture.reorder_purchases to set the right dates. """ try: invoice = self.facture.facture except Facture.DoesNotExist: return - if not hasattr(self, 'cotisation') and self.type_cotisation: + if not hasattr(self, "cotisation") and self.type_cotisation: cotisation = Cotisation(vente=self) cotisation.type_cotisation = self.type_cotisation if date_start: - end_cotisation = Cotisation.objects.filter( - vente__in=Vente.objects.filter( - facture__in=Facture.objects.filter( - user=invoice.user - ).exclude(valid=False)) - ).filter( - Q(type_cotisation='All') | - Q(type_cotisation=self.type_cotisation) - ).filter( - date_start__lt=date_start - ).aggregate(Max('date_end'))['date_end__max'] - elif self.type_cotisation == "Adhesion": - end_cotisation = invoice.user.end_adhesion() + cotisation.date_start = date_start + cotisation.date_end = cotisation.date_start + relativedelta( + months=(self.duration or 0) * self.number, + days=(self.duration_days or 0) * self.number, + ) + self.save() + cotisation.save() else: - end_cotisation = invoice.user.end_connexion() - date_start = date_start or timezone.now() - end_cotisation = end_cotisation or date_start - date_max = max(end_cotisation, date_start) - cotisation.date_start = date_max - cotisation.date_end = cotisation.date_start + relativedelta( - months=self.duration*self.number - ) - return + cotisation.date_start = invoice.date + cotisation.date_end = invoice.date def save(self, *args, **kwargs): """ @@ -497,51 +541,87 @@ class Vente(RevMixin, AclMixin, models.Model): effect on the user's cotisation """ # Checking that if a cotisation is specified, there is also a duration - if self.type_cotisation and not self.duration: - raise ValidationError( - _("Duration must be specified for a subscription.") - ) + if self.type_cotisation and not (self.duration or self.duration_days): + raise ValidationError(_("Duration must be specified for a subscription.")) self.update_cotisation() super(Vente, self).save(*args, **kwargs) def can_edit(self, user_request, *args, **kwargs): - if not user_request.has_perm('cotisations.change_vente'): - return False, _("You don't have the right to edit the purchases.") - elif (not user_request.has_perm('cotisations.change_all_facture') and - not self.facture.user.can_edit( - user_request, *args, **kwargs - )[0]): - return False, _("You don't have the right to edit this user's " - "purchases.") - elif (not user_request.has_perm('cotisations.change_all_vente') and - (self.facture.control or not self.facture.valid)): - return False, _("You don't have the right to edit a purchase " - "already controlled or invalidated.") + user_can, _message, permissions = self.facture.user.can_edit( + user_request, *args, **kwargs + ) + if not user_request.has_perm("cotisations.change_vente"): + return ( + False, + _("You don't have the right to edit a purchase."), + ("cotisations.change_vente",), + ) + elif not (user_request.has_perm("cotisations.change_all_facture") or user_can): + return ( + False, + _("You don't have the right to edit this user's purchases."), + ("cotisations.change_all_facture",) + permissions, + ) + elif not user_request.has_perm("cotisations.change_all_vente") and ( + self.facture.control or not self.facture.valid + ): + return ( + False, + _( + "You don't have the right to edit a purchase" + " already controlled or invalidated." + ), + ("cotisations.change_all_vente",), + ) else: - return True, None + return True, None, None def can_delete(self, user_request, *args, **kwargs): - if not user_request.has_perm('cotisations.delete_vente'): - return False, _("You don't have the right to delete a purchase.") - if not self.facture.user.can_edit(user_request, *args, **kwargs)[0]: - return False, _("You don't have the right to delete this user's " - "purchases.") + user_can, _message, permissions = self.facture.user.can_edit( + user_request, *args, **kwargs + ) + if not user_request.has_perm("cotisations.delete_vente"): + return ( + False, + _("You don't have the right to delete a purchase."), + ("cotisations.delete_vente",), + ) + if not user_can: + return ( + False, + _("You don't have the right to delete this user's purchases."), + permissions, + ) if self.facture.control or not self.facture.valid: - return False, _("You don't have the right to delete a purchase " - "already controlled or invalidated.") + return ( + False, + _( + "You don't have the right to delete a purchase" + " already controlled or invalidated." + ), + None, + ) else: - return True, None + return True, None, 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, _("You don't have the right to view someone " - "else's purchase history.") + if ( + not user_request.has_perm("cotisations.view_vente") + and self.facture.user != user_request + ): + return ( + False, + _( + "You don't have the right to view someone" + " else's purchase history." + ), + ("cotisations.view_vente",), + ) else: - return True, None + return True, None, None def __str__(self): - return str(self.name) + ' ' + str(self.facture) + return str(self.name) + " " + str(self.facture) # TODO : change vente to purchase @@ -551,12 +631,12 @@ def vente_post_save(**kwargs): Creates a 'cotisation' related object if needed and synchronise the LDAP user when a purchase has been saved. """ - purchase = kwargs['instance'] + purchase = kwargs["instance"] try: purchase.facture.facture except Facture.DoesNotExist: return - if hasattr(purchase, 'cotisation'): + if hasattr(purchase, "cotisation"): purchase.cotisation.vente = purchase purchase.cotisation.save() if purchase.type_cotisation: @@ -573,7 +653,7 @@ def vente_post_delete(**kwargs): """ Synchronise the LDAP user after a purchase has been deleted. """ - purchase = kwargs['instance'] + purchase = kwargs["instance"] try: invoice = purchase.facture.facture except Facture.DoesNotExist: @@ -599,38 +679,39 @@ class Article(RevMixin, AclMixin, models.Model): # TODO : Either use TYPE or TYPES in both choices but not both USER_TYPES = ( - ('Adherent', _("Member")), - ('Club', _("Club")), - ('All', _("Both of them")), + ("Adherent", _("Member")), + ("Club", _("Club")), + ("All", _("Both of them")), ) COTISATION_TYPE = ( - ('Connexion', _("Connection")), - ('Adhesion', _("Membership")), - ('All', _("Both of them")), + ("Connexion", _("Connection")), + ("Adhesion", _("Membership")), + ("All", _("Both of them")), ) - name = models.CharField( - max_length=255, - verbose_name=_("designation") - ) + name = models.CharField(max_length=255, verbose_name=_("designation")) # TODO : change prix to price prix = models.DecimalField( - max_digits=5, - decimal_places=2, - verbose_name=_("unit price") + max_digits=5, decimal_places=2, verbose_name=_("unit price") ) duration = models.PositiveIntegerField( blank=True, null=True, validators=[MinValueValidator(0)], - verbose_name=_("duration (in months)") + verbose_name=_("duration (in months)"), + ) + duration_days = models.PositiveIntegerField( + blank=True, + null=True, + validators=[MinValueValidator(0)], + verbose_name=_("duration (in days, will be added to duration in months)"), ) type_user = models.CharField( choices=USER_TYPES, - default='All', + default="All", max_length=255, - verbose_name=_("type of users concerned") + verbose_name=_("type of users concerned"), ) type_cotisation = models.CharField( choices=COTISATION_TYPE, @@ -638,32 +719,27 @@ class Article(RevMixin, AclMixin, models.Model): blank=True, null=True, max_length=255, - verbose_name=_("subscription type") + verbose_name=_("subscription type"), ) available_for_everyone = models.BooleanField( - default=False, - verbose_name=_("is available for every user") + default=False, verbose_name=_("is available for every user") ) - unique_together = ('name', 'type_user') + unique_together = ("name", "type_user") class Meta: permissions = ( - ('view_article', _("Can view an article object")), - ('buy_every_article', _("Can buy every article")) + ("view_article", _("Can view an article object")), + ("buy_every_article", _("Can buy every article")), ) verbose_name = "article" verbose_name_plural = "articles" def clean(self): - if self.name.lower() == 'solde': - raise ValidationError( - _("Balance is a reserved article name.") - ) - if self.type_cotisation and not self.duration: - raise ValidationError( - _("Duration must be specified for a subscription.") - ) + if self.name.lower() == "solde": + raise ValidationError(_("Solde is a reserved article name.")) + if self.type_cotisation and not (self.duration or self.duration_days): + raise ValidationError(_("Duration must be specified for a subscription.")) def __str__(self): return self.name @@ -679,11 +755,15 @@ class Article(RevMixin, AclMixin, models.Model): A boolean stating if usage is granted and an explanation message if the boolean is `False`. """ - return ( + can = ( self.available_for_everyone - or user.has_perm('cotisations.buy_every_article') - or user.has_perm('cotisations.add_facture'), - _("You can't buy this article.") + or user.has_perm("cotisations.buy_every_article") + or user.has_perm("cotisations.add_facture") + ) + return ( + can, + _("You can't buy this article.") if not can else None, + ("cotisations.buy_every_article", "cotisations.add_facture"), ) @classmethod @@ -695,20 +775,18 @@ class Article(RevMixin, AclMixin, models.Model): target_user: The user to sell articles """ if target_user is None: - objects_pool = cls.objects.filter(Q(type_user='All')) + objects_pool = cls.objects.all() elif target_user.is_class_club: - objects_pool = cls.objects.filter( - Q(type_user='All') | Q(type_user='Club') - ) + objects_pool = cls.objects.filter(Q(type_user="All") | Q(type_user="Club")) else: objects_pool = cls.objects.filter( - Q(type_user='All') | Q(type_user='Adherent') + Q(type_user="All") | Q(type_user="Adherent") ) if target_user is not None and not target_user.is_adherent(): objects_pool = objects_pool.filter( - Q(type_cotisation='All') | Q(type_cotisation='Adhesion') + Q(type_cotisation="All") | Q(type_cotisation="Adhesion") ) - if user.has_perm('cotisations.buy_every_article'): + if user.has_perm("cotisations.buy_every_article"): return objects_pool return objects_pool.filter(available_for_everyone=True) @@ -722,14 +800,10 @@ class Banque(RevMixin, AclMixin, models.Model): it's easier to use simple object for the banks. """ - name = models.CharField( - max_length=255, - ) + name = models.CharField(max_length=255) class Meta: - permissions = ( - ('view_banque', _("Can view a bank object")), - ) + permissions = (("view_banque", _("Can view a bank object")),) verbose_name = _("bank") verbose_name_plural = _("banks") @@ -747,26 +821,22 @@ class Paiement(RevMixin, AclMixin, models.Model): """ # TODO : change moyen to method - moyen = models.CharField( - max_length=255, - verbose_name=_("method") - ) + moyen = models.CharField(max_length=255, verbose_name=_("method")) available_for_everyone = models.BooleanField( - default=False, - verbose_name=_("is available for every user") + default=False, verbose_name=_("is available for every user") ) is_balance = models.BooleanField( default=False, editable=False, verbose_name=_("is user balance"), help_text=_("There should be only one balance payment method."), - validators=[check_no_balance] + validators=[check_no_balance], ) class Meta: permissions = ( - ('view_paiement', _("Can view a payment method object")), - ('use_every_payment', _("Can use every payment method")), + ("view_paiement", _("Can view a payment method object")), + ("use_every_payment", _("Can use every payment method")), ) verbose_name = _("payment method") verbose_name_plural = _("payment methods") @@ -807,22 +877,19 @@ class Paiement(RevMixin, AclMixin, models.Model): if any(sell.type_cotisation for sell in invoice.vente_set.all()): messages.success( request, - _("The subscription of %(member_name)s was extended to" - " %(end_date)s.") % { - 'member_name': invoice.user.pseudo, - 'end_date': invoice.user.end_adhesion() - } + _( + "The subscription of %(member_name)s was extended to" + " %(end_date)s." + ) + % { + "member_name": invoice.user.pseudo, + "end_date": invoice.user.end_adhesion(), + }, ) # Else, only tell the invoice was created else: - messages.success( - request, - _("The invoice was created.") - ) - return redirect(reverse( - 'users:profil', - kwargs={'userid': invoice.user.pk} - )) + messages.success(request, _("The invoice was created.")) + return redirect(reverse("users:profil", kwargs={"userid": invoice.user.pk})) def can_use_payment(self, user, *_args, **_kwargs): """Check if a user can use this payment. @@ -834,11 +901,15 @@ class Paiement(RevMixin, AclMixin, models.Model): A boolean stating if usage is granted and an explanation message if the boolean is `False`. """ - return ( + can = ( self.available_for_everyone - or user.has_perm('cotisations.use_every_payment') - or user.has_perm('cotisations.add_facture'), - _("You can't use this payment method.") + or user.has_perm("cotisations.use_every_payment") + or user.has_perm("cotisations.add_facture") + ) + return ( + can, + _("You can't use this payment method.") if not can else None, + ("cotisations.use_every_payment", "cotisations.add_facture"), ) @classmethod @@ -848,7 +919,7 @@ class Paiement(RevMixin, AclMixin, models.Model): Args: user: The user requesting payment methods. """ - if user.has_perm('cotisations.use_every_payment'): + if user.has_perm("cotisations.use_every_payment"): return cls.objects.all() return cls.objects.filter(available_for_everyone=True) @@ -856,7 +927,7 @@ class Paiement(RevMixin, AclMixin, models.Model): p = find_payment_method(self) if p is not None: return p._meta.verbose_name - return _("No custom payment method.") + return _("No custom payment methods.") class Cotisation(RevMixin, AclMixin, models.Model): @@ -872,70 +943,96 @@ class Cotisation(RevMixin, AclMixin, models.Model): """ COTISATION_TYPE = ( - ('Connexion', _("Connection")), - ('Adhesion', _("Membership")), - ('All', _("Both of them")), + ("Connexion", _("Connection")), + ("Adhesion", _("Membership")), + ("All", _("Both of them")), ) # TODO : change vente to purchase vente = models.OneToOneField( - 'Vente', - on_delete=models.CASCADE, - null=True, - verbose_name=_("purchase") + "Vente", on_delete=models.CASCADE, null=True, verbose_name=_("purchase") ) type_cotisation = models.CharField( choices=COTISATION_TYPE, max_length=255, - default='All', - verbose_name=_("subscription type") - ) - date_start = models.DateTimeField( - verbose_name=_("start date") - ) - date_end = models.DateTimeField( - verbose_name=_("end date") + default="All", + verbose_name=_("subscription type"), ) + date_start = models.DateTimeField(verbose_name=_("start date")) + date_end = models.DateTimeField(verbose_name=_("end date")) class Meta: permissions = ( - ('view_cotisation', _("Can view a subscription object")), - ('change_all_cotisation', _("Can edit the previous subscriptions")), + ("view_cotisation", _("Can view a subscription object")), + ("change_all_cotisation", _("Can edit the previous subscriptions")), ) verbose_name = _("subscription") verbose_name_plural = _("subscriptions") def can_edit(self, user_request, *_args, **_kwargs): - if not user_request.has_perm('cotisations.change_cotisation'): - return False, _("You don't have the right to edit a subscription.") - elif not user_request.has_perm('cotisations.change_all_cotisation') \ - and (self.vente.facture.control or - not self.vente.facture.valid): - return False, _("You don't have the right to edit a subscription " - "already controlled or invalidated.") + if not user_request.has_perm("cotisations.change_cotisation"): + return ( + False, + _("You don't have the right to edit a subscription."), + ("cotisations.change_cotisation",), + ) + elif not user_request.has_perm("cotisations.change_all_cotisation") and ( + self.vente.facture.control or not self.vente.facture.valid + ): + return ( + False, + _( + "You don't have the right to edit a subscription" + " already controlled or invalidated." + ), + ("cotisations.change_all_cotisation",), + ) else: - return True, None + return True, None, None def can_delete(self, user_request, *_args, **_kwargs): - if not user_request.has_perm('cotisations.delete_cotisation'): - return False, _("You don't have the right to delete a " - "subscription.") + if not user_request.has_perm("cotisations.delete_cotisation"): + return ( + False, + _("You don't have the right to delete a subscription."), + ("cotisations.delete_cotisation",), + ) if self.vente.facture.control or not self.vente.facture.valid: - return False, _("You don't have the right to delete a subscription " - "already controlled or invalidated.") + return ( + False, + _( + "You don't have the right to delete a subscription" + " already controlled or invalidated." + ), + None, + ) else: - return True, None + return True, None, 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, _("You don't have the right to view someone else's " - "subscription history.") + if ( + not user_request.has_perm("cotisations.view_cotisation") + and self.vente.facture.user != user_request + ): + return ( + False, + _( + "You don't have the right to view someone else's" + " subscription history." + ), + ("cotisations.view_cotisation",), + ) else: - return True, None + return True, None, None def __str__(self): - return str(self.vente) + return ( + str(self.vente) + + "from " + + str(self.date_start) + + " to " + + str(self.date_end) + ) @receiver(post_save, sender=Cotisation) @@ -944,10 +1041,10 @@ def cotisation_post_save(**_kwargs): Mark some services as needing a regeneration after the edition of a cotisation. Indeed the membership status may have changed. """ - regen('dns') - regen('dhcp') - regen('mac_ip_list') - regen('mailing') + regen("dns") + regen("dhcp") + regen("mac_ip_list") + regen("mailing") @receiver(post_delete, sender=Cotisation) @@ -956,5 +1053,5 @@ def cotisation_post_delete(**_kwargs): Mark some services as needing a regeneration after the deletion of a cotisation. Indeed the membership status may have changed. """ - regen('mac_ip_list') - regen('mailing') + regen("mac_ip_list") + regen("mailing") diff --git a/cotisations/payment_methods/__init__.py b/cotisations/payment_methods/__init__.py index 1efde30b..cbb9c4a6 100644 --- a/cotisations/payment_methods/__init__.py +++ b/cotisations/payment_methods/__init__.py @@ -127,11 +127,6 @@ method to your model, where `form` is an instance of """ -from . import comnpay, cheque, balance, note_kfet, urls +from . import comnpay, cheque, balance, note_kfet, free, urls -PAYMENT_METHODS = [ - comnpay, - cheque, - balance, - note_kfet -] +PAYMENT_METHODS = [comnpay, cheque, balance, note_kfet, free] diff --git a/cotisations/payment_methods/balance/__init__.py b/cotisations/payment_methods/balance/__init__.py index cacd73f7..ebfaaddd 100644 --- a/cotisations/payment_methods/balance/__init__.py +++ b/cotisations/payment_methods/balance/__init__.py @@ -22,6 +22,7 @@ This module contains a method to pay online using user balance. """ from . import models + NAME = "BALANCE" PaymentMethod = models.BalancePayment diff --git a/cotisations/payment_methods/balance/models.py b/cotisations/payment_methods/balance/models.py index 221cca3e..afa43c48 100644 --- a/cotisations/payment_methods/balance/models.py +++ b/cotisations/payment_methods/balance/models.py @@ -40,21 +40,21 @@ class BalancePayment(PaymentMethodMixin, models.Model): payment = models.OneToOneField( Paiement, on_delete=models.CASCADE, - related_name='payment_method', - editable=False + related_name="payment_method_balance", + editable=False, ) minimum_balance = models.DecimalField( - verbose_name=_("Minimum balance"), - help_text=_("The minimal amount of money allowed for the balance" - " at the end of a payment. You can specify negative " - "amount." - ), + verbose_name=_("minimum balance"), + help_text=_( + "The minimal amount of money allowed for the balance at the end" + " of a payment. You can specify a negative amount." + ), max_digits=5, decimal_places=2, default=0, ) maximum_balance = models.DecimalField( - verbose_name=_("Maximum balance"), + verbose_name=_("maximum balance"), help_text=_("The maximal amount of money allowed for the balance."), max_digits=5, decimal_places=2, @@ -63,8 +63,7 @@ class BalancePayment(PaymentMethodMixin, models.Model): null=True, ) credit_balance_allowed = models.BooleanField( - verbose_name=_("Allow user to credit their balance"), - default=False, + verbose_name=_("allow user to credit their balance"), default=False ) def end_payment(self, invoice, request): @@ -74,27 +73,17 @@ class BalancePayment(PaymentMethodMixin, models.Model): user = invoice.user total_price = invoice.prix_total() if user.solde - total_price < self.minimum_balance: - messages.error( - request, - _("Your balance is too low for this operation.") - ) - return redirect(reverse( - 'users:profil', - kwargs={'userid': user.id} - )) - return invoice.paiement.end_payment( - invoice, - request, - use_payment_method=False - ) + messages.error(request, _("Your balance is too low for this operation.")) + return redirect(reverse("users:profil", kwargs={"userid": user.id})) + return invoice.paiement.end_payment(invoice, request, use_payment_method=False) def valid_form(self, form): """Checks that there is not already a balance payment method.""" p = Paiement.objects.filter(is_balance=True) if len(p) > 0: form.add_error( - 'payment_method', - _("There is already a payment method for user balance.") + "payment_method", + _("There is already a payment method for user balance."), ) def alter_payment(self, payment): @@ -107,12 +96,11 @@ class BalancePayment(PaymentMethodMixin, models.Model): """ return ( user.solde - price >= self.minimum_balance, - _("Your balance is too low for this operation.") + _("Your balance is too low for this operation."), ) def can_credit_balance(self, user_request): return ( - len(Paiement.find_allowed_payments(user_request) - .exclude(is_balance=True)) > 0 + len(Paiement.find_allowed_payments(user_request).exclude(is_balance=True)) + > 0 ) and self.credit_balance_allowed - diff --git a/cotisations/payment_methods/cheque/__init__.py b/cotisations/payment_methods/cheque/__init__.py index 9eb17b09..27e985e5 100644 --- a/cotisations/payment_methods/cheque/__init__.py +++ b/cotisations/payment_methods/cheque/__init__.py @@ -22,6 +22,7 @@ This module contains a method to pay online using cheque. """ from . import models, urls, views + NAME = "CHEQUE" PaymentMethod = models.ChequePayment diff --git a/cotisations/payment_methods/cheque/forms.py b/cotisations/payment_methods/cheque/forms.py index 37942687..370a701d 100644 --- a/cotisations/payment_methods/cheque/forms.py +++ b/cotisations/payment_methods/cheque/forms.py @@ -26,6 +26,7 @@ from cotisations.models import Facture as Invoice class InvoiceForm(FormRevMixin, forms.ModelForm): """A simple form to get the bank a the cheque number.""" + class Meta: model = Invoice - fields = ['banque', 'cheque'] + fields = ["banque", "cheque"] diff --git a/cotisations/payment_methods/cheque/models.py b/cotisations/payment_methods/cheque/models.py index 8f00ff46..62479f22 100644 --- a/cotisations/payment_methods/cheque/models.py +++ b/cotisations/payment_methods/cheque/models.py @@ -33,21 +33,19 @@ class ChequePayment(PaymentMethodMixin, models.Model): """ class Meta: - verbose_name = _("Cheque") + verbose_name = _("cheque") payment = models.OneToOneField( Paiement, on_delete=models.CASCADE, - related_name='payment_method', - editable=False + related_name="payment_method_cheque", + editable=False, ) def end_payment(self, invoice, request): """Invalidates the invoice then redirect the user towards a view asking for informations to add to the invoice before validating it. """ - return redirect(reverse( - 'cotisations:cheque:validate', - kwargs={'invoice_pk': invoice.pk} - )) - + return redirect( + reverse("cotisations:cheque:validate", kwargs={"invoice_pk": invoice.pk}) + ) diff --git a/cotisations/payment_methods/cheque/urls.py b/cotisations/payment_methods/cheque/urls.py index 54fe6a50..a29e1b8c 100644 --- a/cotisations/payment_methods/cheque/urls.py +++ b/cotisations/payment_methods/cheque/urls.py @@ -21,10 +21,4 @@ from django.conf.urls import url from . import views -urlpatterns = [ - url( - r'^validate/(?P[0-9]+)$', - views.cheque, - name='validate' - ) -] +urlpatterns = [url(r"^validate/(?P[0-9]+)$", views.cheque, name="validate")] diff --git a/cotisations/payment_methods/cheque/views.py b/cotisations/payment_methods/cheque/views.py index 3cce3e5c..a22e9c09 100644 --- a/cotisations/payment_methods/cheque/views.py +++ b/cotisations/payment_methods/cheque/views.py @@ -42,29 +42,17 @@ def cheque(request, invoice_pk): invoice = get_object_or_404(Invoice, pk=invoice_pk) payment_method = find_payment_method(invoice.paiement) if invoice.valid or not isinstance(payment_method, ChequePayment): - messages.error( - request, - _("You can't pay this invoice with a cheque.") - ) - return redirect(reverse( - 'users:profil', - kwargs={'userid': request.user.pk} - )) + messages.error(request, _("You can't pay this invoice with a cheque.")) + return redirect(reverse("users:profil", kwargs={"userid": request.user.pk})) form = InvoiceForm(request.POST or None, instance=invoice) if form.is_valid(): form.instance.valid = True form.save() return form.instance.paiement.end_payment( - form.instance, - request, - use_payment_method=False + form.instance, request, use_payment_method=False ) return render( request, - 'cotisations/payment.html', - { - 'form': form, - 'amount': invoice.prix_total() - } + "cotisations/payment.html", + {"form": form, "amount": invoice.prix_total()}, ) - diff --git a/cotisations/payment_methods/comnpay/__init__.py b/cotisations/payment_methods/comnpay/__init__.py index 7c80311d..0cfcfab5 100644 --- a/cotisations/payment_methods/comnpay/__init__.py +++ b/cotisations/payment_methods/comnpay/__init__.py @@ -22,5 +22,6 @@ This module contains a method to pay online using comnpay. """ from . import models, urls, views + NAME = "COMNPAY" PaymentMethod = models.ComnpayPayment diff --git a/cotisations/payment_methods/comnpay/comnpay.py b/cotisations/payment_methods/comnpay/comnpay.py index 272ab928..b03239a4 100644 --- a/cotisations/payment_methods/comnpay/comnpay.py +++ b/cotisations/payment_methods/comnpay/comnpay.py @@ -10,13 +10,21 @@ import hashlib from collections import OrderedDict -class Transaction(): +class Transaction: """ The class representing a transaction with all the functions used during the negociation """ - def __init__(self, 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 @@ -26,8 +34,7 @@ class Transaction(): self.typeTr = typeTr self.idTransaction = "" - def buildSecretHTML(self, produit="Produit", montant="0.00", - idTransaction=""): + def buildSecretHTML(self, produit="Produit", montant="0.00", idTransaction=""): """ Build an HTML hidden form with the different parameters for the transaction """ @@ -43,30 +50,26 @@ class Transaction(): idTPE=self.vad_number, idTransaction=self.idTransaction, devise="EUR", - lang='fr', + lang="fr", nom_produit=produit, source=self.source, urlRetourOK=self.urlRetourOK, urlRetourNOK=self.urlRetourNOK, - typeTr=str(self.typeTr) + typeTr=str(self.typeTr), ) if self.urlIPN != "": - array_tpe['urlIPN'] = self.urlIPN + array_tpe["urlIPN"] = self.urlIPN - array_tpe['key'] = self.secret_key - strWithKey = base64.b64encode(bytes( - '|'.join(array_tpe.values()), - 'utf-8' - )) + 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() + array_tpe["sec"] = hashlib.sha512(strWithKey).hexdigest() ret = "" for key in array_tpe: ret += ''.format( - k=key, - v=array_tpe[key] + k=key, v=array_tpe[key] ) return ret @@ -75,12 +78,13 @@ class Transaction(): def validSec(values, secret_key): """ Check if the secret value is correct """ if "sec" in values: - sec = values['sec'] + sec = values["sec"] del values["sec"] - strWithKey = hashlib.sha512(base64.b64encode(bytes( - '|'.join(values.values()) + "|" + secret_key, - 'utf-8' - ))).hexdigest() + strWithKey = hashlib.sha512( + base64.b64encode( + bytes("|".join(values.values()) + "|" + secret_key, "utf-8") + ) + ).hexdigest() return strWithKey.upper() == sec.upper() else: return False diff --git a/cotisations/payment_methods/comnpay/models.py b/cotisations/payment_methods/comnpay/models.py index 7fac089a..2c46f685 100644 --- a/cotisations/payment_methods/comnpay/models.py +++ b/cotisations/payment_methods/comnpay/models.py @@ -41,39 +41,37 @@ class ComnpayPayment(PaymentMethodMixin, models.Model): payment = models.OneToOneField( Paiement, on_delete=models.CASCADE, - related_name='payment_method', - editable=False + related_name="payment_method_comnpay", + editable=False, ) payment_credential = models.CharField( - max_length=255, - default='', - blank=True, - verbose_name=_("ComNpay VAT Number"), + max_length=255, default="", blank=True, verbose_name=_("ComNpay VAT Number") ) payment_pass = AESEncryptedField( - max_length=255, - null=True, - blank=True, - verbose_name=_("ComNpay secret key"), + max_length=255, null=True, blank=True, verbose_name=_("ComNpay secret key") ) minimum_payment = models.DecimalField( - verbose_name=_("Minimum payment"), - help_text=_("The minimal amount of money you have to use when paying" - " with ComNpay"), + verbose_name=_("minimum payment"), + help_text=_( + "The minimal amount of money you have to use when paying with" + " ComNpay." + ), max_digits=5, decimal_places=2, default=1, ) production = models.BooleanField( default=True, - verbose_name=_("Production mode enabled (production URL, instead of homologation)"), + verbose_name=_( + "production mode enabled (production URL, instead of homologation)" + ), ) def return_url_comnpay(self): if self.production: - return 'https://secure.comnpay.com' + return "https://secure.comnpay.com" else: - return 'https://secure.homologation.comnpay.com' + return "https://secure.homologation.comnpay.com" def end_payment(self, invoice, request): """ @@ -85,32 +83,36 @@ class ComnpayPayment(PaymentMethodMixin, models.Model): p = Transaction( str(self.payment_credential), str(self.payment_pass), - 'https://' + host + reverse( - 'cotisations:comnpay:accept_payment', - kwargs={'factureid': invoice.id} + "https://" + + host + + reverse( + "cotisations:comnpay:accept_payment", kwargs={"factureid": invoice.id} ), - 'https://' + host + reverse('cotisations:comnpay:refuse_payment'), - 'https://' + host + reverse('cotisations:comnpay:ipn'), + "https://" + host + reverse("cotisations:comnpay:refuse_payment"), + "https://" + host + reverse("cotisations:comnpay:ipn"), "", - "D" + "D", ) r = { - 'action': self.return_url_comnpay(), - 'method': 'POST', - 'content': p.buildSecretHTML( - _("Pay invoice number ")+str(invoice.id), + "action": self.return_url_comnpay(), + "method": "POST", + "content": p.buildSecretHTML( + _("Pay invoice number ") + str(invoice.id), invoice.prix_total(), - idTransaction=str(invoice.id) + idTransaction=str(invoice.id), ), - 'amount': invoice.prix_total(), + "amount": invoice.prix_total(), } - return render(request, 'cotisations/payment.html', r) + return render(request, "cotisations/payment.html", r) def check_price(self, price, *args, **kwargs): """Checks that the price meets the requirement to be paid with ComNpay. """ - return ((price >= self.minimum_payment), - _("In order to pay your invoice with ComNpay, the price must" - " be greater than {} €.").format(self.minimum_payment)) - + return ( + (price >= self.minimum_payment), + _( + "In order to pay your invoice with ComNpay, the price must" + " be greater than {} €." + ).format(self.minimum_payment), + ) diff --git a/cotisations/payment_methods/comnpay/urls.py b/cotisations/payment_methods/comnpay/urls.py index 241d53dd..69bb3b38 100644 --- a/cotisations/payment_methods/comnpay/urls.py +++ b/cotisations/payment_methods/comnpay/urls.py @@ -22,19 +22,7 @@ from django.conf.urls import url from . import views urlpatterns = [ - url( - r'^accept/(?P[0-9]+)$', - views.accept_payment, - name='accept_payment' - ), - url( - r'^refuse/$', - views.refuse_payment, - name='refuse_payment' - ), - url( - r'^ipn/$', - views.ipn, - name='ipn' - ), + url(r"^accept/(?P[0-9]+)$", views.accept_payment, name="accept_payment"), + url(r"^refuse/$", views.refuse_payment, name="refuse_payment"), + url(r"^ipn/$", views.ipn, name="ipn"), ] diff --git a/cotisations/payment_methods/comnpay/views.py b/cotisations/payment_methods/comnpay/views.py index 12a5747b..5bfa2a82 100644 --- a/cotisations/payment_methods/comnpay/views.py +++ b/cotisations/payment_methods/comnpay/views.py @@ -50,26 +50,24 @@ def accept_payment(request, factureid): if invoice.valid: messages.success( request, - _("The payment of %(amount)s € was accepted.") % { - 'amount': invoice.prix_total() - } + _("The payment of %(amount)s € was accepted.") + % {"amount": invoice.prix_total()}, ) # In case a cotisation was bought, inform the user, the # cotisation time has been extended too - if any(purchase.type_cotisation - for purchase in invoice.vente_set.all()): + if any(purchase.type_cotisation for purchase in invoice.vente_set.all()): messages.success( request, - _("The subscription of %(member_name)s was extended to" - " %(end_date)s.") % { - 'member_name': invoice.user.pseudo, - 'end_date': invoice.user.end_adhesion() - } + _( + "The subscription of %(member_name)s was extended to" + " %(end_date)s." + ) + % { + "member_name": invoice.user.pseudo, + "end_date": invoice.user.end_adhesion(), + }, ) - return redirect(reverse( - 'users:profil', - kwargs={'userid': invoice.user.id} - )) + return redirect(reverse("users:profil", kwargs={"userid": invoice.user.id})) @csrf_exempt @@ -79,14 +77,8 @@ def refuse_payment(request): The view where the user is redirected when a comnpay payment has been refused. """ - messages.error( - request, - _("The payment was refused.") - ) - return redirect(reverse( - 'users:profil', - kwargs={'userid': request.user.id} - )) + messages.error(request, _("The payment was refused.")) + return redirect(reverse("users:profil", kwargs={"userid": request.user.id})) @csrf_exempt @@ -97,27 +89,26 @@ def ipn(request): Comnpay with 400 response if not or with a 200 response if yes. """ p = Transaction() - order = ('idTpe', 'idTransaction', 'montant', 'result', 'sec', ) + 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") - idTransaction = request.POST['idTransaction'] + idTransaction = request.POST["idTransaction"] try: factureid = int(idTransaction) except ValueError: return HttpResponseBadRequest("HTTP/1.1 400 Bad Request") facture = get_object_or_404(Facture, id=factureid) - payment_method = get_object_or_404( - ComnpayPayment, payment=facture.paiement) + payment_method = get_object_or_404(ComnpayPayment, payment=facture.paiement) if not p.validSec(data, payment_method.payment_pass): return HttpResponseBadRequest("HTTP/1.1 400 Bad Request") - result = True if (request.POST['result'] == 'OK') else False - idTpe = request.POST['idTpe'] + result = True if (request.POST["result"] == "OK") else False + idTpe = request.POST["idTpe"] # Checking that the payment is actually for us. if not idTpe == payment_method.payment_credential: @@ -136,4 +127,3 @@ def ipn(request): # Everything worked we send a reponse to Comnpay indicating that # it's ok for them to proceed return HttpResponse("HTTP/1.0 200 OK") - diff --git a/cotisations/payment_methods/forms.py b/cotisations/payment_methods/forms.py index daa65118..a6bac3ed 100644 --- a/cotisations/payment_methods/forms.py +++ b/cotisations/payment_methods/forms.py @@ -24,6 +24,7 @@ from django.utils.translation import ugettext_lazy as _ from . import PAYMENT_METHODS from cotisations.utils import find_payment_method + def payment_method_factory(payment, *args, creation=True, **kwargs): """This function finds the right payment method form for a given payment. @@ -40,12 +41,10 @@ def payment_method_factory(payment, *args, creation=True, **kwargs): Returns: A form or None """ - payment_method = kwargs.pop('instance', find_payment_method(payment)) + payment_method = kwargs.pop("instance", find_payment_method(payment)) if payment_method is not None: - return forms.modelform_factory(type(payment_method), fields='__all__')( - *args, - instance=payment_method, - **kwargs + return forms.modelform_factory(type(payment_method), fields="__all__")( + *args, instance=payment_method, **kwargs ) elif creation: return PaymentMethodForm(*args, **kwargs) @@ -58,23 +57,24 @@ class PaymentMethodForm(forms.Form): payment_method = forms.ChoiceField( label=_("Special payment method"), - help_text=_("Warning: you will not be able to change the payment " - "method later. But you will be allowed to edit the other " - "options." + help_text=_( + "Warning: you will not be able to change the payment" + " method later. But you will be allowed to edit the other" + " options." ), - required=False + required=False, ) def __init__(self, *args, **kwargs): super(PaymentMethodForm, self).__init__(*args, **kwargs) - prefix = kwargs.get('prefix', None) - self.fields['payment_method'].choices = [(i,p.NAME) for (i,p) in enumerate(PAYMENT_METHODS)] - self.fields['payment_method'].choices.insert(0, ('', _('no'))) - self.fields['payment_method'].widget.attrs = { - 'id': 'paymentMethodSelect' - } + prefix = kwargs.get("prefix", None) + self.fields["payment_method"].choices = [ + (i, p.NAME) for (i, p) in enumerate(PAYMENT_METHODS) + ] + self.fields["payment_method"].choices.insert(0, ("", _("No"))) + self.fields["payment_method"].widget.attrs = {"id": "paymentMethodSelect"} self.templates = [ - forms.modelform_factory(p.PaymentMethod, fields='__all__')(prefix=prefix) + forms.modelform_factory(p.PaymentMethod, fields="__all__")(prefix=prefix) for p in PAYMENT_METHODS ] @@ -84,29 +84,29 @@ class PaymentMethodForm(forms.Form): found. Tries to call `payment_method.valid_form` if it exists. """ super(PaymentMethodForm, self).clean() - choice = self.cleaned_data['payment_method'] - if choice=='': + choice = self.cleaned_data["payment_method"] + if choice == "": return choice = int(choice) model = PAYMENT_METHODS[choice].PaymentMethod - form = forms.modelform_factory(model, fields='__all__')(self.data, prefix=self.prefix) + form = forms.modelform_factory(model, fields="__all__")( + self.data, prefix=self.prefix + ) self.payment_method = form.save(commit=False) - if hasattr(self.payment_method, 'valid_form'): + if hasattr(self.payment_method, "valid_form"): self.payment_method.valid_form(self) return self.cleaned_data - - def save(self, payment, *args, **kwargs): """Saves the payment method. Tries to call `payment_method.alter_payment` if it exists. """ - commit = kwargs.pop('commit', True) - if not hasattr(self, 'payment_method'): + commit = kwargs.pop("commit", True) + if not hasattr(self, "payment_method"): return None self.payment_method.payment = payment - if hasattr(self.payment_method, 'alter_payment'): + if hasattr(self.payment_method, "alter_payment"): self.payment_method.alter_payment(payment) if commit: payment.save() diff --git a/cotisations/tests.py b/cotisations/payment_methods/free/__init__.py similarity index 79% rename from cotisations/tests.py rename to cotisations/payment_methods/free/__init__.py index 46b9d708..27041f2e 100644 --- a/cotisations/tests.py +++ b/cotisations/payment_methods/free/__init__.py @@ -1,10 +1,9 @@ +# -*- 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 +# Copyright © 2019 Hugo Levy-Falk # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -19,10 +18,11 @@ # 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.tests -The tests for the Cotisations module. """ +This module contains a method to pay online using user balance. +""" +from . import models -# from django.test import TestCase +NAME = "FREE" -# Create your tests here. +PaymentMethod = models.FreePayment diff --git a/cotisations/payment_methods/free/models.py b/cotisations/payment_methods/free/models.py new file mode 100644 index 00000000..39a3aa80 --- /dev/null +++ b/cotisations/payment_methods/free/models.py @@ -0,0 +1,54 @@ +# -*- mode: python; coding: utf-8 -*- +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2018 Hugo Levy-Falk +# +# 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.db import models +from django.utils.translation import ugettext_lazy as _ +from django.contrib import messages + + +from cotisations.models import Paiement +from cotisations.payment_methods.mixins import PaymentMethodMixin + + +class FreePayment(PaymentMethodMixin, models.Model): + """ + The model allowing you to bypass payment if the invoice is free. + """ + + class Meta: + verbose_name = _("Free payment") + + payment = models.OneToOneField( + Paiement, + on_delete=models.CASCADE, + related_name="payment_method_free", + editable=False, + ) + + def end_payment(self, invoice, request): + """Ends the payment normally. + """ + return invoice.paiement.end_payment(invoice, request, use_payment_method=False) + + def check_price(self, price, user, *args, **kwargs): + """Checks that the price meets the requirement to be paid with user + balance. + """ + return (price == 0, _("You can't pay this invoice for free.")) diff --git a/cotisations/payment_methods/mixins.py b/cotisations/payment_methods/mixins.py index 12503e05..1e808f09 100644 --- a/cotisations/payment_methods/mixins.py +++ b/cotisations/payment_methods/mixins.py @@ -29,5 +29,4 @@ class PaymentMethodMixin: Must return a HttpResponse-like object. """ - return self.payment.end_payment( - invoice, request, use_payment_method=False) + return self.payment.end_payment(invoice, request, use_payment_method=False) diff --git a/cotisations/payment_methods/note_kfet/__init__.py b/cotisations/payment_methods/note_kfet/__init__.py index 1f133d11..99949bbc 100644 --- a/cotisations/payment_methods/note_kfet/__init__.py +++ b/cotisations/payment_methods/note_kfet/__init__.py @@ -22,5 +22,6 @@ This module contains a method to pay online using comnpay. """ from . import models, urls + NAME = "NOTE" PaymentMethod = models.NotePayment diff --git a/cotisations/payment_methods/note_kfet/forms.py b/cotisations/payment_methods/note_kfet/forms.py index e52c275c..7d82b93f 100644 --- a/cotisations/payment_methods/note_kfet/forms.py +++ b/cotisations/payment_methods/note_kfet/forms.py @@ -24,15 +24,11 @@ from django.utils.translation import ugettext_lazy as _ from cotisations.utils import find_payment_method + class NoteCredentialForm(forms.Form): """A special form to get credential to connect to a NoteKfet2015 server throught his API object. """ - login = forms.CharField( - label=_("pseudo note") - ) - password = forms.CharField( - label=_("Password"), - widget=forms.PasswordInput - ) - + + login = forms.CharField(label=_("Username")) + password = forms.CharField(label=_("Password"), widget=forms.PasswordInput) diff --git a/cotisations/payment_methods/note_kfet/models.py b/cotisations/payment_methods/note_kfet/models.py index be54bd54..e83cfb36 100644 --- a/cotisations/payment_methods/note_kfet/models.py +++ b/cotisations/payment_methods/note_kfet/models.py @@ -41,25 +41,17 @@ class NotePayment(PaymentMethodMixin, models.Model): payment = models.OneToOneField( Paiement, - on_delete = models.CASCADE, - related_name = 'payment_method', - editable = False - ) - server = models.CharField( - max_length=255, - verbose_name=_("server") - ) - port = models.PositiveIntegerField( - blank = True, - null = True - ) - id_note = models.PositiveIntegerField( - blank = True, - null = True + on_delete=models.CASCADE, + related_name="payment_method_note", + editable=False, ) + server = models.CharField(max_length=255, verbose_name=_("server")) + port = models.PositiveIntegerField(blank=True, null=True) + id_note = models.PositiveIntegerField(blank=True, null=True) def end_payment(self, invoice, request): - return redirect(reverse( - 'cotisations:note_kfet:note_payment', - kwargs={'factureid': invoice.id} - )) + return redirect( + reverse( + "cotisations:note_kfet:note_payment", kwargs={"factureid": invoice.id} + ) + ) diff --git a/cotisations/payment_methods/note_kfet/note.py b/cotisations/payment_methods/note_kfet/note.py index 8b7614b0..40811913 100755 --- a/cotisations/payment_methods/note_kfet/note.py +++ b/cotisations/payment_methods/note_kfet/note.py @@ -12,13 +12,14 @@ import traceback def get_response(socket): - length_str = b'' + length_str = b"" char = socket.recv(1) - while char != b'\n': + while char != b"\n": length_str += char char = socket.recv(1) total = int(length_str) - return json.loads(socket.recv(total).decode('utf-8')) + return json.loads(socket.recv(total).decode("utf-8")) + def connect(server, port): sock = socket.socket() @@ -35,7 +36,8 @@ def connect(server, port): return (False, sock, "Serveur indisponible") return (True, sock, "") -def login(server, port, username, password, masque = [[], [], True]): + +def login(server, port, username, password, masque=[[], [], True]): result, sock, err = connect(server, port) if not result: return (False, None, err) @@ -43,7 +45,7 @@ def login(server, port, username, password, masque = [[], [], True]): commande = ["login", [username, password, "bdd", masque]] sock.send(json.dumps(commande).encode("utf-8")) response = get_response(sock) - retcode = response['retcode'] + retcode = response["retcode"] if retcode == 0: return (True, sock, "") elif retcode == 5: @@ -60,11 +62,28 @@ def don(sock, montant, id_note, facture): Faire faire un don à l'id_note """ try: - sock.send(json.dumps(["dons", [[id_note], round(montant*100), "Facture : id=%s, designation=%s" % (facture.id, facture.name())]]).encode("utf-8")) + sock.send( + json.dumps( + [ + "dons", + [ + [id_note], + round(montant * 100), + "Facture : id=%s, designation=%s" + % (facture.id, facture.name()), + ], + ] + ).encode("utf-8") + ) response = get_response(sock) - retcode = response['retcode'] + retcode = response["retcode"] transaction_retcode = response["msg"][0][0] - if 0 < retcode < 100 or 200 <= retcode or 0 < transaction_retcode < 100 or 200 <= transaction_retcode: + if ( + 0 < retcode < 100 + or 200 <= retcode + or 0 < transaction_retcode < 100 + or 200 <= transaction_retcode + ): return (False, "Transaction échouée. (Solde trop négatif ?)") elif retcode == 0: return (True, "") diff --git a/cotisations/payment_methods/note_kfet/urls.py b/cotisations/payment_methods/note_kfet/urls.py index 7b939136..89bb3eb9 100644 --- a/cotisations/payment_methods/note_kfet/urls.py +++ b/cotisations/payment_methods/note_kfet/urls.py @@ -23,8 +23,6 @@ from . import views urlpatterns = [ url( - r'^note_payment/(?P[0-9]+)$', - views.note_payment, - name='note_payment' - ), + r"^note_payment/(?P[0-9]+)$", views.note_payment, name="note_payment" + ) ] diff --git a/cotisations/payment_methods/note_kfet/views.py b/cotisations/payment_methods/note_kfet/views.py index cfdda9b0..4069a8f5 100644 --- a/cotisations/payment_methods/note_kfet/views.py +++ b/cotisations/payment_methods/note_kfet/views.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2018 Gabriel Detraz -# Copyright © 2018 Pierre-Antoine Comby +# Copyright © 2018 Pierre-Antoine Comby # # 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 @@ -39,13 +39,11 @@ from cotisations.models import Facture from cotisations.utils import find_payment_method from .models import NotePayment from re2o.views import form -from re2o.acl import ( - can_create, - can_edit -) +from re2o.acl import can_create, can_edit from .note import login, don from .forms import NoteCredentialForm + @login_required @can_edit(Facture) def note_payment(request, facture, factureid): @@ -58,40 +56,38 @@ def note_payment(request, facture, factureid): payment_method = find_payment_method(facture.paiement) if not payment_method or not isinstance(payment_method, NotePayment): messages.error(request, _("Unknown error.")) - return redirect(reverse( - 'users:profil', - kwargs={'userid': user.id} - )) + return redirect(reverse("users:profil", kwargs={"userid": user.id})) noteform = NoteCredentialForm(request.POST or None) if noteform.is_valid(): - pseudo = noteform.cleaned_data['login'] - password = noteform.cleaned_data['password'] - result, sock, err = login(payment_method.server, payment_method.port, pseudo, password) + pseudo = noteform.cleaned_data["login"] + password = noteform.cleaned_data["password"] + result, sock, err = login( + payment_method.server, payment_method.port, pseudo, password + ) if not result: messages.error(request, err) return form( - {'form': noteform, 'amount': facture.prix_total()}, + {"form": noteform, "amount": facture.prix_total()}, "cotisations/payment.html", - request + request, ) else: - result, err = don(sock, facture.prix_total(), payment_method.id_note, facture) + result, err = don( + sock, facture.prix_total(), payment_method.id_note, facture + ) if not result: messages.error(request, err) return form( - {'form': noteform, 'amount': facture.prix_total()}, + {"form": noteform, "amount": facture.prix_total()}, "cotisations/payment.html", - request + request, ) facture.valid = True facture.save() messages.success(request, _("The payment with note was done.")) - return redirect(reverse( - 'users:profil', - kwargs={'userid': user.id} - )) + return redirect(reverse("users:profil", kwargs={"userid": user.id})) return form( - {'form': noteform, 'amount': facture.prix_total()}, - "cotisations/payment.html", - request - ) + {"form": noteform, "amount": facture.prix_total()}, + "cotisations/payment.html", + request, + ) diff --git a/cotisations/payment_methods/urls.py b/cotisations/payment_methods/urls.py index f6bb31dd..adb606bc 100644 --- a/cotisations/payment_methods/urls.py +++ b/cotisations/payment_methods/urls.py @@ -22,7 +22,7 @@ from django.conf.urls import include, url from . import comnpay, cheque, note_kfet urlpatterns = [ - url(r'^comnpay/', include(comnpay.urls, namespace='comnpay')), - url(r'^cheque/', include(cheque.urls, namespace='cheque')), - url(r'^note_kfet/', include(note_kfet.urls, namespace='note_kfet')), + url(r"^comnpay/", include(comnpay.urls, namespace="comnpay")), + url(r"^cheque/", include(cheque.urls, namespace="cheque")), + url(r"^note_kfet/", include(note_kfet.urls, namespace="note_kfet")), ] diff --git a/cotisations/templates/cotisations/aff_article.html b/cotisations/templates/cotisations/aff_article.html index 682d6a05..7ead24dc 100644 --- a/cotisations/templates/cotisations/aff_article.html +++ b/cotisations/templates/cotisations/aff_article.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Price" %} {% trans "Subscription type" %} {% trans "Duration (in months)" %} + {% trans "Duration (in days)" %} {% trans "Concerned users" %} {% trans "Available for everyone" %} @@ -45,6 +46,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {{ article.prix }} {{ article.type_cotisation }} {{ article.duration }} + {{ article.duration_days }} {{ article.type_user }} {{ article.available_for_everyone | tick }} diff --git a/cotisations/templates/cotisations/aff_banque.html b/cotisations/templates/cotisations/aff_banque.html index 057c6995..1bf1fcd2 100644 --- a/cotisations/templates/cotisations/aff_banque.html +++ b/cotisations/templates/cotisations/aff_banque.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/cotisations/templates/cotisations/aff_cotisations.html b/cotisations/templates/cotisations/aff_cotisations.html index e27ae8c7..b9a1a810 100644 --- a/cotisations/templates/cotisations/aff_cotisations.html +++ b/cotisations/templates/cotisations/aff_cotisations.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/cotisations/templates/cotisations/aff_paiement.html b/cotisations/templates/cotisations/aff_paiement.html index afb78b48..6043da67 100644 --- a/cotisations/templates/cotisations/aff_paiement.html +++ b/cotisations/templates/cotisations/aff_paiement.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -31,7 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Payment type" %} - {% trans "Is available for everyone" %} + {% trans "Available for everyone" %} {% trans "Custom payment method" %} diff --git a/cotisations/templates/cotisations/control.html b/cotisations/templates/cotisations/control.html index 5a18bd01..497de6f4 100644 --- a/cotisations/templates/cotisations/control.html +++ b/cotisations/templates/cotisations/control.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -45,12 +45,12 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Profile" %} - {% trans "Last name" as tr_last_name %} - {% include 'buttons/sort.html' with prefix='control' col='name' text=tr_last_name %} + {% trans "First name" as tr_first_name %} + {% include 'buttons/sort.html' with prefix='control' col='name' text=tr_first_name %} - {% trans "First name" as tr_first_name %} - {% include 'buttons/sort.html' with prefix='control' col='surname' text=tr_first_name %} + {% trans "Surname" as tr_surname %} + {% include 'buttons/sort.html' with prefix='control' col='surname' text=tr_surname %} {% trans "Invoice ID" as tr_invoice_id %} @@ -104,8 +104,8 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endfor %} - {% trans "Edit" as tr_edit %} - {% bootstrap_button tr_edit button_type='submit' icon='ok' button_class='btn-success' %} + {% trans "Confirm" as tr_confirm %} + {% bootstrap_button tr_confirm button_type='submit' icon='ok' button_class='btn-success' %} {% endblock %} diff --git a/cotisations/templates/cotisations/delete.html b/cotisations/templates/cotisations/delete.html index e6060d09..e6f1b362 100644 --- a/cotisations/templates/cotisations/delete.html +++ b/cotisations/templates/cotisations/delete.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% csrf_token %}

- {% blocktrans %}Warning: are you sure you really want to delete this {{ object_name }} object ( {{ objet }} )?{% endblocktrans %} + {% blocktrans %}Warning: are you sure you really want to delete this {{ objet_name }} object ( {{ objet }} )?{% endblocktrans %}

{% trans "Confirm" as tr_confirm %} {% bootstrap_button tr_confirm button_type='submit' icon='trash' button_class='btn-danger' %} diff --git a/cotisations/templates/cotisations/edit_facture.html b/cotisations/templates/cotisations/edit_facture.html index 891f7476..99dd2cd8 100644 --- a/cotisations/templates/cotisations/edit_facture.html +++ b/cotisations/templates/cotisations/edit_facture.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/cotisations/templates/cotisations/email_invoice b/cotisations/templates/cotisations/email_invoice index 8d6b2cc2..01f80225 100644 --- a/cotisations/templates/cotisations/email_invoice +++ b/cotisations/templates/cotisations/email_invoice @@ -6,17 +6,17 @@ Nous vous remercions pour votre achat auprès de {{asso_name}} et nous vous en j En cas de question, n’hésitez pas à nous contacter par mail à {{contact_mail}}. -Cordialement, -L’équipe de {{asso_name}} +Respectueusement, +L’équipe de {{asso_name}}. === English version === -Dear {{name}}, +Hello {{name}}, -Thank you for your purchase. Here is your invoice. +Thank you for your purchase at {{asso_name}}. Here is your invoice. -Should you need extra information, you can email us at {{contact_mail}}. +Should you need extra information, do not hesitate to email us at {{contact_mail}}. -Best regards, - {{ asso_name }}'s team +Regards, +The {{ asso_name }} team. diff --git a/cotisations/templates/cotisations/email_subscription_accepted b/cotisations/templates/cotisations/email_subscription_accepted index 58027cec..cb0eb760 100644 --- a/cotisations/templates/cotisations/email_subscription_accepted +++ b/cotisations/templates/cotisations/email_subscription_accepted @@ -1,22 +1,23 @@ Bonjour {{name}} ! -Nous vous informons que votre cotisation auprès de {{asso_name}} a été acceptée. Vous voilà donc membre de l'association. +Nous vous informons que votre cotisation auprès de {{asso_name}} a été acceptée. Vous voilà donc membre de l'association jusqu'au {{ date_end|date:"d/m/Y" }}. Vous trouverez en pièce jointe un reçu. Pour nous faire part de toute remarque, suggestion ou problème vous pouvez nous envoyer un mail à {{asso_email}}. -À bientôt, +Respectueusement, L'équipe de {{asso_name}}. --- -Your subscription to {{asso_name}} has just been accepted. You are now a full member of {{asso_name}}. +Hello {{name}}! + +Your subscription to {{asso_name}} has just been accepted. You are now a full member of {{asso_name}} until {{ date_end|date:"d/m/Y" }}. You will find with this email a subscription voucher. -For any information, suggestion or problem, you can contact us via email at -{{asso_email}}. +To express any comment, suggestion or problem, you can send us an email to {{asso_email}}. Regards, The {{asso_name}} team. diff --git a/cotisations/templates/cotisations/facture.html b/cotisations/templates/cotisations/facture.html index 65b05199..dc9b31f7 100644 --- a/cotisations/templates/cotisations/facture.html +++ b/cotisations/templates/cotisations/facture.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -44,9 +44,6 @@ with this program; if not, write to the Free Software Foundation, Inc., {% blocktrans %}Current balance: {{ balance }} €{% endblocktrans %}

{% endif %} -{% if factureform %} -{% bootstrap_form_errors factureform %} -{% endif %} {% if discount_form %} {% bootstrap_form_errors discount_form %} {% endif %} @@ -90,7 +87,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% if articlesformset %} var prices = {}; {% for article in articlelist %} -prices[{{ article.id|escapejs }}] = {{ article.prix }}; +prices[{{ article.id|escapejs }}] = "{{ article.prix }}"; {% endfor %} var template = `Article :   @@ -124,7 +121,7 @@ function update_price(){ if (article == '') { continue; } - article_price = prices[article]; + article_price = parseFloat(prices[article].replace(',', '.')); quantity = document.getElementById( 'id_form-' + i.toString() + '-quantity').value; price += article_price * quantity; diff --git a/cotisations/templates/cotisations/index.html b/cotisations/templates/cotisations/index.html index 42b8a3bf..ba3a3ea4 100644 --- a/cotisations/templates/cotisations/index.html +++ b/cotisations/templates/cotisations/index.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/cotisations/templates/cotisations/index_article.html b/cotisations/templates/cotisations/index_article.html index 5f93b5ce..1a4c3c8d 100644 --- a/cotisations/templates/cotisations/index_article.html +++ b/cotisations/templates/cotisations/index_article.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -30,14 +30,14 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block title %}{% trans "Articles" %}{% endblock %} {% block content %} -

{% trans "List of article types" %}

+

{% trans "List of articles" %}

{% can_create Article %} - {% trans "Add an article type" %} + {% trans "Add an article" %} {% acl_end %} - {% trans "Delete one or several article types" %} + {% trans "Delete one or several articles" %} {% include 'cotisations/aff_article.html' with article_list=article_list %} {% endblock %} diff --git a/cotisations/templates/cotisations/index_banque.html b/cotisations/templates/cotisations/index_banque.html index 87067222..c653acfd 100644 --- a/cotisations/templates/cotisations/index_banque.html +++ b/cotisations/templates/cotisations/index_banque.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

{% trans "List of banks" %}

{% can_create Banque %} - {% trans "Add a bank" %} + {% trans "Add a bank" %} {% acl_end %} diff --git a/cotisations/templates/cotisations/index_cost_estimate.html b/cotisations/templates/cotisations/index_cost_estimate.html index c3d57197..0d2fad01 100644 --- a/cotisations/templates/cotisations/index_cost_estimate.html +++ b/cotisations/templates/cotisations/index_cost_estimate.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/cotisations/templates/cotisations/index_custom_invoice.html b/cotisations/templates/cotisations/index_custom_invoice.html index 9b539614..cbc502d2 100644 --- a/cotisations/templates/cotisations/index_custom_invoice.html +++ b/cotisations/templates/cotisations/index_custom_invoice.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/cotisations/templates/cotisations/index_paiement.html b/cotisations/templates/cotisations/index_paiement.html index 09b7e033..1411add0 100644 --- a/cotisations/templates/cotisations/index_paiement.html +++ b/cotisations/templates/cotisations/index_paiement.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

{% trans "List of payment methods" %}

{% can_create Paiement %}
- {% trans "Add a payment method" %} + {% trans "Add a payment method" %} {% acl_end %} diff --git a/cotisations/templates/cotisations/payment.html b/cotisations/templates/cotisations/payment.html index ceb8db6f..39dbb195 100644 --- a/cotisations/templates/cotisations/payment.html +++ b/cotisations/templates/cotisations/payment.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/cotisations/templates/cotisations/sidebar.html b/cotisations/templates/cotisations/sidebar.html index 608f95c2..8e69a8c9 100644 --- a/cotisations/templates/cotisations/sidebar.html +++ b/cotisations/templates/cotisations/sidebar.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -52,7 +52,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% acl_end %} {% can_view_all Article %} - {% trans "Available articles" %} + {% trans "Articles" %} {% acl_end %} {% can_view_all Banque %} diff --git a/cotisations/test_models.py b/cotisations/test_models.py new file mode 100644 index 00000000..6e90d882 --- /dev/null +++ b/cotisations/test_models.py @@ -0,0 +1,153 @@ +from django.test import TestCase + +import datetime +from django.utils import timezone +from dateutil.relativedelta import relativedelta + +from users.models import User +from .models import Vente, Facture, Cotisation, Paiement + + +class VenteModelTests(TestCase): + def setUp(self): + self.user = User.objects.create(pseudo="testUserPlop", email="test@example.org") + self.paiement = Paiement.objects.create(moyen="test payment") + self.f = Facture.objects.create( + user=self.user, paiement=self.paiement, valid=True + ) + + def test_one_day_cotisation(self): + """ + It should be possible to have one day membership. + """ + date = timezone.now() + purchase = Vente.objects.create( + facture=self.f, + number=1, + name="Test purchase", + duration=0, + duration_days=1, + type_cotisation="All", + prix=0, + ) + self.f.reorder_purchases() + self.assertAlmostEqual( + self.user.end_connexion() - date, + datetime.timedelta(days=1), + delta=datetime.timedelta(seconds=1), + ) + + def test_one_month_cotisation(self): + """ + It should be possible to have one day membership. + """ + date = timezone.now() + Vente.objects.create( + facture=self.f, + number=1, + name="Test purchase", + duration=1, + duration_days=0, + type_cotisation="All", + prix=0, + ) + self.f.reorder_purchases() + end = self.user.end_connexion() + expected_end = date + relativedelta(months=1) + self.assertEqual(end.day, expected_end.day) + self.assertEqual(end.month, expected_end.month) + self.assertEqual(end.year, expected_end.year) + + def test_one_month_and_one_week_cotisation(self): + """ + It should be possible to have one day membership. + """ + date = timezone.now() + Vente.objects.create( + facture=self.f, + number=1, + name="Test purchase", + duration=1, + duration_days=7, + type_cotisation="All", + prix=0, + ) + self.f.reorder_purchases() + end = self.user.end_connexion() + expected_end = date + relativedelta(months=1, days=7) + self.assertEqual(end.day, expected_end.day) + self.assertEqual(end.month, expected_end.month) + self.assertEqual(end.year, expected_end.year) + + def test_date_start_cotisation(self): + """ + It should be possible to add a cotisation with a specific start date + """ + v = Vente( + facture=self.f, + number=1, + name="Test purchase", + duration=0, + duration_days=1, + type_cotisation = 'All', + prix=0 + ) + v.create_cotis(date_start=timezone.make_aware(datetime.datetime(1998, 10, 16))) + v.save() + self.assertEqual(v.cotisation.date_end, timezone.make_aware(datetime.datetime(1998, 10, 17))) + + + def tearDown(self): + self.f.delete() + self.user.delete() + self.paiement.delete() + + +class FactureModelTests(TestCase): + def setUp(self): + self.user = User.objects.create(pseudo="testUserPlop", email="test@example.org") + self.paiement = Paiement.objects.create(moyen="test payment") + def tearDown(self): + self.user.delete() + self.paiement.delete() + def test_cotisations_prolongation(self): + """When user already have one valid cotisation, the new one should be + added at the end of the existing one.""" + date = timezone.now() + invoice1 = Facture.objects.create( + user=self.user, paiement=self.paiement, valid=True + ) + Vente.objects.create( + facture=invoice1, + number=1, + name="Test purchase", + duration=1, + duration_days=0, + type_cotisation="All", + prix=0, + ) + invoice1.reorder_purchases() + invoice2 = Facture.objects.create( + user=self.user, paiement=self.paiement, valid=True + ) + Vente.objects.create( + facture=invoice2, + number=1, + name="Test purchase", + duration=1, + duration_days=0, + type_cotisation="All", + prix=0, + ) + invoice1.reorder_purchases() + delta = relativedelta(self.user.end_connexion(), date) + delta.microseconds = 0 + try: + self.assertEqual(delta, relativedelta(months=2)) + except Exception as e: + invoice1.delete() + invoice2.delete() + raise e + invoice1.delete() + invoice2.delete() + diff --git a/cotisations/test_views.py b/cotisations/test_views.py new file mode 100644 index 00000000..f0c739b0 --- /dev/null +++ b/cotisations/test_views.py @@ -0,0 +1,145 @@ +from django.test import TestCase +from django.urls import reverse +from django.contrib.auth.models import Permission + +import datetime +from dateutil.relativedelta import relativedelta +from django.utils import timezone + +from users.models import Adherent +from .models import Vente, Facture, Cotisation, Paiement, Article + + +class NewFactureTests(TestCase): + def tearDown(self): + self.user.facture_set.all().delete() + self.user.delete() + self.paiement.delete() + self.article_one_day.delete() + self.article_one_month.delete() + self.article_one_month_and_one_week.delete() + + def setUp(self): + self.user = Adherent.objects.create(pseudo="testUser", email="test@example.org") + self.user.set_password("plopiplop") + self.user.user_permissions.set( + [ + Permission.objects.get_by_natural_key( + "add_facture", "cotisations", "Facture" + ), + Permission.objects.get_by_natural_key( + "use_every_payment", "cotisations", "Paiement" + ), + ] + ) + self.user.save() + + self.paiement = Paiement.objects.create(moyen="test payment") + self.article_one_day = Article.objects.create( + name="One day", + prix=0, + duration=0, + duration_days=1, + type_cotisation="All", + available_for_everyone=True, + ) + self.article_one_month = Article.objects.create( + name="One day", + prix=0, + duration=1, + duration_days=0, + type_cotisation="All", + available_for_everyone=True, + ) + self.article_one_month_and_one_week = Article.objects.create( + name="One day", + prix=0, + duration=1, + duration_days=7, + type_cotisation="All", + available_for_everyone=True, + ) + self.client.login(username="testUser", password="plopiplop") + + def test_invoice_with_one_day(self): + data = { + "Facture-paiement": self.paiement.pk, + "form-TOTAL_FORMS": 1, + "form-INITIAL_FORMS": 0, + "form-MIN_NUM_FORMS": 0, + "form-MAX_NUM_FORMS": 1000, + "form-0-article": 1, + "form-0-quantity": 1, + } + date = timezone.now() + response = self.client.post( + reverse("cotisations:new-facture", kwargs={"userid": self.user.pk}), data + ) + self.assertEqual(response.status_code, 302) + self.assertEqual(response.url, "/users/profil/%d" % self.user.pk) + self.assertAlmostEqual( + self.user.end_connexion() - date, + datetime.timedelta(days=1), + delta=datetime.timedelta(seconds=1), + ) + + def test_invoice_with_one_month(self): + data = { + "Facture-paiement": self.paiement.pk, + "form-TOTAL_FORMS": 1, + "form-INITIAL_FORMS": 0, + "form-MIN_NUM_FORMS": 0, + "form-MAX_NUM_FORMS": 1000, + "form-0-article": 2, + "form-0-quantity": 1, + } + date = timezone.now() + response = self.client.post( + reverse("cotisations:new-facture", kwargs={"userid": self.user.pk}), data + ) + self.assertEqual(response.status_code, 302) + self.assertEqual(response.url, "/users/profil/%d" % self.user.pk) + delta = relativedelta(self.user.end_connexion(), date) + delta.microseconds = 0 + self.assertEqual(delta, relativedelta(months=1)) + + def test_invoice_with_one_month_and_one_week(self): + data = { + "Facture-paiement": self.paiement.pk, + "form-TOTAL_FORMS": 2, + "form-INITIAL_FORMS": 0, + "form-MIN_NUM_FORMS": 0, + "form-MAX_NUM_FORMS": 1000, + "form-0-article": 1, + "form-0-quantity": 7, + "form-1-article": 2, + "form-1-quantity": 1, + } + date = timezone.now() + response = self.client.post( + reverse("cotisations:new-facture", kwargs={"userid": self.user.pk}), data + ) + self.assertEqual(response.status_code, 302) + self.assertEqual(response.url, "/users/profil/%d" % self.user.pk) + invoice = self.user.facture_set.first() + delta = relativedelta(self.user.end_connexion(), date) + delta.microseconds = 0 + self.assertEqual(delta, relativedelta(months=1, days=7)) + + def test_several_articles_creates_several_purchases(self): + data = { + "Facture-paiement": self.paiement.pk, + "form-TOTAL_FORMS": 2, + "form-INITIAL_FORMS": 0, + "form-MIN_NUM_FORMS": 0, + "form-MAX_NUM_FORMS": 1000, + "form-0-article": 2, + "form-0-quantity": 1, + "form-1-article": 2, + "form-1-quantity": 1, + } + response = self.client.post( + reverse("cotisations:new-facture", kwargs={"userid": self.user.pk}), data + ) + f = self.user.facture_set.first() + self.assertEqual(f.vente_set.count(), 2) diff --git a/cotisations/tex.py b/cotisations/tex.py index 1ab964ba..2930fffe 100644 --- a/cotisations/tex.py +++ b/cotisations/tex.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -33,19 +33,17 @@ from datetime import datetime from django.db import models from django.template.loader import get_template -from django.template import Context from django.http import HttpResponse from django.conf import settings from django.utils.text import slugify -from django.utils.translation import ugettext_lazy as _ from re2o.mixins import AclMixin, RevMixin from preferences.models import CotisationsOption -TEMP_PREFIX = getattr(settings, 'TEX_TEMP_PREFIX', 'render_tex-') -CACHE_PREFIX = getattr(settings, 'TEX_CACHE_PREFIX', 'render-tex') -CACHE_TIMEOUT = getattr(settings, 'TEX_CACHE_TIMEOUT', 86400) # 1 day +TEMP_PREFIX = getattr(settings, "TEX_TEMP_PREFIX", "render_tex-") +CACHE_PREFIX = getattr(settings, "TEX_CACHE_PREFIX", "render-tex") +CACHE_TIMEOUT = getattr(settings, "TEX_CACHE_TIMEOUT", 86400) # 1 day def render_invoice(_request, ctx={}): @@ -54,20 +52,20 @@ def render_invoice(_request, ctx={}): date, the user, the articles, the prices, ... """ options, _ = CotisationsOption.objects.get_or_create() - is_estimate = ctx.get('is_estimate', False) - filename = '_'.join([ - 'cost_estimate' if is_estimate else 'invoice', - slugify(ctx.get('asso_name', "")), - slugify(ctx.get('recipient_name', "")), - str(ctx.get('DATE', datetime.now()).year), - str(ctx.get('DATE', datetime.now()).month), - str(ctx.get('DATE', datetime.now()).day), - ]) - templatename = options.invoice_template.template.name.split('/')[-1] - r = render_tex(_request, templatename, ctx) - r['Content-Disposition'] = 'attachment; filename="{name}.pdf"'.format( - name=filename + is_estimate = ctx.get("is_estimate", False) + filename = "_".join( + [ + "cost_estimate" if is_estimate else "invoice", + slugify(ctx.get("asso_name", "")), + slugify(ctx.get("recipient_name", "")), + str(ctx.get("DATE", datetime.now()).year), + str(ctx.get("DATE", datetime.now()).month), + str(ctx.get("DATE", datetime.now()).day), + ] ) + templatename = options.invoice_template.template.name.split("/")[-1] + r = render_tex(_request, templatename, ctx) + r["Content-Disposition"] = 'attachment; filename="{name}.pdf"'.format(name=filename) return r @@ -76,20 +74,20 @@ def render_voucher(_request, ctx={}): Render a subscribtion voucher. """ options, _ = CotisationsOption.objects.get_or_create() - filename = '_'.join([ - 'voucher', - slugify(ctx.get('asso_name', "")), - slugify(ctx.get('firstname', "")), - slugify(ctx.get('lastname', "")), - str(ctx.get('date_begin', datetime.now()).year), - str(ctx.get('date_begin', datetime.now()).month), - str(ctx.get('date_begin', datetime.now()).day), - ]) - templatename = options.voucher_template.template.name.split('/')[-1] - r = render_tex(_request, templatename, ctx) - r['Content-Disposition'] = 'attachment; filename="{name}.pdf"'.format( - name=filename + filename = "_".join( + [ + "voucher", + slugify(ctx.get("asso_name", "")), + slugify(ctx.get("firstname", "")), + slugify(ctx.get("lastname", "")), + str(ctx.get("date_begin", datetime.now()).year), + str(ctx.get("date_begin", datetime.now()).month), + str(ctx.get("date_begin", datetime.now()).day), + ] ) + templatename = options.voucher_template.template.name.split("/")[-1] + r = render_tex(_request, templatename, ctx) + r["Content-Disposition"] = 'attachment; filename="{name}.pdf"'.format(name=filename) return r @@ -105,20 +103,19 @@ def create_pdf(template, ctx={}): Returns: The content of the temporary PDF file generated. """ - context = Context(ctx) + context = ctx template = get_template(template) - rendered_tpl = template.render(context).encode('utf-8') + rendered_tpl = template.render(context).encode("utf-8") with tempfile.TemporaryDirectory() as tempdir: for _ in range(2): - with open("/var/www/re2o/out.log", "w") as f: - process = Popen( - ['pdflatex', '-output-directory', tempdir], - stdin=PIPE, - stdout=f,#PIPE, - ) - process.communicate(rendered_tpl) - with open(os.path.join(tempdir, 'texput.pdf'), 'rb') as f: + process = Popen( + ["pdflatex", "-output-directory", tempdir], + stdin=PIPE, + stdout=PIPE, + ) + process.communicate(rendered_tpl) + with open(os.path.join(tempdir, "texput.pdf"), "rb") as f: pdf = f.read() return pdf @@ -128,10 +125,7 @@ def escape_chars(string): """Escape the '%' and the '€' signs to avoid messing with LaTeX""" if not isinstance(string, str): return string - mapping = ( - ('€', r'\euro'), - ('%', r'\%'), - ) + mapping = (("€", r"\euro"), ("%", r"\%")) r = str(string) for k, v in mapping: r = r.replace(k, v) @@ -153,6 +147,6 @@ def render_tex(_request, template, ctx={}): An HttpResponse with type `application/pdf` containing the PDF file. """ pdf = create_pdf(template, ctx) - r = HttpResponse(content_type='application/pdf') + r = HttpResponse(content_type="application/pdf") r.write(pdf) return r diff --git a/cotisations/urls.py b/cotisations/urls.py index 0c7256f7..e0a3aa16 100644 --- a/cotisations/urls.py +++ b/cotisations/urls.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -31,155 +31,77 @@ from . import views from . import payment_methods urlpatterns = [ + url(r"^new_facture/(?P[0-9]+)$", views.new_facture, name="new-facture"), url( - r'^new_facture/(?P[0-9]+)$', - views.new_facture, - name='new-facture' + r"^edit_facture/(?P[0-9]+)$", views.edit_facture, name="edit-facture" + ), + url(r"^del_facture/(?P[0-9]+)$", views.del_facture, name="del-facture"), + url(r"^facture_pdf/(?P[0-9]+)$", views.facture_pdf, name="facture-pdf"), + url(r"^voucher_pdf/(?P[0-9]+)$", views.voucher_pdf, name="voucher-pdf"), + url(r"^new_cost_estimate/$", views.new_cost_estimate, name="new-cost-estimate"), + url( + r"^index_cost_estimate/$", views.index_cost_estimate, name="index-cost-estimate" ), url( - r'^edit_facture/(?P[0-9]+)$', - views.edit_facture, - name='edit-facture' - ), - url( - r'^del_facture/(?P[0-9]+)$', - views.del_facture, - name='del-facture' - ), - url( - r'^facture_pdf/(?P[0-9]+)$', - views.facture_pdf, - name='facture-pdf' - ), - url( - r'^voucher_pdf/(?P[0-9]+)$', - views.voucher_pdf, - name='voucher-pdf' - ), - url( - r'^new_cost_estimate/$', - views.new_cost_estimate, - name='new-cost-estimate' - ), - url( - r'^index_cost_estimate/$', - views.index_cost_estimate, - name='index-cost-estimate' - ), - url( - r'^cost_estimate_pdf/(?P[0-9]+)$', + r"^cost_estimate_pdf/(?P[0-9]+)$", views.cost_estimate_pdf, - name='cost-estimate-pdf', + name="cost-estimate-pdf", ), url( - r'^index_custom_invoice/$', + r"^index_custom_invoice/$", views.index_custom_invoice, - name='index-custom-invoice' + name="index-custom-invoice", ), url( - r'^edit_cost_estimate/(?P[0-9]+)$', + r"^edit_cost_estimate/(?P[0-9]+)$", views.edit_cost_estimate, - name='edit-cost-estimate' + name="edit-cost-estimate", ), url( - r'^cost_estimate_to_invoice/(?P[0-9]+)$', + r"^cost_estimate_to_invoice/(?P[0-9]+)$", views.cost_estimate_to_invoice, - name='cost-estimate-to-invoice' + name="cost-estimate-to-invoice", ), url( - r'^del_cost_estimate/(?P[0-9]+)$', + r"^del_cost_estimate/(?P[0-9]+)$", views.del_cost_estimate, - name='del-cost-estimate' + name="del-cost-estimate", ), + url(r"^new_custom_invoice/$", views.new_custom_invoice, name="new-custom-invoice"), url( - r'^new_custom_invoice/$', - views.new_custom_invoice, - name='new-custom-invoice' - ), - url( - r'^edit_custom_invoice/(?P[0-9]+)$', + r"^edit_custom_invoice/(?P[0-9]+)$", views.edit_custom_invoice, - name='edit-custom-invoice' + name="edit-custom-invoice", ), url( - r'^custom_invoice_pdf/(?P[0-9]+)$', + r"^custom_invoice_pdf/(?P[0-9]+)$", views.custom_invoice_pdf, - name='custom-invoice-pdf', + name="custom-invoice-pdf", ), url( - r'^del_custom_invoice/(?P[0-9]+)$', + r"^del_custom_invoice/(?P[0-9]+)$", views.del_custom_invoice, - name='del-custom-invoice' + name="del-custom-invoice", ), + url(r"^credit_solde/(?P[0-9]+)$", views.credit_solde, name="credit-solde"), + url(r"^add_article/$", views.add_article, name="add-article"), url( - r'^credit_solde/(?P[0-9]+)$', - views.credit_solde, - name='credit-solde' + r"^edit_article/(?P[0-9]+)$", views.edit_article, name="edit-article" ), + url(r"^del_article/$", views.del_article, name="del-article"), + url(r"^add_paiement/$", views.add_paiement, name="add-paiement"), url( - r'^add_article/$', - views.add_article, - name='add-article' - ), - url( - r'^edit_article/(?P[0-9]+)$', - views.edit_article, - name='edit-article' - ), - url( - r'^del_article/$', - views.del_article, - name='del-article' - ), - url( - r'^add_paiement/$', - views.add_paiement, - name='add-paiement' - ), - url( - r'^edit_paiement/(?P[0-9]+)$', + r"^edit_paiement/(?P[0-9]+)$", views.edit_paiement, - name='edit-paiement' + name="edit-paiement", ), - url( - r'^del_paiement/$', - views.del_paiement, - name='del-paiement' - ), - url( - r'^add_banque/$', - views.add_banque, - name='add-banque' - ), - url( - r'^edit_banque/(?P[0-9]+)$', - views.edit_banque, - name='edit-banque' - ), - url( - r'^del_banque/$', - views.del_banque, - name='del-banque' - ), - url( - r'^index_article/$', - views.index_article, - name='index-article' - ), - url( - r'^index_banque/$', - views.index_banque, - name='index-banque' - ), - url( - r'^index_paiement/$', - views.index_paiement, - name='index-paiement' - ), - url( - r'^control/$', - views.control, - name='control' - ), - url(r'^$', views.index, name='index'), + url(r"^del_paiement/$", views.del_paiement, name="del-paiement"), + url(r"^add_banque/$", views.add_banque, name="add-banque"), + url(r"^edit_banque/(?P[0-9]+)$", views.edit_banque, name="edit-banque"), + url(r"^del_banque/$", views.del_banque, name="del-banque"), + url(r"^index_article/$", views.index_article, name="index-article"), + url(r"^index_banque/$", views.index_banque, name="index-banque"), + url(r"^index_paiement/$", views.index_paiement, name="index-paiement"), + url(r"^control/$", views.control, name="control"), + url(r"^$", views.index, name="index"), ] + payment_methods.urls.urlpatterns diff --git a/cotisations/utils.py b/cotisations/utils.py index 4715338b..facc1197 100644 --- a/cotisations/utils.py +++ b/cotisations/utils.py @@ -25,7 +25,7 @@ from django.template.loader import get_template from django.core.mail import EmailMessage from .tex import create_pdf -from preferences.models import AssoOption, GeneralOption, CotisationsOption +from preferences.models import AssoOption, GeneralOption, CotisationsOption, Mandate from re2o.settings import LOGO_PATH from re2o import settings @@ -33,6 +33,7 @@ from re2o import settings def find_payment_method(payment): """Finds the payment method associated to the payment if it exists.""" from cotisations.payment_methods import PAYMENT_METHODS + for method in PAYMENT_METHODS: try: o = method.PaymentMethod.objects.get(payment=payment) @@ -46,85 +47,82 @@ def send_mail_invoice(invoice): """Creates the pdf of the invoice and sends it by email to the client""" purchases_info = [] for purchase in invoice.vente_set.all(): - purchases_info.append({ - 'name': purchase.name, - 'price': purchase.prix, - 'quantity': purchase.number, - 'total_price': purchase.prix_total - }) + purchases_info.append( + { + "name": purchase.name, + "price": purchase.prix, + "quantity": purchase.number, + "total_price": purchase.prix_total, + } + ) ctx = { - 'paid': True, - 'fid': invoice.id, - 'DATE': invoice.date, - 'recipient_name': "{} {}".format( - invoice.user.name, - invoice.user.surname - ), - 'address': invoice.user.room, - 'article': purchases_info, - 'total': invoice.prix_total(), - '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) + "paid": True, + "fid": invoice.id, + "DATE": invoice.date, + "recipient_name": "{} {}".format(invoice.user.name, invoice.user.surname), + "address": invoice.user.room, + "article": purchases_info, + "total": invoice.prix_total(), + "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), } - pdf = create_pdf('cotisations/factures.tex', ctx) - template = get_template('cotisations/email_invoice') + pdf = create_pdf("cotisations/factures.tex", ctx) + template = get_template("cotisations/email_invoice") ctx = { - 'name': "{} {}".format( - invoice.user.name, - invoice.user.surname - ), - 'contact_mail': AssoOption.get_cached_value('contact'), - 'asso_name': AssoOption.get_cached_value('name') + "name": "{} {}".format(invoice.user.name, invoice.user.surname), + "contact_mail": AssoOption.get_cached_value("contact"), + "asso_name": AssoOption.get_cached_value("name"), } mail = EmailMessage( - 'Votre facture / Your invoice', + "Votre facture / Your invoice", template.render(ctx), - GeneralOption.get_cached_value('email_from'), + GeneralOption.get_cached_value("email_from"), [invoice.user.get_mail], - attachments=[('invoice.pdf', pdf, 'application/pdf')] + attachments=[("invoice.pdf", pdf, "application/pdf")], ) mail.send() def send_mail_voucher(invoice): """Creates a voucher from an invoice and sends it by email to the client""" + president = Mandate.get_mandate(invoice.date).president ctx = { - 'asso_name': AssoOption.get_cached_value('name'), - 'pres_name': AssoOption.get_cached_value('pres_name'), - 'firstname': invoice.user.name, - 'lastname': invoice.user.surname, - 'email': invoice.user.email, - 'phone': invoice.user.telephone, - 'date_end': invoice.get_subscription().latest('date_end').date_end, - 'date_begin': invoice.get_subscription().earliest('date_start').date_start + "asso_name": AssoOption.get_cached_value("name"), + "pres_name": " ".join([president.name, president.surname]), + "firstname": invoice.user.name, + "lastname": invoice.user.surname, + "email": invoice.user.email, + "phone": invoice.user.telephone, + "date_end": invoice.get_subscription().latest("date_end").date_end, + "date_begin": invoice.get_subscription().earliest("date_start").date_start, } - templatename = CotisationsOption.get_cached_value('voucher_template').template.name.split('/')[-1] + templatename = CotisationsOption.get_cached_value( + "voucher_template" + ).template.name.split("/")[-1] pdf = create_pdf(templatename, ctx) - template = get_template('cotisations/email_subscription_accepted') + template = get_template("cotisations/email_subscription_accepted") ctx = { - 'name': "{} {}".format( - invoice.user.name, - invoice.user.surname - ), - 'asso_email': AssoOption.get_cached_value('contact'), - 'asso_name': AssoOption.get_cached_value('name') + "name": "{} {}".format(invoice.user.name, invoice.user.surname), + "asso_email": AssoOption.get_cached_value("contact"), + "asso_name": AssoOption.get_cached_value("name"), + "date_end": invoice.get_subscription().latest("date_end").date_end, } mail = EmailMessage( - 'Votre reçu / Your voucher', + "Votre reçu / Your voucher", template.render(ctx), - GeneralOption.get_cached_value('email_from'), + GeneralOption.get_cached_value("email_from"), [invoice.user.get_mail], - attachments=[('voucher.pdf', pdf, 'application/pdf')] + attachments=[("voucher.pdf", pdf, "application/pdf")], ) mail.send() diff --git a/cotisations/validators.py b/cotisations/validators.py index b1683e82..91a3b627 100644 --- a/cotisations/validators.py +++ b/cotisations/validators.py @@ -12,11 +12,9 @@ def check_no_balance(is_balance): ValidationError: if such a Paiement exists. """ from .models import Paiement + if not is_balance: return p = Paiement.objects.filter(is_balance=True) if len(p) > 0: - raise ValidationError( - _("There is already a payment method for user balance.") - ) - + raise ValidationError(_("There is already a payment method for user balance.")) diff --git a/cotisations/views.py b/cotisations/views.py index b6d5a8a6..cc80e22d 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # Copyright © 2018 Hugo Levy-Falk # @@ -22,7 +22,7 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # App de gestion des users pour re2o -# Goulven Kermarec, Gabriel Détraz +# Lara Kermarec, Gabriel Détraz # Gplv2 """cotisations.views The different views used in the Cotisations module @@ -47,10 +47,7 @@ from users.models import User from re2o.settings import LOGO_PATH from re2o import settings from re2o.views import form -from re2o.base import ( - SortTable, - re2o_paginator, -) +from re2o.base import SortTable, re2o_paginator from re2o.acl import ( can_create, can_edit, @@ -60,7 +57,7 @@ from re2o.acl import ( can_delete_set, can_change, ) -from preferences.models import AssoOption, GeneralOption +from preferences.models import AssoOption, GeneralOption, Mandate from .models import ( Facture, Article, @@ -105,64 +102,59 @@ def new_facture(request, user, userid): invoice = Facture(user=user) # The template needs the list of articles (for the JS part) article_list = Article.objects.filter( - Q(type_user='All') | Q(type_user=request.user.class_name) + Q(type_user="All") | Q(type_user=user.class_type) ) # Building the invoice form and the article formset invoice_form = FactureForm( - request.POST or None, - instance=invoice, - user=request.user, - creation=True + request.POST or None, instance=invoice, user=request.user, creation=True ) article_formset = formset_factory(SelectArticleForm)( - request.POST or None, - form_kwargs={'user': request.user, 'target_user': user} + request.POST or None, form_kwargs={"user": request.user, "target_user": user} ) if invoice_form.is_valid() and article_formset.is_valid(): new_invoice_instance = invoice_form.save(commit=False) articles = article_formset - # Check if at leat one article has been selected + # Check if at least one article has been selected if any(art.cleaned_data for art in articles): # Building a purchase for each article sold purchases = [] total_price = 0 for art_item in articles: if art_item.cleaned_data: - article = art_item.cleaned_data['article'] - quantity = art_item.cleaned_data['quantity'] - total_price += article.prix*quantity + article = art_item.cleaned_data["article"] + quantity = art_item.cleaned_data["quantity"] + total_price += article.prix * quantity new_purchase = Vente( facture=new_invoice_instance, name=article.name, prix=article.prix, type_cotisation=article.type_cotisation, duration=article.duration, - number=quantity + duration_days=article.duration_days, + number=quantity, ) purchases.append(new_purchase) p = find_payment_method(new_invoice_instance.paiement) - if hasattr(p, 'check_price'): + if hasattr(p, "check_price"): price_ok, msg = p.check_price(total_price, user) invoice_form.add_error(None, msg) else: price_ok = True if price_ok: new_invoice_instance.save() + # Saving purchases so the invoice can find them. Invoice + # will modify them after being validated to put the right dates. for p in purchases: p.facture = new_invoice_instance p.save() return new_invoice_instance.paiement.end_payment( - new_invoice_instance, - request + new_invoice_instance, request ) else: - messages.error( - request, - _("You need to choose at least one article.") - ) + messages.error(request, _("You need to choose at least one article.")) p = Paiement.objects.filter(is_balance=True) if len(p) and p[0].can_use_payment(request.user): balance = user.solde @@ -171,13 +163,15 @@ def new_facture(request, user, userid): return form( { - 'factureform': invoice_form, - 'articlesformset': article_formset, - 'articlelist': article_list, - 'balance': balance, - 'action_name': _('Confirm'), + "factureform": invoice_form, + "articlesformset": article_formset, + "articlelist": article_list, + "balance": balance, + "action_name": _("Confirm"), + "title": _("New invoice"), }, - 'cotisations/facture.html', request + "cotisations/facture.html", + request, ) @@ -190,48 +184,50 @@ def new_cost_estimate(request): point of view. """ # The template needs the list of articles (for the JS part) - articles = Article.objects.filter( - Q(type_user='All') | Q(type_user=request.user.class_name) - ) + articles = Article.objects.all() # Building the invocie form and the article formset cost_estimate_form = CostEstimateForm(request.POST or None) articles_formset = formset_factory(SelectArticleForm)( - request.POST or None, - form_kwargs={'user': request.user} + request.POST or None, form_kwargs={"user": request.user} ) discount_form = DiscountForm(request.POST or None) - if cost_estimate_form.is_valid() and articles_formset.is_valid() and discount_form.is_valid(): + if ( + cost_estimate_form.is_valid() + and articles_formset.is_valid() + and discount_form.is_valid() + ): cost_estimate_instance = cost_estimate_form.save() for art_item in articles_formset: if art_item.cleaned_data: - article = art_item.cleaned_data['article'] - quantity = art_item.cleaned_data['quantity'] + article = art_item.cleaned_data["article"] + quantity = art_item.cleaned_data["quantity"] Vente.objects.create( facture=cost_estimate_instance, name=article.name, prix=article.prix, type_cotisation=article.type_cotisation, duration=article.duration, - number=quantity + number=quantity, ) discount_form.apply_to_invoice(cost_estimate_instance) - messages.success( - request, - _("The cost estimate was created.") - ) - return redirect(reverse('cotisations:index-cost-estimate')) + messages.success(request, _("The cost estimate was created.")) + return redirect(reverse("cotisations:index-cost-estimate")) - return form({ - 'factureform': cost_estimate_form, - 'action_name': _("Confirm"), - 'articlesformset': articles_formset, - 'articlelist': articles, - 'discount_form': discount_form, - 'title': _("Cost estimate"), - }, 'cotisations/facture.html', request) + return form( + { + "factureform": cost_estimate_form, + "action_name": _("Confirm"), + "articlesformset": articles_formset, + "articlelist": articles, + "discount_form": discount_form, + "title": _("New cost estimate"), + }, + "cotisations/facture.html", + request, + ) @login_required @@ -243,46 +239,49 @@ def new_custom_invoice(request): point of view. """ # The template needs the list of articles (for the JS part) - articles = Article.objects.filter( - Q(type_user='All') | Q(type_user=request.user.class_name) - ) + articles = Article.objects.all() # Building the invocie form and the article formset invoice_form = CustomInvoiceForm(request.POST or None) articles_formset = formset_factory(SelectArticleForm)( - request.POST or None, - form_kwargs={'user': request.user} + request.POST or None, form_kwargs={"user": request.user} ) discount_form = DiscountForm(request.POST or None) - if invoice_form.is_valid() and articles_formset.is_valid() and discount_form.is_valid(): + if ( + invoice_form.is_valid() + and articles_formset.is_valid() + and discount_form.is_valid() + ): new_invoice_instance = invoice_form.save() for art_item in articles_formset: if art_item.cleaned_data: - article = art_item.cleaned_data['article'] - quantity = art_item.cleaned_data['quantity'] + article = art_item.cleaned_data["article"] + quantity = art_item.cleaned_data["quantity"] Vente.objects.create( facture=new_invoice_instance, name=article.name, prix=article.prix, type_cotisation=article.type_cotisation, duration=article.duration, - number=quantity + number=quantity, ) discount_form.apply_to_invoice(new_invoice_instance) - messages.success( - request, - _("The custom invoice was created.") - ) - return redirect(reverse('cotisations:index-custom-invoice')) + messages.success(request, _("The custom invoice was created.")) + return redirect(reverse("cotisations:index-custom-invoice")) - return form({ - 'factureform': invoice_form, - 'action_name': _("Confirm"), - 'articlesformset': articles_formset, - 'articlelist': articles, - 'discount_form': discount_form - }, 'cotisations/facture.html', request) + return form( + { + "factureform": invoice_form, + "action_name": _("Confirm"), + "articlesformset": articles_formset, + "articlelist": articles, + "discount_form": discount_form, + "title": _("New custom invoice"), + }, + "cotisations/facture.html", + request, + ) # TODO : change facture to invoice @@ -301,32 +300,34 @@ def facture_pdf(request, facture, **_kwargs): # contiaining (article_name, article_price, quantity, total_price) purchases_info = [] for purchase in purchases_objects: - purchases_info.append({ - 'name': purchase.name, - 'price': purchase.prix, - 'quantity': purchase.number, - 'total_price': purchase.prix_total - }) - return render_invoice(request, { - 'paid': True, - 'fid': facture.id, - 'DATE': facture.date, - 'recipient_name': "{} {}".format( - facture.user.name, - facture.user.surname - ), - 'address': facture.user.room, - 'article': purchases_info, - 'total': facture.prix_total(), - '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), - 'payment_method': facture.paiement.moyen, - }) + purchases_info.append( + { + "name": purchase.name, + "price": purchase.prix, + "quantity": purchase.number, + "total_price": purchase.prix_total, + } + ) + return render_invoice( + request, + { + "paid": True, + "fid": facture.id, + "DATE": facture.date, + "recipient_name": "{} {}".format(facture.user.name, facture.user.surname), + "address": facture.user.room, + "article": purchases_info, + "total": facture.prix_total(), + "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), + "payment_method": facture.paiement.moyen, + }, + ) # TODO : change facture to invoice @@ -340,34 +341,24 @@ def edit_facture(request, facture, **_kwargs): an invoice. """ invoice_form = FactureForm( - request.POST or None, - instance=facture, - user=request.user + request.POST or None, instance=facture, user=request.user ) purchases_objects = Vente.objects.filter(facture=facture) purchase_form_set = modelformset_factory( - Vente, - fields=('name', 'number'), - extra=0, - max_num=len(purchases_objects) - ) - purchase_form = purchase_form_set( - request.POST or None, - queryset=purchases_objects + Vente, fields=("name", "number"), extra=0, max_num=len(purchases_objects) ) + purchase_form = purchase_form_set(request.POST or None, queryset=purchases_objects) if invoice_form.is_valid() and purchase_form.is_valid(): if invoice_form.changed_data: invoice_form.save() purchase_form.save() - messages.success( - request, - _("The invoice was edited.") - ) - return redirect(reverse('cotisations:index')) - return form({ - 'factureform': invoice_form, - 'venteform': purchase_form - }, 'cotisations/edit_facture.html', request) + messages.success(request, _("The invoice was edited.")) + return redirect(reverse("cotisations:index")) + return form( + {"factureform": invoice_form, "venteform": purchase_form}, + "cotisations/edit_facture.html", + request, + ) # TODO : change facture to invoice @@ -379,51 +370,41 @@ def del_facture(request, facture, **_kwargs): """ if request.method == "POST": facture.delete() - messages.success( - request, - _("The invoice was deleted.") - ) - return redirect(reverse('cotisations:index')) - return form({ - 'objet': facture, - 'objet_name': _("Invoice") - }, 'cotisations/delete.html', request) + messages.success(request, _("The invoice was deleted.")) + return redirect(reverse("cotisations:index")) + return form( + {"objet": facture, "objet_name": _("invoice")}, + "cotisations/delete.html", + request, + ) @login_required @can_edit(CostEstimate) def edit_cost_estimate(request, invoice, **kwargs): # Building the invocie form and the article formset - invoice_form = CostEstimateForm( - request.POST or None, - instance=invoice - ) + invoice_form = CostEstimateForm(request.POST or None, instance=invoice) purchases_objects = Vente.objects.filter(facture=invoice) purchase_form_set = modelformset_factory( - Vente, - fields=('name', 'number'), - extra=0, - max_num=len(purchases_objects) - ) - purchase_form = purchase_form_set( - request.POST or None, - queryset=purchases_objects + Vente, fields=("name", "number"), extra=0, max_num=len(purchases_objects) ) + purchase_form = purchase_form_set(request.POST or None, queryset=purchases_objects) if invoice_form.is_valid() and purchase_form.is_valid(): if invoice_form.changed_data: invoice_form.save() purchase_form.save() - messages.success( - request, - _("The cost estimate was edited.") - ) - return redirect(reverse('cotisations:index-cost-estimate')) + messages.success(request, _("The cost estimate was edited.")) + return redirect(reverse("cotisations:index-cost-estimate")) - return form({ - 'factureform': invoice_form, - 'venteform': purchase_form, - 'title': _("Edit cost estimate") - }, 'cotisations/edit_facture.html', request) + return form( + { + "factureform": invoice_form, + "venteform": purchase_form, + "title": _("Edit cost estimate"), + }, + "cotisations/edit_facture.html", + request, + ) @login_required @@ -433,45 +414,37 @@ def cost_estimate_to_invoice(request, cost_estimate, **_kwargs): """Create a custom invoice from a cos estimate""" cost_estimate.create_invoice() messages.success( - request, - _("An invoice was successfully created from your cost estimate.") + request, _("An invoice was successfully created from your cost estimate.") ) - return redirect(reverse('cotisations:index-custom-invoice')) + return redirect(reverse("cotisations:index-custom-invoice")) @login_required @can_edit(CustomInvoice) def edit_custom_invoice(request, invoice, **kwargs): # Building the invocie form and the article formset - invoice_form = CustomInvoiceForm( - request.POST or None, - instance=invoice - ) + invoice_form = CustomInvoiceForm(request.POST or None, instance=invoice) purchases_objects = Vente.objects.filter(facture=invoice) purchase_form_set = modelformset_factory( - Vente, - fields=('name', 'number'), - extra=0, - max_num=len(purchases_objects) - ) - purchase_form = purchase_form_set( - request.POST or None, - queryset=purchases_objects + Vente, fields=("name", "number"), extra=0, max_num=len(purchases_objects) ) + purchase_form = purchase_form_set(request.POST or None, queryset=purchases_objects) if invoice_form.is_valid() and purchase_form.is_valid(): if invoice_form.changed_data: invoice_form.save() purchase_form.save() - messages.success( - request, - _("The invoice was edited.") - ) - return redirect(reverse('cotisations:index-custom-invoice')) + messages.success(request, _("The invoice was edited.")) + return redirect(reverse("cotisations:index-custom-invoice")) - return form({ - 'factureform': invoice_form, - 'venteform': purchase_form - }, 'cotisations/edit_facture.html', request) + return form( + { + "factureform": invoice_form, + "venteform": purchase_form, + "title": _("Edit custom invoice"), + }, + "cotisations/edit_facture.html", + request, + ) @login_required @@ -488,32 +461,37 @@ def cost_estimate_pdf(request, invoice, **_kwargs): # contiaining (article_name, article_price, quantity, total_price) purchases_info = [] for purchase in purchases_objects: - purchases_info.append({ - 'name': escape_chars(purchase.name), - 'price': purchase.prix, - 'quantity': purchase.number, - 'total_price': purchase.prix_total - }) - return render_invoice(request, { - 'paid': invoice.paid, - 'fid': invoice.id, - 'DATE': invoice.date, - 'recipient_name': invoice.recipient, - 'address': invoice.address, - 'article': purchases_info, - 'total': invoice.prix_total(), - '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), - 'payment_method': invoice.payment, - 'remark': invoice.remark, - 'end_validity': invoice.date + invoice.validity, - 'is_estimate': True, - }) + purchases_info.append( + { + "name": escape_chars(purchase.name), + "price": purchase.prix, + "quantity": purchase.number, + "total_price": purchase.prix_total, + } + ) + return render_invoice( + request, + { + "paid": invoice.paid, + "fid": invoice.id, + "DATE": invoice.date, + "recipient_name": invoice.recipient, + "address": invoice.address, + "article": purchases_info, + "total": invoice.prix_total(), + "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), + "payment_method": invoice.payment, + "remark": invoice.remark, + "end_validity": invoice.date + invoice.validity, + "is_estimate": True, + }, + ) @login_required @@ -524,15 +502,13 @@ def del_cost_estimate(request, estimate, **_kwargs): """ if request.method == "POST": estimate.delete() - messages.success( - request, - _("The cost estimate was deleted.") - ) - return redirect(reverse('cotisations:index-cost-estimate')) - return form({ - 'objet': estimate, - 'objet_name': _("Cost estimate") - }, 'cotisations/delete.html', request) + messages.success(request, _("The cost estimate was deleted.")) + return redirect(reverse("cotisations:index-cost-estimate")) + return form( + {"objet": estimate, "objet_name": _("cost estimate")}, + "cotisations/delete.html", + request, + ) @login_required @@ -550,30 +526,35 @@ def custom_invoice_pdf(request, invoice, **_kwargs): # contiaining (article_name, article_price, quantity, total_price) purchases_info = [] for purchase in purchases_objects: - purchases_info.append({ - 'name': escape_chars(purchase.name), - 'price': purchase.prix, - 'quantity': purchase.number, - 'total_price': purchase.prix_total - }) - return render_invoice(request, { - 'paid': invoice.paid, - 'fid': invoice.id, - 'DATE': invoice.date, - 'recipient_name': invoice.recipient, - 'address': invoice.address, - 'article': purchases_info, - 'total': invoice.prix_total(), - '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), - 'payment_method': invoice.payment, - 'remark': invoice.remark, - }) + purchases_info.append( + { + "name": escape_chars(purchase.name), + "price": purchase.prix, + "quantity": purchase.number, + "total_price": purchase.prix_total, + } + ) + return render_invoice( + request, + { + "paid": invoice.paid, + "fid": invoice.id, + "DATE": invoice.date, + "recipient_name": invoice.recipient, + "address": invoice.address, + "article": purchases_info, + "total": invoice.prix_total(), + "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), + "payment_method": invoice.payment, + "remark": invoice.remark, + }, + ) @login_required @@ -584,15 +565,13 @@ def del_custom_invoice(request, invoice, **_kwargs): """ if request.method == "POST": invoice.delete() - messages.success( - request, - _("The invoice was deleted.") - ) - return redirect(reverse('cotisations:index-custom-invoice')) - return form({ - 'objet': invoice, - 'objet_name': _("Invoice") - }, 'cotisations/delete.html', request) + messages.success(request, _("The invoice was deleted.")) + return redirect(reverse("cotisations:index-custom-invoice")) + return form( + {"objet": invoice, "objet_name": _("invoice")}, + "cotisations/delete.html", + request, + ) @login_required @@ -610,16 +589,17 @@ def add_article(request): article = ArticleForm(request.POST or None) if article.is_valid(): article.save() - messages.success( - request, - _("The article was created.") - ) - return redirect(reverse('cotisations:index-article')) - return form({ - 'factureform': article, - 'action_name': _("Add"), - 'title': _("New article") - }, 'cotisations/facture.html', request) + messages.success(request, _("The article was created.")) + return redirect(reverse("cotisations:index-article")) + return form( + { + "factureform": article, + "action_name": _("Add"), + "title": _("New article"), + }, + "cotisations/facture.html", + request, + ) @login_required @@ -632,16 +612,17 @@ def edit_article(request, article_instance, **_kwargs): if article.is_valid(): if article.changed_data: article.save() - messages.success( - request, - _("The article was edited.") - ) - return redirect(reverse('cotisations:index-article')) - return form({ - 'factureform': article, - 'action_name': _('Edit'), - 'title': _("Edit article") - }, 'cotisations/facture.html', request) + messages.success(request, _("The article was edited.")) + return redirect(reverse("cotisations:index-article")) + return form( + { + "factureform": article, + "action_name": _("Edit"), + "title": _("Edit article"), + }, + "cotisations/facture.html", + request, + ) @login_required @@ -652,18 +633,19 @@ def del_article(request, instances): """ article = DelArticleForm(request.POST or None, instances=instances) if article.is_valid(): - article_del = article.cleaned_data['articles'] + article_del = article.cleaned_data["articles"] article_del.delete() - messages.success( - request, - _("The articles were deleted.") - ) - return redirect(reverse('cotisations:index-article')) - return form({ - 'factureform': article, - 'action_name': _("Delete"), - 'title': _("Delete article") - }, 'cotisations/facture.html', request) + messages.success(request, _("The articles were deleted.")) + return redirect(reverse("cotisations:index-article")) + return form( + { + "factureform": article, + "action_name": _("Delete"), + "title": _("Delete article"), + }, + "cotisations/facture.html", + request, + ) # TODO : change paiement to payment @@ -673,26 +655,25 @@ def add_paiement(request): """ View used to add a payment method. """ - payment = PaiementForm(request.POST or None, prefix='payment') + payment = PaiementForm(request.POST or None, prefix="payment") payment_method = payment_method_factory( - payment.instance, - request.POST or None, - prefix='payment_method' + payment.instance, request.POST or None, prefix="payment_method" ) if payment.is_valid() and payment_method.is_valid(): payment = payment.save() payment_method.save(payment) - messages.success( - request, - _("The payment method was created.") - ) - return redirect(reverse('cotisations:index-paiement')) - return form({ - 'factureform': payment, - 'payment_method': payment_method, - 'action_name': _("Add"), - 'title': _("New payment method") - }, 'cotisations/facture.html', request) + messages.success(request, _("The payment method was created.")) + return redirect(reverse("cotisations:index-paiement")) + return form( + { + "factureform": payment, + "payment_method": payment_method, + "action_name": _("Add"), + "title": _("New payment method"), + }, + "cotisations/facture.html", + request, + ) # TODO : chnage paiement to Payment @@ -703,32 +684,28 @@ def edit_paiement(request, paiement_instance, **_kwargs): View used to edit a payment method. """ payment = PaiementForm( - request.POST or None, - instance=paiement_instance, - prefix="payment" + request.POST or None, instance=paiement_instance, prefix="payment" ) payment_method = payment_method_factory( - paiement_instance, - request.POST or None, - prefix='payment_method', - creation=False + paiement_instance, request.POST or None, prefix="payment_method", creation=False ) - if payment.is_valid() and \ - (payment_method is None or payment_method.is_valid()): + if payment.is_valid() and (payment_method is None or payment_method.is_valid()): payment.save() if payment_method is not None: payment_method.save() - messages.success( - request, _("The payment method was edited.") - ) - return redirect(reverse('cotisations:index-paiement')) - return form({ - 'factureform': payment, - 'payment_method': payment_method, - 'action_name': _("Edit"), - 'title': _("Edit payment method") - }, 'cotisations/facture.html', request) + messages.success(request, _("The payment method was edited.")) + return redirect(reverse("cotisations:index-paiement")) + return form( + { + "factureform": payment, + "payment_method": payment_method, + "action_name": _("Edit"), + "title": _("Edit payment method"), + }, + "cotisations/facture.html", + request, + ) # TODO : change paiement to payment @@ -740,30 +717,34 @@ def del_paiement(request, instances): """ payment = DelPaiementForm(request.POST or None, instances=instances) if payment.is_valid(): - payment_dels = payment.cleaned_data['paiements'] + payment_dels = payment.cleaned_data["paiements"] for payment_del in payment_dels: try: payment_del.delete() messages.success( request, - _("The payment method %(method_name)s was deleted.") % { - 'method_name': payment_del - } + _("The payment method %(method_name)s was deleted.") + % {"method_name": payment_del}, ) except ProtectedError: messages.error( request, - _("The payment method %(method_name)s can't be deleted \ - because there are invoices using it.") % { - 'method_name': payment_del - } + _( + "The payment method %(method_name)s can't be deleted" + " because there are invoices using it." + ) + % {"method_name": payment_del}, ) - return redirect(reverse('cotisations:index-paiement')) - return form({ - 'factureform': payment, - 'action_name': _("Delete"), - 'title': _("Delete payment method") - }, 'cotisations/facture.html', request) + return redirect(reverse("cotisations:index-paiement")) + return form( + { + "factureform": payment, + "action_name": _("Delete"), + "title": _("Delete payment method"), + }, + "cotisations/facture.html", + request, + ) # TODO : change banque to bank @@ -776,16 +757,17 @@ def add_banque(request): bank = BanqueForm(request.POST or None) if bank.is_valid(): bank.save() - messages.success( - request, - _("The bank was created.") - ) - return redirect(reverse('cotisations:index-banque')) - return form({ - 'factureform': bank, - 'action_name': _("Add"), - 'title': _("New bank") - }, 'cotisations/facture.html', request) + messages.success(request, _("The bank was created.")) + return redirect(reverse("cotisations:index-banque")) + return form( + { + "factureform": bank, + "action_name": _("Add"), + "title": _("New bank"), + }, + "cotisations/facture.html", + request, + ) # TODO : change banque to bank @@ -799,16 +781,17 @@ def edit_banque(request, banque_instance, **_kwargs): if bank.is_valid(): if bank.changed_data: bank.save() - messages.success( - request, - _("The bank was edited.") - ) - return redirect(reverse('cotisations:index-banque')) - return form({ - 'factureform': bank, - 'action_name': _("Edit"), - 'title': _("Edit bank") - }, 'cotisations/facture.html', request) + messages.success(request, _("The bank was edited.")) + return redirect(reverse("cotisations:index-banque")) + return form( + { + "factureform": bank, + "action_name": _("Edit"), + "title": _("Edit bank"), + }, + "cotisations/facture.html", + request, + ) # TODO : chnage banque to bank @@ -820,71 +803,70 @@ def del_banque(request, instances): """ bank = DelBanqueForm(request.POST or None, instances=instances) if bank.is_valid(): - bank_dels = bank.cleaned_data['banques'] + bank_dels = bank.cleaned_data["banques"] for bank_del in bank_dels: try: bank_del.delete() messages.success( request, - _("The bank %(bank_name)s was deleted.") % { - 'bank_name': bank_del - } + _("The bank %(bank_name)s was deleted.") % {"bank_name": bank_del}, ) except ProtectedError: messages.error( request, - _("The bank %(bank_name)s can't be deleted because there" - " are invoices using it.") % { - 'bank_name': bank_del - } + _( + "The bank %(bank_name)s can't be deleted because there" + " are invoices using it." + ) + % {"bank_name": bank_del}, ) - return redirect(reverse('cotisations:index-banque')) - return form({ - 'factureform': bank, - 'action_name': _("Delete"), - 'title': _("Delete bank") - }, 'cotisations/facture.html', request) + return redirect(reverse("cotisations:index-banque")) + return form( + { + "factureform": bank, + "action_name": _("Delete"), + "title": _("Delete bank"), + }, + "cotisations/facture.html", + request, + ) # TODO : change facture to invoice @login_required @can_view_all(Facture) -@can_change(Facture, 'control') +@can_change(Facture, "control") def control(request): """ View used to control the invoices all at once. """ - pagination_number = GeneralOption.get_cached_value('pagination_number') - invoice_list = (Facture.objects.select_related('user'). - select_related('paiement')) + pagination_number = GeneralOption.get_cached_value("pagination_number") + invoice_list = Facture.objects.select_related("user").select_related("paiement") invoice_list = SortTable.sort( invoice_list, - request.GET.get('col'), - request.GET.get('order'), - SortTable.COTISATIONS_CONTROL + request.GET.get("col"), + request.GET.get("order"), + SortTable.COTISATIONS_CONTROL, ) control_invoices_formset = modelformset_factory( - Facture, - fields=('control', 'valid'), - extra=0 + Facture, fields=("control", "valid"), extra=0 ) invoice_list = re2o_paginator(request, invoice_list, pagination_number) control_invoices_form = control_invoices_formset( - request.POST or None, - queryset=invoice_list.object_list + request.POST or None, queryset=invoice_list.object_list ) if control_invoices_form.is_valid(): control_invoices_form.save() - reversion.set_comment("Controle") + reversion.set_comment("Control") messages.success( - request, - _("Your changes have been properly taken into account.") + request, _("Your changes have been properly taken into account.") ) - return redirect(reverse('cotisations:control')) - return render(request, 'cotisations/control.html', { - 'facture_list': invoice_list, - 'controlform': control_invoices_form - }) + return redirect(reverse("cotisations:control")) + return render( + request, + "cotisations/control.html", + {"facture_list": invoice_list, "controlform": control_invoices_form}, + ) @login_required @@ -894,10 +876,10 @@ def index_article(request): View used to display the list of all available articles. """ # TODO : Offer other means of sorting - article_list = Article.objects.order_by('name') - return render(request, 'cotisations/index_article.html', { - 'article_list': article_list - }) + article_list = Article.objects.order_by("name") + return render( + request, "cotisations/index_article.html", {"article_list": article_list} + ) # TODO : change paiement to payment @@ -907,10 +889,10 @@ def index_paiement(request): """ View used to display the list of all available payment methods. """ - payment_list = Paiement.objects.order_by('moyen') - return render(request, 'cotisations/index_paiement.html', { - 'paiement_list': payment_list - }) + payment_list = Paiement.objects.order_by("moyen") + return render( + request, "cotisations/index_paiement.html", {"paiement_list": payment_list} + ) # TODO : change banque to bank @@ -920,56 +902,53 @@ def index_banque(request): """ View used to display the list of all available banks. """ - bank_list = Banque.objects.order_by('name') - return render(request, 'cotisations/index_banque.html', { - 'banque_list': bank_list - }) + bank_list = Banque.objects.order_by("name") + return render(request, "cotisations/index_banque.html", {"banque_list": bank_list}) @login_required @can_view_all(CustomInvoice) def index_cost_estimate(request): """View used to display every custom invoice.""" - pagination_number = GeneralOption.get_cached_value('pagination_number') - cost_estimate_list = CostEstimate.objects.prefetch_related('vente_set') + pagination_number = GeneralOption.get_cached_value("pagination_number") + cost_estimate_list = CostEstimate.objects.prefetch_related("vente_set") cost_estimate_list = SortTable.sort( cost_estimate_list, - request.GET.get('col'), - request.GET.get('order'), - SortTable.COTISATIONS_CUSTOM + request.GET.get("col"), + request.GET.get("order"), + SortTable.COTISATIONS_CUSTOM, ) - cost_estimate_list = re2o_paginator( + cost_estimate_list = re2o_paginator(request, cost_estimate_list, pagination_number) + return render( request, - cost_estimate_list, - pagination_number, + "cotisations/index_cost_estimate.html", + {"cost_estimate_list": cost_estimate_list}, ) - return render(request, 'cotisations/index_cost_estimate.html', { - 'cost_estimate_list': cost_estimate_list - }) @login_required @can_view_all(CustomInvoice) def index_custom_invoice(request): """View used to display every custom invoice.""" - pagination_number = GeneralOption.get_cached_value('pagination_number') - cost_estimate_ids = [i for i, in CostEstimate.objects.values_list('id')] - custom_invoice_list = CustomInvoice.objects.prefetch_related( - 'vente_set').exclude(id__in=cost_estimate_ids) + pagination_number = GeneralOption.get_cached_value("pagination_number") + cost_estimate_ids = [i for i, in CostEstimate.objects.values_list("id")] + custom_invoice_list = CustomInvoice.objects.prefetch_related("vente_set").exclude( + id__in=cost_estimate_ids + ) custom_invoice_list = SortTable.sort( custom_invoice_list, - request.GET.get('col'), - request.GET.get('order'), - SortTable.COTISATIONS_CUSTOM + request.GET.get("col"), + request.GET.get("order"), + SortTable.COTISATIONS_CUSTOM, ) custom_invoice_list = re2o_paginator( - request, - custom_invoice_list, - pagination_number, + request, custom_invoice_list, pagination_number + ) + return render( + request, + "cotisations/index_custom_invoice.html", + {"custom_invoice_list": custom_invoice_list}, ) - return render(request, 'cotisations/index_custom_invoice.html', { - 'custom_invoice_list': custom_invoice_list - }) @login_required @@ -979,19 +958,20 @@ def index(request): """ View used to display the list of all exisitng invoices. """ - pagination_number = GeneralOption.get_cached_value('pagination_number') - invoice_list = Facture.objects.select_related('user')\ - .select_related('paiement').prefetch_related('vente_set') + pagination_number = GeneralOption.get_cached_value("pagination_number") + invoice_list = ( + Facture.objects.select_related("user") + .select_related("paiement") + .prefetch_related("vente_set") + ) invoice_list = SortTable.sort( invoice_list, - request.GET.get('col'), - request.GET.get('order'), - SortTable.COTISATIONS_INDEX + request.GET.get("col"), + request.GET.get("order"), + SortTable.COTISATIONS_INDEX, ) invoice_list = re2o_paginator(request, invoice_list, pagination_number) - return render(request, 'cotisations/index.html', { - 'facture_list': invoice_list, - }) + return render(request, "cotisations/index.html", {"facture_list": invoice_list}) # TODO : change solde to balance @@ -1007,28 +987,22 @@ def credit_solde(request, user, **_kwargs): except Paiement.DoesNotExist: credit_allowed = False else: - credit_allowed = ( - balance is not None - and balance.can_credit_balance(request.user) + credit_allowed = balance is not None and balance.can_credit_balance( + request.user ) if not credit_allowed: - messages.error( - request, - _("You are not allowed to credit your balance.") - ) - return redirect(reverse( - 'users:profil', - kwargs={'userid': user.id} - )) + messages.error(request, _("You are not allowed to credit your balance.")) + return redirect(reverse("users:profil", kwargs={"userid": user.id})) refill_form = RechargeForm( - request.POST or None, user=user, user_source=request.user) + request.POST or None, user=user, user_source=request.user + ) if refill_form.is_valid(): - price = refill_form.cleaned_data['value'] + price = refill_form.cleaned_data["value"] invoice = Facture(user=user) - invoice.paiement = refill_form.cleaned_data['payment'] + invoice.paiement = refill_form.cleaned_data["payment"] p = find_payment_method(invoice.paiement) - if hasattr(p, 'check_price'): + if hasattr(p, "check_price"): price_ok, msg = p.check_price(price, user) refill_form.add_error(None, msg) else: @@ -1037,20 +1011,24 @@ def credit_solde(request, user, **_kwargs): invoice.save() Vente.objects.create( facture=invoice, - name='solde', - prix=refill_form.cleaned_data['value'], - number=1 + name="solde", + prix=refill_form.cleaned_data["value"], + number=1, ) return invoice.paiement.end_payment(invoice, request) p = get_object_or_404(Paiement, is_balance=True) - return form({ - 'factureform': refill_form, - 'balance': user.solde, - 'title': _("Refill your balance"), - 'action_name': _("Pay"), - 'max_balance': find_payment_method(p).maximum_balance, - }, 'cotisations/facture.html', request) + return form( + { + "factureform": refill_form, + "balance": user.solde, + "title": _("Refill your balance"), + "action_name": _("Pay"), + "max_balance": find_payment_method(p).maximum_balance, + }, + "cotisations/facture.html", + request, + ) @login_required @@ -1063,18 +1041,19 @@ def voucher_pdf(request, invoice, **_kwargs): legal information for the user. """ if not invoice.control: - messages.error( - request, - _("Could not find a voucher for that invoice.") - ) - return redirect(reverse('cotisations:index')) - return render_voucher(request, { - 'asso_name': AssoOption.get_cached_value('name'), - 'pres_name': AssoOption.get_cached_value('pres_name'), - 'firstname': invoice.user.name, - 'lastname': invoice.user.surname, - 'email': invoice.user.email, - 'phone': invoice.user.telephone, - 'date_end': invoice.get_subscription().latest('date_end').date_end, - 'date_begin': invoice.date - }) + messages.error(request, _("Could not find a voucher for that invoice.")) + return redirect(reverse("cotisations:index")) + president = Mandate.get_mandate(invoice.date).president + return render_voucher( + request, + { + "asso_name": AssoOption.get_cached_value("name"), + "pres_name": " ".join([president.name, president.surname]), + "firstname": invoice.user.name, + "lastname": invoice.user.surname, + "email": invoice.user.email, + "phone": invoice.user.telephone, + "date_end": invoice.get_subscription().latest("date_end").date_end, + "date_begin": invoice.date, + }, + ) diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index 54364dc1..496f2f3f 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -5,7 +5,7 @@ # # Copyirght © 2017 Daniel Stan # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -62,7 +62,7 @@ from preferences.models import RadiusOption #: Serveur radius de test (pas la prod) -TEST_SERVER = bool(os.getenv('DBG_FREERADIUS', False)) +TEST_SERVER = bool(os.getenv("DBG_FREERADIUS", False)) # Logging @@ -77,13 +77,13 @@ class RadiusdHandler(logging.Handler): rad_sig = radiusd.L_INFO else: rad_sig = radiusd.L_DBG - radiusd.radlog(rad_sig, record.msg.encode('utf-8')) + radiusd.radlog(rad_sig, record.msg.encode("utf-8")) # Initialisation d'un logger (pour logguer unifié) -logger = logging.getLogger('auth.py') +logger = logging.getLogger("auth.py") logger.setLevel(logging.DEBUG) -formatter = logging.Formatter('%(name)s: [%(levelname)s] %(message)s') +formatter = logging.Formatter("%(name)s: [%(levelname)s] %(message)s") handler = RadiusdHandler() handler.setFormatter(formatter) logger.addHandler(handler) @@ -111,14 +111,16 @@ def radius_event(fun): for (key, value) in auth_data or []: # Beware: les valeurs scalaires sont entre guillemets # Ex: Calling-Station-Id: "une_adresse_mac" - data[key] = value.replace('"', '') + data[key] = value.replace('"', "") try: # TODO s'assurer ici que les tuples renvoyés sont bien des # (str,str) : rlm_python ne digère PAS les unicodes return fun(data) except Exception as err: - logger.error('Failed %r on data %r' % (err, auth_data)) - logger.debug('Function %r, Traceback: %s' % (fun, repr(traceback.format_stack()))) + exc_type, exc_instance, exc_traceback = sys.exc_info() + formatted_traceback = "".join(traceback.format_tb(exc_traceback)) + logger.error("Failed %r on data %r" % (err, auth_data)) + logger.error("Function %r, Traceback : %r" % (fun, formatted_traceback)) return radiusd.RLM_MODULE_FAIL return new_f @@ -128,9 +130,9 @@ def radius_event(fun): def instantiate(*_): """Utile pour initialiser les connexions ldap une première fois (otherwise, do nothing)""" - logger.info('Instantiation') + logger.info("Instantiation") if TEST_SERVER: - logger.info(u'DBG_FREERADIUS is enabled') + logger.info(u"DBG_FREERADIUS is enabled") @radius_event @@ -143,23 +145,19 @@ def authorize(data): accept en authorize """ # Pour les requetes proxifiees, on split - nas = data.get('NAS-IP-Address', data.get('NAS-Identifier', None)) + nas = data.get("NAS-IP-Address", data.get("NAS-Identifier", None)) nas_instance = find_nas_from_request(nas) # Toutes les reuquètes non proxifiées nas_type = None if nas_instance: - nas_type = Nas.objects.filter(nas_type=nas_instance.type).first() - if not nas_type or nas_type.port_access_mode == '802.1X': - user = data.get('User-Name', '').decode('utf-8', errors='replace') - user = user.split('@', 1)[0] - mac = data.get('Calling-Station-Id', '') - result, log, password = check_user_machine_and_register( - nas_type, - user, - mac - ) - logger.info(log.encode('utf-8')) - logger.info(user.encode('utf-8')) + nas_type = Nas.objects.filter(nas_type=nas_instance.machine_type).first() + if not nas_type or nas_type.port_access_mode == "802.1X": + user = data.get("User-Name", "").decode("utf-8", errors="replace") + user = user.split("@", 1)[0] + mac = data.get("Calling-Station-Id", "") + result, log, password = check_user_machine_and_register(nas_type, user, mac) + logger.info(log.encode("utf-8")) + logger.info(user.encode("utf-8")) if not result: return radiusd.RLM_MODULE_REJECT @@ -167,19 +165,11 @@ def authorize(data): return ( radiusd.RLM_MODULE_UPDATED, (), - ( - (str("NT-Password"), str(password)), - ), + ((str("NT-Password"), str(password)),), ) else: - return ( - radiusd.RLM_MODULE_UPDATED, - (), - ( - ("Auth-Type", "Accept"), - ), - ) + return (radiusd.RLM_MODULE_UPDATED, (), (("Auth-Type", "Accept"),)) @radius_event @@ -187,52 +177,49 @@ def post_auth(data): """ Function called after the user is authenticated """ - nas = data.get('NAS-IP-Address', data.get('NAS-Identifier', None)) + nas = data.get("NAS-IP-Address", data.get("NAS-Identifier", None)) nas_instance = find_nas_from_request(nas) # Toutes les reuquètes non proxifiées if not nas_instance: - logger.info(u"Requete proxifiee, nas inconnu".encode('utf-8')) + logger.info(u"Requete proxifiee, nas inconnu".encode("utf-8")) return radiusd.RLM_MODULE_OK - nas_type = Nas.objects.filter(nas_type=nas_instance.type).first() + nas_type = Nas.objects.filter(nas_type=nas_instance.machine_type).first() if not nas_type: - logger.info( - u"Type de nas non enregistre dans la bdd!".encode('utf-8') - ) + logger.info(u"Type de nas non enregistre dans la bdd!".encode("utf-8")) return radiusd.RLM_MODULE_OK - mac = data.get('Calling-Station-Id', None) + mac = data.get("Calling-Station-Id", None) # Switch et bornes héritent de machine et peuvent avoir plusieurs # interfaces filles nas_machine = nas_instance.machine # Si il s'agit d'un switch - if hasattr(nas_machine, 'switch'): - port = data.get('NAS-Port-Id', data.get('NAS-Port', None)) + if hasattr(nas_machine, "switch"): + port = data.get("NAS-Port-Id", data.get("NAS-Port", None)) # Pour les infrastructures possédant des switchs Juniper : # On vérifie si le switch fait partie d'un stack Juniper instance_stack = nas_machine.switch.stack if instance_stack: # Si c'est le cas, on resélectionne le bon switch dans la stack - id_stack_member = port.split("-")[1].split('/')[0] - nas_machine = (Switch.objects - .filter(stack=instance_stack) - .filter(stack_member_id=id_stack_member) - .prefetch_related( - 'interface_set__domain__extension' - ) - .first()) + id_stack_member = port.split("-")[1].split("/")[0] + nas_machine = ( + Switch.objects.filter(stack=instance_stack) + .filter(stack_member_id=id_stack_member) + .prefetch_related("interface_set__domain__extension") + .first() + ) # On récupère le numéro du port sur l'output de freeradius. # La ligne suivante fonctionne pour cisco, HP et Juniper - port = port.split(".")[0].split('/')[-1][-2:] + port = port.split(".")[0].split("/")[-1][-2:] out = decide_vlan_switch(nas_machine, nas_type, port, mac) - sw_name, room, reason, vlan_id, decision = out + sw_name, room, reason, vlan_id, decision, attributes = out if decision: - log_message = '(fil) %s -> %s [%s%s]' % ( + log_message = "(fil) %s -> %s [%s%s]" % ( sw_name + u":" + port + u"/" + str(room), mac, vlan_id, - (reason and u': ' + reason).encode('utf-8') + (reason and u": " + reason).encode("utf-8"), ) logger.info(log_message) @@ -242,19 +229,20 @@ def post_auth(data): ( ("Tunnel-Type", "VLAN"), ("Tunnel-Medium-Type", "IEEE-802"), - ("Tunnel-Private-Group-Id", '%d' % int(vlan_id)), - ), - () + ("Tunnel-Private-Group-Id", "%d" % int(vlan_id)), + ) + + tuple(attributes), + (), ) else: - log_message = '(fil) %s -> %s [Reject:%s]' % ( + log_message = "(fil) %s -> %s [Reject:%s]" % ( sw_name + u":" + port + u"/" + str(room), mac, - (reason and u': ' + reason).encode('utf-8') + (reason and u": " + reason).encode("utf-8"), ) logger.info(log_message) - return radiusd.RLM_MODULE_REJECT + return (radiusd.RLM_MODULE_REJECT, tuple(attributes), ()) else: return radiusd.RLM_MODULE_OK @@ -275,13 +263,14 @@ def detach(_=None): def find_nas_from_request(nas_id): """ Get the nas object from its ID """ - nas = (Interface.objects - .filter( - Q(domain=Domain.objects.filter(name=nas_id)) | - Q(ipv4=IpList.objects.filter(ipv4=nas_id)) - ) - .select_related('type') - .select_related('machine__switch__stack')) + nas = ( + Interface.objects.filter( + Q(domain=Domain.objects.filter(name=nas_id)) + | Q(ipv4=IpList.objects.filter(ipv4=nas_id)) + ) + .select_related("machine_type") + .select_related("machine__switch__stack") + ) return nas.first() @@ -293,17 +282,18 @@ def check_user_machine_and_register(nas_type, username, mac_address): interface = Interface.objects.filter(mac_address=mac_address).first() user = User.objects.filter(pseudo__iexact=username).first() if not user: - return (False, u"User inconnu", '') + return (False, u"User inconnu", "") if not user.has_access(): - return (False, u"Adherent non cotisant", '') + return (False, u"Adherent non cotisant", "") if interface: if interface.machine.user != user: - return (False, - u"Machine enregistree sur le compte d'un autre " - "user...", - '') + return ( + False, + u"Machine enregistree sur le compte d'un autre " "user...", + "", + ) elif not interface.is_active: - return (False, u"Machine desactivee", '') + return (False, u"Machine desactivee", "") elif not interface.ipv4: interface.assign_ipv4() return (True, u"Ok, Reassignation de l'ipv4", user.pwd_ntlm) @@ -313,19 +303,16 @@ def check_user_machine_and_register(nas_type, username, mac_address): if nas_type.autocapture_mac: result, reason = user.autoregister_machine(mac_address, nas_type) if result: - return (True, - u'Access Ok, Capture de la mac...', - user.pwd_ntlm) + return (True, u"Access Ok, Capture de la mac...", user.pwd_ntlm) else: - return (False, u'Erreur dans le register mac %s' % reason, '') + return (False, u"Erreur dans le register mac %s" % reason, "") else: - return (False, u'Machine inconnue', '') + return (False, u"Machine inconnue", "") else: - return (False, u"Machine inconnue", '') + return (False, u"Machine inconnue", "") -def decide_vlan_switch(nas_machine, nas_type, port_number, - mac_address): +def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address): """Fonction de placement vlan pour un switch en radius filaire auth par mac. Plusieurs modes : @@ -363,21 +350,30 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, - raison de la décision (str) - vlan_id (int) - decision (bool) + - Attributs supplémentaires (attribut:str, operateur:str, valeur:str) """ + attributes_kwargs = { + "client_mac": str(mac_address), + "switch_port": str(port_number), + } # Get port from switch and port number extra_log = "" # Si le NAS est inconnu, on place sur le vlan defaut if not nas_machine: - return ('?', u'Chambre inconnue', u'Nas inconnu', RadiusOption.get_cached_value('vlan_decision_ok').vlan_id, True) + return ( + "?", + u"Chambre inconnue", + u"Nas inconnu", + RadiusOption.get_cached_value("vlan_decision_ok").vlan_id, + True, + RadiusOption.get_attributes("ok_attributes", attributes_kwargs), + ) - sw_name = str(getattr(nas_machine, 'short_name', str(nas_machine))) + sw_name = str(getattr(nas_machine, "short_name", str(nas_machine))) - port = (Port.objects - .filter( - switch=Switch.objects.filter(machine_ptr=nas_machine), - port=port_number - ) - .first()) + switch = Switch.objects.filter(machine_ptr=nas_machine).first() + attributes_kwargs["switch_ip"] = str(switch.ipv4) + port = Port.objects.filter(switch=switch, port=port_number).first() # Si le port est inconnu, on place sur le vlan defaut # Aucune information particulière ne permet de déterminer quelle @@ -385,10 +381,13 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, if not port: return ( sw_name, - "Chambre inconnue", - u'Port inconnu', - getattr(RadiusOption.get_cached_value('unknown_port_vlan'), 'vlan_id', None), - RadiusOption.get_cached_value('unknown_port')!= RadiusOption.REJECT + "Port inconnu", + u"Port inconnu", + getattr( + RadiusOption.get_cached_value("unknown_port_vlan"), "vlan_id", None + ), + RadiusOption.get_cached_value("unknown_port") != RadiusOption.REJECT, + RadiusOption.get_attributes("unknown_port_attributes", attributes_kwargs), ) # On récupère le profil du port @@ -399,32 +398,38 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, if port_profile.vlan_untagged: DECISION_VLAN = int(port_profile.vlan_untagged.vlan_id) extra_log = u"Force sur vlan " + str(DECISION_VLAN) + attributes = () else: - DECISION_VLAN = RadiusOption.get_cached_value('vlan_decision_ok').vlan_id + DECISION_VLAN = RadiusOption.get_cached_value("vlan_decision_ok").vlan_id + attributes = RadiusOption.get_attributes("ok_attributes", attributes_kwargs) # Si le port est désactivé, on rejette la connexion if not port.state: - return (sw_name, port.room, u'Port desactive', None, False) + return (sw_name, port.room, u"Port desactive", None, False, ()) # Si radius est désactivé, on laisse passer - if port_profile.radius_type == 'NO': - return (sw_name, - "", - u"Pas d'authentification sur ce port" + extra_log, - DECISION_VLAN, - True) + if port_profile.radius_type == "NO": + return ( + sw_name, + "", + u"Pas d'authentification sur ce port" + extra_log, + DECISION_VLAN, + True, + attributes, + ) # Si le 802.1X est activé sur ce port, cela veut dire que la personne a # été accept précédemment # Par conséquent, on laisse passer sur le bon vlan - if (nas_type.port_access_mode, port_profile.radius_type) == ('802.1X', '802.1X'): + if (nas_type.port_access_mode, port_profile.radius_type) == ("802.1X", "802.1X"): room = port.room or "Chambre/local inconnu" return ( sw_name, room, - u'Acceptation authentification 802.1X', + u"Acceptation authentification 802.1X", DECISION_VLAN, - True + True, + attributes, ) # Sinon, cela veut dire qu'on fait de l'auth radius par mac @@ -433,15 +438,20 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, # (anti squattage) # Il n'est pas possible de se connecter sur une prise strict sans adhérent # à jour de cotis dedans - if port_profile.radius_mode == 'STRICT': + if port_profile.radius_mode == "STRICT": room = port.room if not room: return ( sw_name, "Inconnue", - u'Chambre inconnue', - getattr(RadiusOption.get_cached_value('unknown_room_vlan'), 'vlan_id', None), - RadiusOption.get_cached_value('unknown_room')!= RadiusOption.REJECT + u"Chambre inconnue", + getattr( + RadiusOption.get_cached_value("unknown_room_vlan"), "vlan_id", None + ), + RadiusOption.get_cached_value("unknown_room") != RadiusOption.REJECT, + RadiusOption.get_attributes( + "unknown_room_attributes", attributes_kwargs + ), ) room_user = User.objects.filter( @@ -451,38 +461,52 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, return ( sw_name, room, - u'Chambre non cotisante -> Web redirect', - None, - False + u"Chambre non cotisante", + getattr( + RadiusOption.get_cached_value("non_member_vlan"), "vlan_id", None + ), + RadiusOption.get_cached_value("non_member") != RadiusOption.REJECT, + RadiusOption.get_attributes("non_member_attributes", attributes_kwargs), ) for user in room_user: if user.is_ban() or user.state != User.STATE_ACTIVE: return ( sw_name, room, - u'Utilisateur banni ou desactive -> Web redirect', - None, - False + u"Utilisateur banni ou desactive", + getattr( + RadiusOption.get_cached_value("banned_vlan"), "vlan_id", None + ), + RadiusOption.get_cached_value("banned") != RadiusOption.REJECT, + RadiusOption.get_attributes("banned_attributes", attributes_kwargs), ) elif not (user.is_connected() or user.is_whitelisted()): return ( sw_name, room, - u'Utilisateur non cotisant', - getattr(RadiusOption.get_cached_value('non_member_vlan'), 'vlan_id', None), - RadiusOption.get_cached_value('non_member')!= RadiusOption.REJECT + u"Utilisateur non cotisant", + getattr( + RadiusOption.get_cached_value("non_member_vlan"), + "vlan_id", + None, + ), + RadiusOption.get_cached_value("non_member") != RadiusOption.REJECT, + RadiusOption.get_attributes( + "non_member_attributes", attributes_kwargs + ), ) # else: user OK, on passe à la verif MAC # Si on fait de l'auth par mac, on cherche l'interface # via sa mac dans la bdd - if port_profile.radius_mode == 'COMMON' or port_profile.radius_mode == 'STRICT': + if port_profile.radius_mode == "COMMON" or port_profile.radius_mode == "STRICT": # Authentification par mac - interface = (Interface.objects - .filter(mac_address=mac_address) - .select_related('machine__user') - .select_related('ipv4') - .first()) + interface = ( + Interface.objects.filter(mac_address=mac_address) + .select_related("machine__user") + .select_related("ipv4") + .first() + ) if not interface: room = port.room # On essaye de register la mac, si l'autocapture a été activée, @@ -491,9 +515,17 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, return ( sw_name, room, - u'Machine Inconnue -> Web redirect', - None, - False + u"Machine Inconnue", + getattr( + RadiusOption.get_cached_value("unknown_machine_vlan"), + "vlan_id", + None, + ), + RadiusOption.get_cached_value("unknown_machine") + != RadiusOption.REJECT, + RadiusOption.get_attributes( + "unknown_machine_attributes", attributes_kwargs + ), ) # Sinon on bascule sur la politique définie dans les options # radius. @@ -501,9 +533,17 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, return ( sw_name, "", - u'Machine inconnue', - getattr(RadiusOption.get_cached_value('unknown_machine_vlan'), 'vlan_id', None), - RadiusOption.get_cached_value('unknown_machine')!= RadiusOption.REJECT + u"Machine inconnue", + getattr( + RadiusOption.get_cached_value("unknown_machine_vlan"), + "vlan_id", + None, + ), + RadiusOption.get_cached_value("unknown_machine") + != RadiusOption.REJECT, + RadiusOption.get_attributes( + "unknown_machine_attributes", attributes_kwargs + ), ) # L'interface a été trouvée, on vérifie qu'elle est active, @@ -516,22 +556,32 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, return ( sw_name, room, - u'Adherent banni', - getattr(RadiusOption.get_cached_value('banned_vlan'), 'vlan_id', None), - RadiusOption.get_cached_value('banned')!= RadiusOption.REJECT + u"Adherent banni", + getattr( + RadiusOption.get_cached_value("banned_vlan"), "vlan_id", None + ), + RadiusOption.get_cached_value("banned") != RadiusOption.REJECT, + RadiusOption.get_attributes("banned_attributes", attributes_kwargs), ) if not interface.is_active: return ( sw_name, room, - u'Machine non active / adherent non cotisant', - getattr(RadiusOption.get_cached_value('non_member_vlan'), 'vlan_id', None), - RadiusOption.get_cached_value('non_member')!= RadiusOption.REJECT + u"Machine non active / adherent non cotisant", + getattr( + RadiusOption.get_cached_value("non_member_vlan"), + "vlan_id", + None, + ), + RadiusOption.get_cached_value("non_member") != RadiusOption.REJECT, + RadiusOption.get_attributes( + "non_member_attributes", attributes_kwargs + ), ) # Si on choisi de placer les machines sur le vlan # correspondant à leur type : - if RadiusOption.get_cached_value('radius_general_policy') == 'MACHINE': - DECISION_VLAN = interface.type.ip_type.vlan.vlan_id + if RadiusOption.get_cached_value("radius_general_policy") == "MACHINE": + DECISION_VLAN = interface.machine_type.ip_type.vlan.vlan_id if not interface.ipv4: interface.assign_ipv4() return ( @@ -539,13 +589,15 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, room, u"Ok, Reassignation de l'ipv4" + extra_log, DECISION_VLAN, - True + True, + attributes, ) else: return ( sw_name, room, - u'Machine OK' + extra_log, + u"Machine OK" + extra_log, DECISION_VLAN, - True + True, + attributes, ) diff --git a/freeradius_utils/freeradius3/clients.conf b/freeradius_utils/freeradius3/clients.conf new file mode 100644 index 00000000..186181d4 --- /dev/null +++ b/freeradius_utils/freeradius3/clients.conf @@ -0,0 +1,29 @@ +# Your client radius configuration below +client radius-filaire { + ipaddr = + netmask = + secret = + require_message_authenticator = no + nastype = other + virtual_server = radius-filaire +} +client radius-wifi { + ipaddr = + netmask = + secret = + require_message_authenticator = no + nastype = other + virtual_server = radius-wifi +} + +# Parangon (federez) +client parangon { + ipaddr = 185.230.78.47 + secret = please_ask_for_a_secret_to_federez_admin +} + +# Dodecagon (federez) +client dodecagon { + ipaddr = 163.172.48.168 + secret = please_ask_for_a_secret_to_federez_admin +} diff --git a/freeradius_utils/freeradius3/mods-enabled/eap b/freeradius_utils/freeradius3/mods-enabled/eap index 72f72955..8213bdae 100644 --- a/freeradius_utils/freeradius3/mods-enabled/eap +++ b/freeradius_utils/freeradius3/mods-enabled/eap @@ -2,7 +2,7 @@ ## ## eap.conf -- Configuration for EAP types (PEAP, TTLS, etc.) ## -## $Id: 0e8d5caef5ad09dfa6acb14c5d475bae55cf4b27 $ +## $Id: f67cbdbff9b6560cec9f68da1adb82b59723d2ef $ ####################################################################### # @@ -285,6 +285,10 @@ eap { ca_path = ${cadir} + # Accept an expired Certificate Revocation List + # +# allow_expired_crl = no + # # If check_cert_issuer is set, the value will # be checked against the DN of the issuer in @@ -292,10 +296,10 @@ eap { # match, the certificate verification will fail, # rejecting the user. # - # In 2.1.10 and later, this check can be done - # more generally by checking the value of the - # TLS-Client-Cert-Issuer attribute. This check - # can be done via any mechanism you choose. + # This check can be done more generally by checking + # the value of the TLS-Client-Cert-Issuer attribute. + # This check can be done via any mechanism you + # choose. # # check_cert_issuer = "/C=GB/ST=Berkshire/L=Newbury/O=My Company Ltd" @@ -325,16 +329,42 @@ eap { # cipher_list = "DEFAULT" - # Work-arounds for OpenSSL nonsense - # OpenSSL 1.0.1f and 1.0.1g do not calculate - # the EAP keys correctly. The fix is to upgrade - # OpenSSL, or disable TLS 1.2 here. - # - # For EAP-FAST, this MUST be set to "yes". - # -# disable_tlsv1_2 = no + # If enabled, OpenSSL will use server cipher list + # (possibly defined by cipher_list option above) + # for choosing right cipher suite rather than + # using client-specified list which is OpenSSl default + # behavior. Having it set to yes is a current best practice + # for TLS + cipher_server_preference = no # + # You can selectively disable TLS versions for + # compatability with old client devices. + # + # If your system has OpenSSL 1.1.0 or greater, do NOT + # use these. Instead, set tls_min_version and + # tls_max_version. + # +# disable_tlsv1_2 = no +# disable_tlsv1_1 = no +# disable_tlsv1 = no + + # + # Set min / max TLS version. Mainly for Debian + # "trusty", which disables older versions of TLS, and + # requires the application to manually enable them. + # + # If you are running Debian trusty, you should set + # these options, otherwise older clients will not be + # able to connect. + # + # Allowed values are "1.0", "1.1", and "1.2". + # + # The values must be in quotes. + # + tls_min_version = "1.0" + tls_max_version = "1.2" + # # Elliptical cryptography configuration @@ -374,6 +404,12 @@ eap { # Enable it. The default is "no". Deleting the entire "cache" # subsection also disables caching. # + # As of version 3.0.14, the session cache requires the use + # of the "name" and "persist_dir" configuration items, below. + # + # The internal OpenSSL session cache has been permanently + # disabled. + # # You can disallow resumption for a particular user by adding the # following attribute to the control item list: # @@ -388,16 +424,7 @@ eap { # Lifetime of the cached entries, in hours. The sessions will be # deleted/invalidated after this time. # - lifetime = 24 # hours - - # - # The maximum number of entries in the - # cache. Set to "0" for "infinite". - # - # This could be set to the number of users - # who are logged in... which can be a LOT. - # - max_entries = 255 + lifetime = 1 # hours # # Internal "name" of the session cache. Used to @@ -416,6 +443,11 @@ eap { # state and the cached VPs. This will persist session # across server restarts. # + # The default directory is ${logdir}, for historical + # reasons. You should ${db_dir} instead. And check + # the value of db_dir in the main radiusd.conf file. + # It should not point to ${raddb} + # # The server will need write perms, and the directory # should be secured from anyone else. You might want # a script to remove old files from here periodically: @@ -663,6 +695,10 @@ eap { # # in the control items for a request. # + # Note that the majority of supplicants do not support using a + # client certificate with EAP-TTLS, so this option is unlikely + # to be usable for most people. + # # require_client_cert = yes } @@ -789,6 +825,10 @@ eap { # # in the control items for a request. # + # Note that the majority of supplicants do not support using a + # client certificate with PEAP, so this option is unlikely to + # be usable for most people. + # # require_client_cert = yes } @@ -839,13 +879,26 @@ eap { # fast { # Point to the common TLS configuration # - # cipher_list though must include "ADH" for anonymous provisioning. - # This is not as straight forward as appending "ADH" alongside - # "DEFAULT" as "DEFAULT" contains "!aNULL" so instead it is - # recommended "ALL:!EXPORT:!eNULL:!SSLv2" is used - # # tls = tls-common + # + # If 'cipher_list' is set here, it will over-ride the + # 'cipher_list' configuration from the 'tls-common' + # configuration. The EAP-FAST module has it's own + # over-ride for 'cipher_list' because the + # specifications mandata a different set of ciphers + # than are used by the other EAP methods. + # + # cipher_list though must include "ADH" for anonymous provisioning. + # This is not as straight forward as appending "ADH" alongside + # "DEFAULT" as "DEFAULT" contains "!aNULL" so instead it is + # recommended "ALL:!EXPORT:!eNULL:!SSLv2" is used + # + # Note - for OpenSSL 1.1.0 and above you may need + # to add ":@SECLEVEL=0" + # +# cipher_list = "ALL:!EXPORT:!eNULL:!SSLv2" + # PAC lifetime in seconds (default: seven days) # # pac_lifetime = 604800 diff --git a/install_re2o.sh b/install_re2o.sh index d6b0a4ef..6cc9a2fb 100755 --- a/install_re2o.sh +++ b/install_re2o.sh @@ -4,11 +4,21 @@ SETTINGS_LOCAL_FILE='re2o/settings_local.py' SETTINGS_EXAMPLE_FILE='re2o/settings_local.example.py' APT_REQ_FILE="apt_requirements.txt" +APT_RADIUS_REQ_FILE="apt_requirements_radius.txt" PIP_REQ_FILE="pip_requirements.txt" +PIP_RADIUS_REQ_FILE="pip_requirements_radius.txt" LDIF_DB_FILE="install_utils/db.ldiff" LDIF_SCHEMA_FILE="install_utils/schema.ldiff" +FREERADIUS_CLIENTS="freeradius_utils/freeradius3/clients.conf" +FREERADIUS_AUTH="freeradius_utils/auth.py" +FREERADIUS_RADIUSD="freeradius_utils/freeradius3/radiusd.conf" +FREERADIUS_MOD_PYTHON="freeradius_utils/freeradius3/mods-enabled/python" +FREERADIUS_MOD_EAP="freeradius_utils/freeradius3/mods-enabled/eap" +FREERADIUS_SITE_DEFAULT="freeradius_utils/freeradius3/sites-enabled/default" +FREERADIUS_SITE_INNER_TUNNEL="freeradius_utils/freeradius3/sites-enabled/inner-tunnel" +EDITOR="nano" VALUE= # global value used to return values by some functions @@ -73,6 +83,44 @@ install_requirements() { } +install_radius_requirements() { + ### Usage: install_radius_requirements + # + # This function will install the required packages from APT repository + # and Pypi repository. Those packages are all required for Re2o to work + # properly. + ### + + echo "Setting up the required packages ..." + cat $APT_RADIUS_REQ_FILE | xargs apt-get -y install + python -m pip install -r $PIP_RADIUS_REQ_FILE + echo "Setting up the required packages: Done" +} + + +configure_radius() { + ### Usage: configure_radius + # + # This function configures freeradius. + ### + echo "Configuring Freeradius ..." + + cat $FREERADIUS_CLIENTS >> /etc/freeradius/3.0/clients.conf + ln -fs $(pwd)/$FREERADIUS_AUTH /etc/freeradius/3.0/auth.py + ln -fs $(pwd)/$FREERADIUS_RADIUSD /etc/freeradius/3.0/radiusd.conf + ln -fs $(pwd)/$FREERADIUS_MOD_PYTHON /etc/freeradius/3.0/mods-enabled/python + ln -fs $(pwd)/$FREERADIUS_MOD_EAP /etc/freeradius/3.0/mods-enabled/eap + ln -fs $(pwd)/$FREERADIUS_SITE_DEFAULT /etc/freeradius/3.0/sites-enabled/default + ln -fs $(pwd)/$FREERADIUS_SITE_INNER_TUNNEL /etc/freeradius/3.0/sites-enabled/inner-tunnel + _ask_value "Ready to edit clients.conf ?" "yes" + $EDITOR /etc/freeradius/3.0/clients.conf + + + echo "Configuring Freeradius: Done" + +} + + install_database() { ### Usage: install_database @@ -103,17 +151,17 @@ install_database() { if [ "$engine_type" == 1 ]; then echo "Installing MySQL client ..." - apt-get -y install python3-mysqldb mysql-client + apt-get -y install python3-mysqldb default-mysql-client echo "Installing MySQL client: Done" mysql_command="CREATE DATABASE $db_name collate='utf8_general_ci'; CREATE USER '$username'@'localhost' IDENTIFIED BY '$password'; GRANT ALL PRIVILEGES ON $db_name.* TO '$username'@'localhost'; - FLUSH PRIVILEGES;" + FLUSH PRIVILEGES;SET GLOBAL SQL_MODE=ANSI_QUOTES;" if [ "$local_setup" == 1 ]; then echo "Setting up local MySQL server ..." - apt-get -y install mysql-server + apt-get -y install default-mysql-server mysql -u root --execute="$mysql_command" echo "Setting up local MySQL server: Done" else @@ -715,6 +763,265 @@ interactive_guide() { +interactive_radius_guide() { + ### Usage: interactive_radius_guide + # + # This function will guide through the automated setup of radius with + # Re2o by asking the user for some informations and some installation + # choices. It will then proceed to setup and configuration of the + # required tools according to the user choices. + ### + + echo "Re2o setup !" + echo "This tool will help you setup re2o radius. It is highly recommended to use a Debian clean server for this operation." + + echo "Installing basic packages required for this script to work ..." + apt-get -y install sudo dialog + echo "Installing basic packages required for this script to work: Done" + + # Common setup for the dialog prompts + export DEBIAN_FRONTEND=noninteractive + HEIGHT=20 + WIDTH=60 + CHOICE_HEIGHT=4 + + + + ############# + ## Welcome ## + ############# + + BACKTITLE="Re2o setup" + + # Welcome prompt + TITLE="Welcome" + MSGBOX="This tool will help you setup re2o. It is highly recommended to use a Debian clean server for this operation." + init="$(dialog --clear --backtitle "$BACKTITLE" \ + --title "$TITLE" --msgbox "$MSGBOX" \ + $HEIGHT $WIDTH 2>&1 >/dev/tty)" + + + + ###################### + ## Database options ## + ###################### + + BACKTITLE="Re2o setup - configuration of the database" + + # Prompt for choosing the database engine + TITLE="Database engine" + MENU="Which engine should be used as the database ?" + OPTIONS=(1 "mysql" + 2 "postgresql") + sql_bdd_type="$(dialog --clear --backtitle "$BACKTITLE" \ + --title "$TITLE" --menu "$MENU" \ + $HEIGHT $WIDTH $CHOICE_HEIGHT "${OPTIONS[@]}" 2>&1 >/dev/tty)" + + # Prompt for choosing the database location + TITLE="SQL location" + MENU="Where to install the SQL database ? + * 'Local' will setup everything automatically but is not recommended for production + * 'Remote' will ask you to manually perform some setup commands on the remote server" + OPTIONS=(1 "Local" + 2 "Remote") + sql_is_local="$(dialog --clear --backtitle "$BACKTITLE" \ + --title "$TITLE" --menu "$MENU" \ + $HEIGHT $WIDTH $CHOICE_HEIGHT "${OPTIONS[@]}" 2>&1 >/dev/tty)" + + if [ $sql_is_local == 2 ]; then + # Prompt to enter the remote database hostname + TITLE="SQL hostname" + INPUTBOX="The hostname of the remote SQL database" + sql_host="$(dialog --clear --backtitle "$BACKTITLE" \ + --title "$TITLE" --inputbox "$INPUTBOX" \ + $HEIGHT $WIDTH 2>&1 >/dev/tty)" + + # Prompt to enter the remote database name + TITLE="SQL database name" + INPUTBOX="The name of the remote SQL database" + sql_name="$(dialog --clear --backtitle "$BACKTITLE" \ + --title "$TITLE" --inputbox "$INPUTBOX" \ + $HEIGHT $WIDTH 2>&1 >/dev/tty)" + + # Prompt to enter the remote database username + TITLE="SQL username" + INPUTBOX="The username to access the remote SQL database" + sql_login="$(dialog --clear --backtitle "$BACKTITLE" \ + --title "$TITLE" --inputbox "$INPUTBOX" \ + $HEIGHT $WIDTH 2>&1 >/dev/tty)" + clear + else + # Use of default values for local setup + sql_name="re2o" + sql_login="re2o" + sql_host="localhost" + fi + + # Prompt to enter the database password + TITLE="SQL password" + INPUTBOX="The password to access the SQL database" + sql_password="$(dialog --clear --backtitle "$BACKTITLE" \ + --title "$TITLE" --inputbox "$INPUTBOX" \ + $HEIGHT $WIDTH 2>&1 >/dev/tty)" + + + + ################## + ## LDAP options ## + ################## + + BACKTITLE="Re2o setup - configuration of the LDAP" + + # Prompt to choose the LDAP location + TITLE="LDAP location" + MENU="Where would you like to install the LDAP ? + * 'Local' will setup everything automatically but is not recommended for production + * 'Remote' will ask you to manually perform some setup commands on the remote server" + OPTIONS=(1 "Local" + 2 "Remote") + ldap_is_local="$(dialog --clear --backtitle "$BACKTITLE" \ + --title "$TITLE" --menu "$MENU" \ + $HEIGHT $WIDTH $CHOICE_HEIGHT "${OPTIONS[@]}" 2>&1 >/dev/tty)" + + # Prompt to enter the LDAP domain extension + TITLE="Domain extension" + INPUTBOX="The local domain extension to use (e.g. 'example.net'). This is used in the LDAP configuration." + extension_locale="$(dialog --clear --backtitle "$BACKTITLE" \ + --title "$TITLE" --inputbox "$INPUTBOX" \ + $HEIGHT $WIDTH 2>&1 >/dev/tty)" + + # Building the DN of the LDAP from the extension + IFS='.' read -a extension_locale_array <<< $extension_locale + for i in "${extension_locale_array[@]}" + do + ldap_dn+="dc=$i," + done + ldap_dn="${ldap_dn::-1}" + + if [ "$ldap_is_local" == 2 ]; then + # Prompt to enter the remote LDAP hostname + TITLE="LDAP hostname" + INPUTBOX="The hostname of the remote LDAP" + ldap_host="$(dialog --clear --backtitle "$BACKTITLE" \ + --title "$TITLE" --inputbox "$INPUTBOX" \ + $HEIGHT $WIDTH 2>&1 >/dev/tty)" + + # Prompt to choose if TLS should be activated or not for the LDAP + TITLE="TLS on LDAP" + MENU="Would you like to activate TLS for communicating with the remote LDAP ?" + OPTIONS=(1 "Yes" + 2 "No") + ldap_tls="$(dialog --clear --backtitle "$BACKTITLE" \ + --title "$TITLE" --MENU "$MENU" \ + $HEIGHT $WIDTH $CHOICE_HEIGHT "${OPTIONS[@]}" 2>&1 >/dev/tty)" + + # Prompt to enter the admin's CN of the remote LDAP + TITLE="CN of amdin user" + INPUTBOX="The CN entry for the admin user of the remote LDAP" + ldap_cn="$(dialog --clear --backtitle "$BACKTITLE" \ + --title "$TITLE" --inputbox "$INPUTBOX" \ + $HEIGHT $WIDTH 2>&1 >/dev/tty)" + else + ldap_cn="cn=admin," + ldap_cn+="$ldap_dn" + ldap_host="localhost" + ldap_tls=2 + fi + + # Prompt to enter the LDAP password + TITLE="LDAP password" + INPUTBOX="The password to access the LDAP" + ldap_password="$(dialog --clear --backtitle "$BACKTITLE" \ + --title "$TITLE" --inputbox "$INPUTBOX" \ + $HEIGHT $WIDTH 2>&1 >/dev/tty)" + + + + ######################### + ## Mail server options ## + ######################### + + BACKTITLE="Re2o setup - configuration of the mail server" + + # Prompt to enter the hostname of the mail server + TITLE="Mail server hostname" + INPUTBOX="The hostname of the mail server to use" + email_host="$(dialog --clear --backtitle "$BACKTITLE" \ + --title "$TITLE" --inputbox "$TITLE" \ + $HEIGHT $WIDTH 2>&1 >/dev/tty)" + + # Prompt to choose the port of the mail server + TITLE="Mail server port" + MENU="Which port (thus which protocol) to use to contact the mail server" + OPTIONS=(25 "SMTP" + 465 "SMTPS" + 587 "Submission") + email_port="$(dialog --clear --backtitle "$BACKTITLE" \ + --title "$TITLE" --menu "$MENU" \ + $HEIGHT $WIDTH $CHOICE_HEIGHT "${OPTIONS[@]}" 2>&1 >/dev/tty)" + + + + ############################### + ## End of configuration step ## + ############################### + + BACKTITLE="Re2o setup" + + # Prompt to inform the config setup is over + TITLE="End of configuration step" + MSGBOX="The configuration step is now finished. The script will now perform the following actions: + * Install the required packages + * Install and setup the requested database if 'local' has been selected + * Install and setup the ldap if 'local' has been selected + * Write a local version of 'settings_local.py' file with the previously given informations + * Apply the Django migrations for the project + * Install and setup freeradius" + end_config="$(dialog --clear --backtitle "$BACKTITLE" \ + --title "$TITLE" --msgbox "$MSGBOX" \ + $HEIGHT $WIDTH 2>&1 >/dev/tty)" + + clear + + + + ################################ + ## Perform the actual actions ## + ################################ + + install_radius_requirements + + configure_radius + + install_database "$sql_bdd_type" "$sql_is_local" "$sql_name" "$sql_login" "$sql_password" + + install_ldap "$ldap_is_local" "$ldap_password" "$ldap_dn" + + + write_settings_file "$sql_bdd_type" "$sql_host" "$sql_name" "$sql_login" "$sql_password" \ + "$ldap_cn" "$ldap_tls" "$ldap_password" "$ldap_host" "$ldap_dn" \ + "$email_host" "$email_port" "$extension_locale" "$url_server" + + update_django + + + ########################### + ## End of the setup step ## + ########################### + + BACKTITLE="Re2o setup" + + # Prompt to inform the installation process is over + TITLE="End of the setup" + MSGBOX="You can now use your RADIUS server." + end="$(dialog --clear --backtitle "$BACKTITLE" \ + --title "$TITLE" --msgbox "$MSGBOX" \ + $HEIGHT $WIDTH 2>&1 >/dev/tty)" +} + + + + interactive_update_settings() { ### Usage: interactvie_update_settings @@ -763,8 +1070,13 @@ main_function() { echo " * {help} ---------- Display this quick usage documentation" echo " * {setup} --------- Launch the full interactive guide to setup entirely" echo " re2o from scratch" - echo " * {update} -------- Collect frontend statics, install the missing APT and copy LaTeX templates files" - echo " and pip packages and apply the migrations to the DB" + echo " * {setup-radius} -- Launch the full interactive guide to setup entirely" + echo " re2o radius from scratch" + echo " * {update} -------- Collect frontend statics, install the missing APT" + echo " and pip packages, copy LaTeX templates files" + echo " and apply the migrations to the DB" + echo " * {update-radius} - Update radius apt and pip packages and copy radius" + echo " configuration files to their proper location." echo " * {update-django} - Apply Django migration and collect frontend statics" echo " * {copy-template-files} - Copy LaTeX templates files to media/templates" echo " * {update-packages} Install the missing APT and pip packages" @@ -796,12 +1108,21 @@ main_function() { interactive_guide ;; + setup-radius ) + interactive_radius_guide + ;; + update ) install_requirements copy_templates_files update_django ;; + update-radius ) + install_radius_requirements + configure_radius + ;; + copy-templates-files ) copy_templates_files ;; @@ -840,7 +1161,7 @@ main_function() { db_username="re2o" fi install_database "$db_engine_type" 1 "$db_name" "$db_username" "$db_password" - update-django + update_django else echo "Invalid arguments !" echo "Usage: install_re2o setup-db [] [] []" diff --git a/logs/__init__.py b/logs/__init__.py index 20338670..32f6fa7d 100644 --- a/logs/__init__.py +++ b/logs/__init__.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify diff --git a/logs/acl.py b/logs/acl.py index ee9a7b1b..42000ea8 100644 --- a/logs/acl.py +++ b/logs/acl.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -38,7 +38,10 @@ def can_view(user): 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 _("You don't have the right to view this" - " application.") - + can = user.has_module_perms("admin") + return ( + can, + None if can else _("You don't have the right to view this" + " application."), + "admin", + ) diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index 807bda76..f04f6bef 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-01-08 23:16+0100\n" +"POT-Creation-Date: 2019-11-19 23:43+0100\n" "PO-Revision-Date: 2018-06-23 16:01+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -30,111 +30,115 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: acl.py:42 +#: logs/acl.py:44 msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: templates/logs/aff_stats_logs.html:36 +#: logs/templates/logs/aff_stats_logs.html:36 msgid "Edited object" msgstr "Objet modifié" -#: templates/logs/aff_stats_logs.html:37 -#: templates/logs/aff_stats_models.html:32 +#: logs/templates/logs/aff_stats_logs.html:37 +#: logs/templates/logs/aff_stats_models.html:32 msgid "Object type" msgstr "Type d'objet" -#: templates/logs/aff_stats_logs.html:38 +#: logs/templates/logs/aff_stats_logs.html:38 msgid "Edited by" msgstr "Modifié par" -#: templates/logs/aff_stats_logs.html:40 +#: logs/templates/logs/aff_stats_logs.html:40 msgid "Date of editing" msgstr "Date de modification" -#: templates/logs/aff_stats_logs.html:42 +#: logs/templates/logs/aff_stats_logs.html:42 msgid "Comment" msgstr "Commentaire" -#: templates/logs/aff_stats_logs.html:58 templates/logs/aff_summary.html:62 -#: templates/logs/aff_summary.html:85 templates/logs/aff_summary.html:104 -#: templates/logs/aff_summary.html:128 templates/logs/aff_summary.html:147 +#: logs/templates/logs/aff_stats_logs.html:58 +#: logs/templates/logs/aff_summary.html:62 +#: logs/templates/logs/aff_summary.html:85 +#: logs/templates/logs/aff_summary.html:104 +#: logs/templates/logs/aff_summary.html:128 +#: logs/templates/logs/aff_summary.html:147 msgid "Cancel" msgstr "Annuler" -#: templates/logs/aff_stats_models.html:29 +#: logs/templates/logs/aff_stats_models.html:29 #, python-format msgid "Statistics of the set %(key)s" msgstr "Statistiques de l'ensemble %(key)s" -#: templates/logs/aff_stats_models.html:33 +#: logs/templates/logs/aff_stats_models.html:33 msgid "Number of stored entries" msgstr "Nombre d'entrées enregistrées" -#: templates/logs/aff_stats_users.html:31 +#: logs/templates/logs/aff_stats_users.html:31 #, python-format msgid "Statistics per %(key_dict)s of %(key)s" msgstr "Statistiques par %(key_dict)s de %(key)s" -#: templates/logs/aff_stats_users.html:34 +#: logs/templates/logs/aff_stats_users.html:34 #, python-format msgid "Number of %(key)s per %(key_dict)s" msgstr "Nombre de %(key)s par %(key_dict)s" -#: templates/logs/aff_stats_users.html:35 +#: logs/templates/logs/aff_stats_users.html:35 msgid "Rank" msgstr "Rang" -#: templates/logs/aff_summary.html:37 +#: logs/templates/logs/aff_summary.html:37 msgid "Date" msgstr "Date" -#: templates/logs/aff_summary.html:39 +#: logs/templates/logs/aff_summary.html:39 msgid "Editing" msgstr "Modification" -#: templates/logs/aff_summary.html:48 +#: logs/templates/logs/aff_summary.html:48 #, python-format msgid "%(username)s has banned" msgstr "%(username)s a banni" -#: templates/logs/aff_summary.html:52 templates/logs/aff_summary.html:75 +#: logs/templates/logs/aff_summary.html:52 +#: logs/templates/logs/aff_summary.html:75 msgid "No reason" msgstr "Aucun motif" -#: templates/logs/aff_summary.html:71 +#: logs/templates/logs/aff_summary.html:71 #, python-format msgid "%(username)s has graciously authorised" msgstr "%(username)s a autorisé gracieusement" -#: templates/logs/aff_summary.html:94 +#: logs/templates/logs/aff_summary.html:94 #, python-format msgid "%(username)s has updated" msgstr "%(username)s a mis à jour" -#: templates/logs/aff_summary.html:113 +#: logs/templates/logs/aff_summary.html:113 #, python-format msgid "%(username)s has sold %(number)sx %(name)s" msgstr "%(username)s a vendu %(number)sx %(name)s" -#: templates/logs/aff_summary.html:116 -msgid " to" -msgstr " à" +#: logs/templates/logs/aff_summary.html:116 +msgid "to" +msgstr "à" -#: templates/logs/aff_summary.html:119 +#: logs/templates/logs/aff_summary.html:119 #, python-format msgid "+%(duration)s months" msgstr "+%(duration)s mois" -#: templates/logs/aff_summary.html:137 +#: logs/templates/logs/aff_summary.html:137 #, python-format msgid "%(username)s has edited an interface of" msgstr "%(username)s a modifié une interface de" -#: templates/logs/delete.html:29 +#: logs/templates/logs/delete.html:29 msgid "Deletion of actions" msgstr "Suppression d'actions" -#: templates/logs/delete.html:35 +#: logs/templates/logs/delete.html:35 #, python-format msgid "" "Warning: are you sure you want to delete this action %(objet_name)s " @@ -143,204 +147,178 @@ msgstr "" "Attention: voulez-vous vraiment supprimer cette action %(objet_name)s " "( %(objet)s ) ?" -#: templates/logs/delete.html:36 +#: logs/templates/logs/delete.html:36 msgid "Confirm" msgstr "Confirmer" -#: templates/logs/index.html:29 templates/logs/stats_general.html:29 -#: templates/logs/stats_logs.html:29 templates/logs/stats_models.html:29 -#: templates/logs/stats_users.html:29 +#: logs/templates/logs/index.html:29 logs/templates/logs/stats_general.html:29 +#: logs/templates/logs/stats_logs.html:29 +#: logs/templates/logs/stats_models.html:29 +#: logs/templates/logs/stats_users.html:29 msgid "Statistics" msgstr "Statistiques" -#: templates/logs/index.html:32 templates/logs/stats_logs.html:32 views.py:414 +#: logs/templates/logs/index.html:32 logs/templates/logs/stats_logs.html:32 +#: logs/views.py:400 msgid "Actions performed" msgstr "Actions effectuées" -#: templates/logs/sidebar.html:33 +#: logs/templates/logs/sidebar.html:33 msgid "Summary" msgstr "Résumé" -#: templates/logs/sidebar.html:37 +#: logs/templates/logs/sidebar.html:37 msgid "Events" msgstr "Évènements" -#: templates/logs/sidebar.html:41 +#: logs/templates/logs/sidebar.html:41 msgid "General" msgstr "Général" -#: templates/logs/sidebar.html:45 +#: logs/templates/logs/sidebar.html:45 msgid "Database" msgstr "Base de données" -#: templates/logs/sidebar.html:49 +#: logs/templates/logs/sidebar.html:49 msgid "Wiring actions" msgstr "Actions de câblage" -#: templates/logs/sidebar.html:53 views.py:336 +#: logs/templates/logs/sidebar.html:53 msgid "Users" msgstr "Utilisateurs" -#: templates/logs/stats_general.html:32 +#: logs/templates/logs/stats_general.html:32 msgid "General statistics" msgstr "Statistiques générales" -#: templates/logs/stats_models.html:32 +#: logs/templates/logs/stats_models.html:32 msgid "Database statistics" msgstr "Statistiques sur la base de données" -#: templates/logs/stats_users.html:32 +#: logs/templates/logs/stats_users.html:32 msgid "Statistics about users" msgstr "Statistiques sur les utilisateurs" -#: views.py:194 +#: logs/views.py:175 msgid "Nonexistent revision." msgstr "Révision inexistante." -#: views.py:197 +#: logs/views.py:178 msgid "The action was deleted." msgstr "L'action a été supprimée." -#: views.py:230 +#: logs/views.py:219 msgid "Category" msgstr "Catégorie" -#: views.py:231 +#: logs/views.py:220 msgid "Number of users (members and clubs)" msgstr "Nombre d'utilisateurs (adhérents et clubs)" -#: views.py:232 +#: logs/views.py:221 msgid "Number of members" msgstr "Nombre d'adhérents" -#: views.py:233 +#: logs/views.py:222 msgid "Number of clubs" msgstr "Nombre de clubs" -#: views.py:237 +#: logs/views.py:226 msgid "Activated users" msgstr "Utilisateurs activés" -#: views.py:245 +#: logs/views.py:232 msgid "Disabled users" msgstr "Utilisateurs désactivés" -#: views.py:253 +#: logs/views.py:238 msgid "Archived users" msgstr "Utilisateurs archivés" -#: views.py:261 +#: logs/views.py:244 +msgid "Fully archived users" +msgstr "Utilisateurs complètement archivés" + +#: logs/views.py:254 msgid "Not yet active users" msgstr "Utilisateurs pas encore actifs" -#: views.py:269 +#: logs/views.py:264 msgid "Contributing members" msgstr "Adhérents cotisants" -#: views.py:275 +#: logs/views.py:270 msgid "Users benefiting from a connection" msgstr "Utilisateurs bénéficiant d'une connexion" -#: views.py:281 +#: logs/views.py:276 msgid "Banned users" msgstr "Utilisateurs bannis" -#: views.py:287 +#: logs/views.py:282 msgid "Users benefiting from a free connection" msgstr "Utilisateurs bénéficiant d'une connexion gratuite" -#: views.py:293 +#: logs/views.py:288 msgid "Active interfaces (with access to the network)" msgstr "Interfaces actives (ayant accès au réseau)" -#: views.py:303 +#: logs/views.py:302 msgid "Active interfaces assigned IPv4" msgstr "Interfaces actives assignées IPv4" -#: views.py:316 +#: logs/views.py:319 msgid "IP range" msgstr "Plage d'IP" -#: views.py:317 +#: logs/views.py:320 msgid "VLAN" msgstr "VLAN" -#: views.py:318 +#: logs/views.py:321 msgid "Total number of IP addresses" msgstr "Nombre total d'adresses IP" -#: views.py:319 +#: logs/views.py:322 msgid "Number of assigned IP addresses" -msgstr "Nombre d'adresses IP non assignées" +msgstr "Nombre d'adresses IP assignées" -#: views.py:320 +#: logs/views.py:323 msgid "Number of IP address assigned to an activated machine" msgstr "Nombre d'adresses IP assignées à une machine activée" -#: views.py:321 -msgid "Number of nonassigned IP addresses" +#: logs/views.py:324 +msgid "Number of unassigned IP addresses" msgstr "Nombre d'adresses IP non assignées" -#: views.py:348 -msgid "Subscriptions" -msgstr "Cotisations" +#: logs/views.py:339 +msgid "Users (members and clubs)" +msgstr "Utilisateurs (adhérents et clubs)" -#: views.py:370 views.py:431 -msgid "Machines" -msgstr "Machines" - -#: views.py:397 +#: logs/views.py:385 msgid "Topology" msgstr "Topologie" -#: views.py:416 +#: logs/views.py:401 msgid "Number of actions" msgstr "Nombre d'actions" -#: views.py:430 views.py:448 views.py:453 views.py:458 views.py:473 -msgid "User" -msgstr "Utilisateur" +#: logs/views.py:426 +msgid "rights" +msgstr "droits" -#: views.py:434 -msgid "Invoice" -msgstr "Facture" +#: logs/views.py:455 +msgid "actions" +msgstr "actions" -#: views.py:437 -msgid "Ban" -msgstr "Bannissement" - -#: views.py:440 -msgid "Whitelist" -msgstr "Accès gracieux" - -#: views.py:443 -msgid "Rights" -msgstr "Droits" - -#: views.py:447 -msgid "School" -msgstr "Établissement" - -#: views.py:452 -msgid "Payment method" -msgstr "Moyen de paiement" - -#: views.py:457 -msgid "Bank" -msgstr "Banque" - -#: views.py:474 -msgid "Action" -msgstr "Action" - -#: views.py:505 +#: logs/views.py:486 msgid "No model found." msgstr "Aucun modèle trouvé." -#: views.py:511 +#: logs/views.py:492 msgid "Nonexistent entry." msgstr "Entrée inexistante." -#: views.py:518 +#: logs/views.py:499 msgid "You don't have the right to access this menu." msgstr "Vous n'avez pas le droit d'accéder à ce menu." diff --git a/logs/templates/logs/aff_stats_general.html b/logs/templates/logs/aff_stats_general.html index 662efa54..48d79b92 100644 --- a/logs/templates/logs/aff_stats_general.html +++ b/logs/templates/logs/aff_stats_general.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/logs/templates/logs/aff_stats_logs.html b/logs/templates/logs/aff_stats_logs.html index 44a937f9..adccc95f 100644 --- a/logs/templates/logs/aff_stats_logs.html +++ b/logs/templates/logs/aff_stats_logs.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/logs/templates/logs/aff_stats_models.html b/logs/templates/logs/aff_stats_models.html index 7809dfdf..93e14109 100644 --- a/logs/templates/logs/aff_stats_models.html +++ b/logs/templates/logs/aff_stats_models.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/logs/templates/logs/aff_stats_users.html b/logs/templates/logs/aff_stats_users.html index 0ea6a426..4978b2ad 100644 --- a/logs/templates/logs/aff_stats_users.html +++ b/logs/templates/logs/aff_stats_users.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/logs/templates/logs/aff_summary.html b/logs/templates/logs/aff_summary.html index 3c43e2ac..31834a2d 100644 --- a/logs/templates/logs/aff_summary.html +++ b/logs/templates/logs/aff_summary.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -43,7 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% for v in versions_list %} {% if v.version.content_type.model == 'ban' %} - {{ v.datetime }} + {{ v.datetime|date:"j/m/y H:i:s" }} {% blocktrans with username=v.username %}{{ username }} has banned{% endblocktrans %} {{ v.version.object.user.get_username }} @@ -66,7 +66,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% elif v.version.content_type.model == 'whitelist' %} - {{ v.datetime }} + {{ v.datetime|date:"j/m/y H:i:s" }} {% blocktrans with username=v.username %}{{ username }} has graciously authorised{% endblocktrans %} {{ v.version.object.user.get_username }} @@ -89,7 +89,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% elif v.version.content_type.model == 'user' %} - {{ v.datetime }} + {{ v.datetime|date:"j/m/y H:i:s" }} {% blocktrans with username=v.username %}{{ username }} has updated{% endblocktrans %} {{ v.version.object.get_username }} @@ -108,12 +108,12 @@ with this program; if not, write to the Free Software Foundation, Inc., {% elif v.version.content_type.model == 'vente' %} - {{ v.datetime }} + {{ v.datetime|date:"j/m/y H:i:s" }} {% blocktrans with username=v.username number=v.version.object.number name=v.version.object.name %}{{ username }} has sold {{ number }}x {{ name }}{% endblocktrans %} {% with invoice=v.version.object.facture %} {% if invoice|is_facture %} - {% trans " to" %} + {% trans "to" %} {{ v.version.object.facture.facture.user.get_username }} {% if v.version.object.iscotisation %} ({% blocktrans with duration=v.version.object.duration %}+{{ duration }} months{% endblocktrans %}) @@ -132,7 +132,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% elif v.version.content_type.model == 'interface' %} - {{ v.datetime }} + {{ v.datetime|date:"j/m/y H:i:s" }} {% blocktrans with username=v.username %}{{ username }} has edited an interface of{% endblocktrans %} {{ v.version.object.machine.user.get_username }} diff --git a/logs/templates/logs/delete.html b/logs/templates/logs/delete.html index a8f6b52f..3bd0d638 100644 --- a/logs/templates/logs/delete.html +++ b/logs/templates/logs/delete.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/logs/templates/logs/index.html b/logs/templates/logs/index.html index 3bd61b40..cc446f5b 100644 --- a/logs/templates/logs/index.html +++ b/logs/templates/logs/index.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/logs/templates/logs/sidebar.html b/logs/templates/logs/sidebar.html index e997abd5..7f7d6cbf 100644 --- a/logs/templates/logs/sidebar.html +++ b/logs/templates/logs/sidebar.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/logs/templates/logs/stats_general.html b/logs/templates/logs/stats_general.html index 96d5612c..e7021716 100644 --- a/logs/templates/logs/stats_general.html +++ b/logs/templates/logs/stats_general.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/logs/templates/logs/stats_logs.html b/logs/templates/logs/stats_logs.html index df9708b1..e3314cb2 100644 --- a/logs/templates/logs/stats_logs.html +++ b/logs/templates/logs/stats_logs.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/logs/templates/logs/stats_models.html b/logs/templates/logs/stats_models.html index ddc66c15..03b82b37 100644 --- a/logs/templates/logs/stats_models.html +++ b/logs/templates/logs/stats_models.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/logs/templates/logs/stats_users.html b/logs/templates/logs/stats_users.html index d55d1e52..de706833 100644 --- a/logs/templates/logs/stats_users.html +++ b/logs/templates/logs/stats_users.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/logs/templatetags/__init__.py b/logs/templatetags/__init__.py index cd256e09..b8b9a128 100644 --- a/logs/templatetags/__init__.py +++ b/logs/templatetags/__init__.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify diff --git a/logs/templatetags/logs_extra.py b/logs/templatetags/logs_extra.py index 813a577f..c436c1fa 100644 --- a/logs/templatetags/logs_extra.py +++ b/logs/templatetags/logs_extra.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -34,12 +34,14 @@ def classname(obj): """ Returns the object class name """ return obj.__class__.__name__ + @register.filter def is_facture(baseinvoice): """Returns True if a baseinvoice has a `Facture` child.""" - return hasattr(baseinvoice, 'facture') + return hasattr(baseinvoice, "facture") -@register.inclusion_tag('buttons/history.html') + +@register.inclusion_tag("buttons/history.html") def history_button(instance, text=False, html_class=True): """Creates the correct history button for an instance. @@ -51,9 +53,9 @@ def history_button(instance, text=False, html_class=True): """ return { - 'application': instance._meta.app_label, - 'name': instance._meta.model_name, - 'id': instance.id, - 'text': text, - 'class': html_class, + "application": instance._meta.app_label, + "name": instance._meta.model_name, + "id": instance.id, + "text": text, + "class": html_class, } diff --git a/logs/tests.py b/logs/tests.py index 65679fa3..51ef33ae 100644 --- a/logs/tests.py +++ b/logs/tests.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify diff --git a/logs/urls.py b/logs/urls.py index 9398cfe4..8fa0f469 100644 --- a/logs/urls.py +++ b/logs/urls.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -30,18 +30,20 @@ from django.conf.urls import url from . import views urlpatterns = [ - url(r'^$', views.index, name='index'), - url(r'^stats_logs$', views.stats_logs, name='stats-logs'), - url(r'^revert_action/(?P[0-9]+)$', - views.revert_action, - name='revert-action'), - url(r'^stats_general/$', views.stats_general, name='stats-general'), - url(r'^stats_models/$', views.stats_models, name='stats-models'), - url(r'^stats_users/$', views.stats_users, name='stats-users'), - url(r'^stats_actions/$', views.stats_actions, name='stats-actions'), + url(r"^$", views.index, name="index"), + url(r"^stats_logs$", views.stats_logs, name="stats-logs"), url( - r'(?P\w+)/(?P\w+)/(?P[0-9]+)$', + r"^revert_action/(?P[0-9]+)$", + views.revert_action, + name="revert-action", + ), + url(r"^stats_general/$", views.stats_general, name="stats-general"), + url(r"^stats_models/$", views.stats_models, name="stats-models"), + url(r"^stats_users/$", views.stats_users, name="stats-users"), + url(r"^stats_actions/$", views.stats_actions, name="stats-actions"), + url( + r"(?P\w+)/(?P\w+)/(?P[0-9]+)$", views.history, - name='history', + name="history", ), ] diff --git a/logs/views.py b/logs/views.py index a54edd56..7c509134 100644 --- a/logs/views.py +++ b/logs/views.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2018 Gabriel Détraz -# Copyright © 2018 Goulven Kermarec +# Copyright © 2018 Lara Kermarec # Copyright © 2018 Augustin Lemesle # Copyright © 2018 Hugo Levy-Falk # @@ -60,16 +60,9 @@ from users.models import ( Ban, Whitelist, Adherent, - Club -) -from cotisations.models import ( - Facture, - Vente, - Article, - Banque, - Paiement, - Cotisation + Club, ) +from cotisations.models import Facture, Vente, Article, Banque, Paiement, Cotisation from machines.models import ( Machine, MachineType, @@ -84,7 +77,7 @@ from machines.models import ( Nas, SOA, Mx, - Ns + Ns, ) from topologie.models import ( Switch, @@ -93,7 +86,7 @@ from topologie.models import ( Stack, ModelSwitch, ConstructorSwitch, - AccessPoint + AccessPoint, ) from preferences.models import GeneralOption from re2o.views import form @@ -104,37 +97,25 @@ from re2o.utils import ( all_adherent, all_active_assigned_interfaces_count, all_active_interfaces_count, -) -from re2o.base import ( - re2o_paginator, - SortTable -) -from re2o.acl import ( - can_view_all, - can_view_app, - can_edit_history, ) +from re2o.base import re2o_paginator, SortTable +from re2o.acl import can_view_all, can_view_app, can_edit_history @login_required -@can_view_app('logs') +@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)""" - pagination_number = GeneralOption.get_cached_value('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'] + content_type_filter = ["ban", "whitelist", "vente", "interface", "user"] # Select only wanted versions versions = Version.objects.filter( - content_type__in=ContentType.objects.filter( - model__in=content_type_filter - ) - ).select_related('revision') + content_type__in=ContentType.objects.filter(model__in=content_type_filter) + ).select_related("revision") versions = SortTable.sort( - versions, - request.GET.get('col'), - request.GET.get('order'), - SortTable.LOGS_INDEX + versions, request.GET.get("col"), request.GET.get("order"), SortTable.LOGS_INDEX ) versions = re2o_paginator(request, versions, pagination_number) # Force to have a list instead of QuerySet @@ -146,22 +127,21 @@ def index(request): if versions.object_list[i].object: version = versions.object_list[i] versions.object_list[i] = { - 'rev_id': version.revision.id, - 'comment': version.revision.comment, - 'datetime': version.revision.date_created.strftime( - '%d/%m/%y %H:%M:%S' - ), - 'username': - version.revision.user.get_username() - if version.revision.user else '?', - 'user_id': version.revision.user_id, - 'version': version} + "rev_id": version.revision.id, + "comment": version.revision.comment, + "datetime": version.revision.date_created, + "username": version.revision.user.get_username() + if version.revision.user + else "?", + "user_id": version.revision.user_id, + "version": version, + } else: to_remove.insert(0, i) # Remove all tagged invalid items for i in to_remove: versions.object_list.pop(i) - return render(request, 'logs/index.html', {'versions_list': versions}) + return render(request, "logs/index.html", {"versions_list": versions}) @login_required @@ -169,19 +149,20 @@ def index(request): def stats_logs(request): """Affiche l'ensemble des logs et des modifications sur les objets, classés par date croissante, en vrac""" - pagination_number = GeneralOption.get_cached_value('pagination_number') - revisions = Revision.objects.all().select_related('user')\ - .prefetch_related('version_set__object') + pagination_number = GeneralOption.get_cached_value("pagination_number") + revisions = ( + Revision.objects.all() + .select_related("user") + .prefetch_related("version_set__object") + ) revisions = SortTable.sort( revisions, - request.GET.get('col'), - request.GET.get('order'), - SortTable.LOGS_STATS_LOGS + request.GET.get("col"), + request.GET.get("order"), + SortTable.LOGS_STATS_LOGS, ) revisions = re2o_paginator(request, revisions, pagination_number) - return render(request, 'logs/stats_logs.html', { - 'revisions_list': revisions - }) + return render(request, "logs/stats_logs.html", {"revisions_list": revisions}) @login_required @@ -195,11 +176,12 @@ def revert_action(request, revision_id): if request.method == "POST": revision.revert() messages.success(request, _("The action was deleted.")) - return redirect(reverse('logs:index')) - return form({ - 'objet': revision, - 'objet_name': revision.__class__.__name__ - }, 'logs/delete.html', request) + return redirect(reverse("logs:index")) + return form( + {"objet": revision, "objet_name": revision.__class__.__name__}, + "logs/delete.html", + request, + ) @login_required @@ -209,274 +191,273 @@ def stats_general(request): range, et les statistiques générales sur les users : users actifs, cotisants, activés, archivés, etc""" ip_dict = dict() - for ip_range in IpType.objects.select_related('vlan').all(): + for ip_range in IpType.objects.select_related("vlan").all(): all_ip = IpList.objects.filter(ip_type=ip_range) used_ip = Interface.objects.filter(ipv4__in=all_ip).count() - active_ip = all_active_assigned_interfaces_count().filter( - ipv4__in=IpList.objects.filter(ip_type=ip_range) - ).count() - ip_dict[ip_range] = [ip_range, ip_range.vlan, all_ip.count(), - used_ip, active_ip, all_ip.count()-used_ip] - _all_adherent = all_adherent() - _all_has_access = all_has_access() + active_ip = ( + all_active_assigned_interfaces_count() + .filter(ipv4__in=IpList.objects.filter(ip_type=ip_range)) + .count() + ) + ip_dict[ip_range] = [ + ip_range, + ip_range.vlan, + all_ip.count(), + used_ip, + active_ip, + all_ip.count() - used_ip, + ] + _all_adherent = all_adherent(including_asso=False) + _all_has_access = all_has_access(including_asso=False) _all_baned = all_baned() _all_whitelisted = all_whitelisted() _all_active_interfaces_count = all_active_interfaces_count() - _all_active_assigned_interfaces_count = \ - all_active_assigned_interfaces_count() + _all_active_assigned_interfaces_count = all_active_assigned_interfaces_count() stats = [ - [ # First set of data (about users) - [ # Headers + [ # First set of data (about users) + [ # Headers _("Category"), _("Number of users (members and clubs)"), _("Number of members"), - _("Number of clubs") + _("Number of clubs"), ], - { # Data - 'active_users': [ + { # Data + "active_users": [ _("Activated users"), User.objects.filter(state=User.STATE_ACTIVE).count(), - (Adherent.objects - .filter(state=Adherent.STATE_ACTIVE) - .count()), - Club.objects.filter(state=Club.STATE_ACTIVE).count() + (Adherent.objects.filter(state=Adherent.STATE_ACTIVE).count()), + Club.objects.filter(state=Club.STATE_ACTIVE).count(), ], - 'inactive_users': [ + "inactive_users": [ _("Disabled users"), User.objects.filter(state=User.STATE_DISABLED).count(), - (Adherent.objects - .filter(state=Adherent.STATE_DISABLED) - .count()), - Club.objects.filter(state=Club.STATE_DISABLED).count() + (Adherent.objects.filter(state=Adherent.STATE_DISABLED).count()), + Club.objects.filter(state=Club.STATE_DISABLED).count(), ], - 'archive_users': [ + "archive_users": [ _("Archived users"), User.objects.filter(state=User.STATE_ARCHIVE).count(), - (Adherent.objects - .filter(state=Adherent.STATE_ARCHIVE) - .count()), - Club.objects.filter(state=Club.STATE_ARCHIVE).count() + (Adherent.objects.filter(state=Adherent.STATE_ARCHIVE).count()), + Club.objects.filter(state=Club.STATE_ARCHIVE).count(), ], - 'not_active_users': [ + "full_archive_users": [ + _("Fully archived users"), + User.objects.filter(state=User.STATE_FULL_ARCHIVE).count(), + ( + Adherent.objects.filter( + state=Adherent.STATE_FULL_ARCHIVE + ).count() + ), + Club.objects.filter(state=Club.STATE_FULL_ARCHIVE).count(), + ], + "not_active_users": [ _("Not yet active users"), User.objects.filter(state=User.STATE_NOT_YET_ACTIVE).count(), - (Adherent.objects - .filter(state=Adherent.STATE_NOT_YET_ACTIVE) - .count()), - Club.objects.filter(state=Club.STATE_NOT_YET_ACTIVE).count() + ( + Adherent.objects.filter( + state=Adherent.STATE_NOT_YET_ACTIVE + ).count() + ), + Club.objects.filter(state=Club.STATE_NOT_YET_ACTIVE).count(), ], - 'adherent_users': [ + "adherent_users": [ _("Contributing members"), _all_adherent.count(), _all_adherent.exclude(adherent__isnull=True).count(), - _all_adherent.exclude(club__isnull=True).count() + _all_adherent.exclude(club__isnull=True).count(), ], - 'connexion_users': [ + "connexion_users": [ _("Users benefiting from a connection"), _all_has_access.count(), _all_has_access.exclude(adherent__isnull=True).count(), - _all_has_access.exclude(club__isnull=True).count() + _all_has_access.exclude(club__isnull=True).count(), ], - 'ban_users': [ + "ban_users": [ _("Banned users"), _all_baned.count(), _all_baned.exclude(adherent__isnull=True).count(), - _all_baned.exclude(club__isnull=True).count() + _all_baned.exclude(club__isnull=True).count(), ], - 'whitelisted_user': [ + "whitelisted_user": [ _("Users benefiting from a free connection"), _all_whitelisted.count(), _all_whitelisted.exclude(adherent__isnull=True).count(), - _all_whitelisted.exclude(club__isnull=True).count() + _all_whitelisted.exclude(club__isnull=True).count(), ], - 'actives_interfaces': [ + "actives_interfaces": [ _("Active interfaces (with access to the network)"), _all_active_interfaces_count.count(), - (_all_active_interfaces_count - .exclude(machine__user__adherent__isnull=True) - .count()), - (_all_active_interfaces_count - .exclude(machine__user__club__isnull=True) - .count()) + ( + _all_active_interfaces_count.exclude( + machine__user__adherent__isnull=True + ).count() + ), + ( + _all_active_interfaces_count.exclude( + machine__user__club__isnull=True + ).count() + ), ], - 'actives_assigned_interfaces': [ + "actives_assigned_interfaces": [ _("Active interfaces assigned IPv4"), _all_active_assigned_interfaces_count.count(), - (_all_active_assigned_interfaces_count - .exclude(machine__user__adherent__isnull=True) - .count()), - (_all_active_assigned_interfaces_count - .exclude(machine__user__club__isnull=True) - .count()) - ] - } + ( + _all_active_assigned_interfaces_count.exclude( + machine__user__adherent__isnull=True + ).count() + ), + ( + _all_active_assigned_interfaces_count.exclude( + machine__user__club__isnull=True + ).count() + ), + ], + }, ], - [ # Second set of data (about ip adresses) - [ # Headers + [ # Second set of data (about ip adresses) + [ # Headers _("IP range"), _("VLAN"), _("Total number of IP addresses"), _("Number of assigned IP addresses"), _("Number of IP address assigned to an activated machine"), - _("Number of nonassigned IP addresses") + _("Number of unassigned IP addresses"), ], - ip_dict # Data already prepared - ] + ip_dict, # Data already prepared + ], ] - return render(request, 'logs/stats_general.html', {'stats_list': stats}) + return render(request, "logs/stats_general.html", {"stats_list": stats}) @login_required -@can_view_app('users', 'cotisations', 'machines', 'topologie') +@can_view_app("users", "cotisations", "machines", "topologie") def stats_models(request): """Statistiques générales, affiche les comptages par models: nombre d'users, d'écoles, de droits, de bannissements, de factures, de ventes, de banque, de machines, etc""" stats = { - _("Users"): { - 'users': [User._meta.verbose_name, User.objects.count()], - 'adherents': [Adherent._meta.verbose_name, Adherent.objects.count()], - 'clubs': [Club._meta.verbose_name, Club.objects.count()], - 'serviceuser': [ServiceUser._meta.verbose_name, - ServiceUser.objects.count()], - 'school': [School._meta.verbose_name, School.objects.count()], - 'listright': [ListRight._meta.verbose_name, ListRight.objects.count()], - 'listshell': [ListShell._meta.verbose_name, ListShell.objects.count()], - 'ban': [Ban._meta.verbose_name, Ban.objects.count()], - 'whitelist': [Whitelist._meta.verbose_name, Whitelist.objects.count()] + _("Users (members and clubs)"): { + "users": [User._meta.verbose_name, User.objects.count()], + "adherents": [Adherent._meta.verbose_name, Adherent.objects.count()], + "clubs": [Club._meta.verbose_name, Club.objects.count()], + "serviceuser": [ + ServiceUser._meta.verbose_name, + ServiceUser.objects.count(), + ], + "school": [School._meta.verbose_name, School.objects.count()], + "listright": [ListRight._meta.verbose_name, ListRight.objects.count()], + "listshell": [ListShell._meta.verbose_name, ListShell.objects.count()], + "ban": [Ban._meta.verbose_name, Ban.objects.count()], + "whitelist": [Whitelist._meta.verbose_name, Whitelist.objects.count()], }, - _("Subscriptions"): { - 'factures': [ - Facture._meta.verbose_name, - Facture.objects.count() - ], - 'vente': [ - Vente._meta.verbose_name, - Vente.objects.count() - ], - 'cotisation': [ - Cotisation._meta.verbose_name, - Cotisation.objects.count() - ], - 'article': [ - Article._meta.verbose_name, - Article.objects.count() - ], - 'banque': [ - Banque._meta.verbose_name, - Banque.objects.count() - ], + Cotisation._meta.verbose_name_plural.title(): { + "factures": [Facture._meta.verbose_name, Facture.objects.count()], + "vente": [Vente._meta.verbose_name, Vente.objects.count()], + "cotisation": [Cotisation._meta.verbose_name, Cotisation.objects.count()], + "article": [Article._meta.verbose_name, Article.objects.count()], + "banque": [Banque._meta.verbose_name, Banque.objects.count()], }, - _("Machines"): { - 'machine': [Machine._meta.verbose_name, - Machine.objects.count()], - 'typemachine': [MachineType._meta.verbose_name, - MachineType.objects.count()], - 'typeip': [IpType._meta.verbose_name, - IpType.objects.count()], - 'extension': [Extension._meta.verbose_name, - Extension.objects.count()], - 'interface': [Interface._meta.verbose_name, - Interface.objects.count()], - 'alias': [Domain._meta.verbose_name, - Domain.objects.exclude(cname=None).count()], - 'iplist': [IpList._meta.verbose_name, - IpList.objects.count()], - 'service': [Service._meta.verbose_name, - Service.objects.count()], - 'ouvertureportlist': [ + Machine._meta.verbose_name_plural.title(): { + "machine": [Machine._meta.verbose_name, Machine.objects.count()], + "typemachine": [ + MachineType._meta.verbose_name, + MachineType.objects.count(), + ], + "typeip": [IpType._meta.verbose_name, IpType.objects.count()], + "extension": [Extension._meta.verbose_name, Extension.objects.count()], + "interface": [Interface._meta.verbose_name, Interface.objects.count()], + "alias": [ + Domain._meta.verbose_name, + Domain.objects.exclude(cname=None).count(), + ], + "iplist": [IpList._meta.verbose_name, IpList.objects.count()], + "service": [Service._meta.verbose_name, Service.objects.count()], + "ouvertureportlist": [ OuverturePortList._meta.verbose_name, - OuverturePortList.objects.count() + OuverturePortList.objects.count(), ], - 'vlan': [Vlan._meta.verbose_name, Vlan.objects.count()], - 'SOA': [SOA._meta.verbose_name, SOA.objects.count()], - 'Mx': [Mx._meta.verbose_name, Mx.objects.count()], - 'Ns': [Ns._meta.verbose_name, Ns.objects.count()], - 'nas': [Nas._meta.verbose_name, Nas.objects.count()], + "vlan": [Vlan._meta.verbose_name, Vlan.objects.count()], + "SOA": [SOA._meta.verbose_name, SOA.objects.count()], + "Mx": [Mx._meta.verbose_name, Mx.objects.count()], + "Ns": [Ns._meta.verbose_name, Ns.objects.count()], + "nas": [Nas._meta.verbose_name, Nas.objects.count()], }, _("Topology"): { - 'switch': [Switch._meta.verbose_name, - Switch.objects.count()], - 'bornes': [AccessPoint._meta.verbose_name, - AccessPoint.objects.count()], - 'port': [Port._meta.verbose_name, Port.objects.count()], - 'chambre': [Room._meta.verbose_name, Room.objects.count()], - 'stack': [Stack._meta.verbose_name, Stack.objects.count()], - 'modelswitch': [ + "switch": [Switch._meta.verbose_name, Switch.objects.count()], + "bornes": [AccessPoint._meta.verbose_name, AccessPoint.objects.count()], + "port": [Port._meta.verbose_name, Port.objects.count()], + "chambre": [Room._meta.verbose_name, Room.objects.count()], + "stack": [Stack._meta.verbose_name, Stack.objects.count()], + "modelswitch": [ ModelSwitch._meta.verbose_name, - ModelSwitch.objects.count() + ModelSwitch.objects.count(), ], - 'constructorswitch': [ + "constructorswitch": [ ConstructorSwitch._meta.verbose_name, - ConstructorSwitch.objects.count() + ConstructorSwitch.objects.count(), ], }, - _("Actions performed"): - { - 'revision': [_("Number of actions"), Revision.objects.count()], + _("Actions performed"): { + "revision": [_("Number of actions"), Revision.objects.count()] }, } - return render(request, 'logs/stats_models.html', {'stats_list': stats}) + return render(request, "logs/stats_models.html", {"stats_list": stats}) @login_required -@can_view_app('users') +@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, de moyens de paiements par user, de banque par user, de bannissement par user, etc""" stats = { - _("User"): { - _("Machines"): User.objects.annotate( - num=Count('machine') - ).order_by('-num')[:10], - _("Invoice"): User.objects.annotate( - num=Count('facture') - ).order_by('-num')[:10], - _("Ban"): User.objects.annotate( - num=Count('ban') - ).order_by('-num')[:10], - _("Whitelist"): User.objects.annotate( - num=Count('whitelist') - ).order_by('-num')[:10], - _("Rights"): User.objects.annotate( - num=Count('groups') - ).order_by('-num')[:10], + User._meta.verbose_name: { + Machine._meta.verbose_name_plural: User.objects.annotate(num=Count("machine")).order_by("-num")[ + :10 + ], + Facture._meta.verbose_name_plural: User.objects.annotate(num=Count("facture")).order_by("-num")[ + :10 + ], + Ban._meta.verbose_name_plural: User.objects.annotate(num=Count("ban")).order_by("-num")[:10], + Whitelist._meta.verbose_name_plural: User.objects.annotate(num=Count("whitelist")).order_by( + "-num" + )[:10], + _("rights"): User.objects.annotate(num=Count("groups")).order_by("-num")[ + :10 + ], }, - _("School"): { - _("User"): School.objects.annotate( - num=Count('user') - ).order_by('-num')[:10], + School._meta.verbose_name: { + User._meta.verbose_name_plural: School.objects.annotate(num=Count("user")).order_by("-num")[:10] }, - _("Payment method"): { - _("User"): Paiement.objects.annotate( - num=Count('facture') - ).order_by('-num')[:10], + Paiement._meta.verbose_name: { + User._meta.verbose_name_plural: Paiement.objects.annotate(num=Count("facture")).order_by("-num")[ + :10 + ] }, - _("Bank"): { - _("User"): Banque.objects.annotate( - num=Count('facture') - ).order_by('-num')[:10], + Banque._meta.verbose_name: { + User._meta.verbose_name_plural: Banque.objects.annotate(num=Count("facture")).order_by("-num")[ + :10 + ] }, } - return render(request, 'logs/stats_users.html', {'stats_list': stats}) + return render(request, "logs/stats_users.html", {"stats_list": stats}) @login_required -@can_view_app('users') +@can_view_app("users") def stats_actions(request): """Vue qui affiche les statistiques de modifications d'objets par utilisateurs. Affiche le nombre de modifications aggrégées par utilisateurs""" stats = { - _("User"): { - _("Action"): User.objects.annotate( - num=Count('revision') - ).order_by('-num')[:40], - }, + User._meta.verbose_name: { + _("actions"): User.objects.annotate(num=Count("revision")).order_by("-num")[ + :40 + ] + } } - return render(request, 'logs/stats_users.html', {'stats_list': stats}) + return render(request, "logs/stats_users.html", {"stats_list": stats}) def history(request, application, object_name, object_id): @@ -503,33 +484,29 @@ def history(request, application, object_name, object_id): model = apps.get_model(application, object_name) except LookupError: raise Http404(_("No model found.")) - object_name_id = object_name + 'id' + object_name_id = object_name + "id" kwargs = {object_name_id: object_id} try: instance = model.get_instance(**kwargs) except model.DoesNotExist: messages.error(request, _("Nonexistent entry.")) - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(request.user.id)} - )) - can, msg = instance.can_view(request.user) + return redirect( + reverse("users:profil", kwargs={"userid": str(request.user.id)}) + ) + can, msg, _permissions = instance.can_view(request.user) if not can: - messages.error(request, msg or _("You don't have the right to access this menu.")) - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(request.user.id)} - )) - pagination_number = GeneralOption.get_cached_value('pagination_number') + messages.error( + request, msg or _("You don't have the right to access this menu.") + ) + return redirect( + reverse("users:profil", kwargs={"userid": str(request.user.id)}) + ) + pagination_number = GeneralOption.get_cached_value("pagination_number") reversions = Version.objects.get_for_object(instance) - if hasattr(instance, 'linked_objects'): + if hasattr(instance, "linked_objects"): for related_object in chain(instance.linked_objects()): - reversions = (reversions | - Version.objects.get_for_object(related_object)) + reversions = reversions | Version.objects.get_for_object(related_object) reversions = re2o_paginator(request, reversions, pagination_number) return render( - request, - 're2o/history.html', - {'reversions': reversions, 'object': instance} + request, "re2o/history.html", {"reversions": reversions, "object": instance} ) - diff --git a/machines/__init__.py b/machines/__init__.py index f874399a..4f7225d5 100644 --- a/machines/__init__.py +++ b/machines/__init__.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify diff --git a/machines/acl.py b/machines/acl.py index 53f70c27..1989a788 100644 --- a/machines/acl.py +++ b/machines/acl.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -38,6 +38,10 @@ def can_view(user): 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 _("You don't have the right to view this" - " application.") + can = user.has_module_perms("machines") + return ( + can, + None if can else _("You don't have the right to view this" + " application."), + ("machines",), + ) diff --git a/machines/admin.py b/machines/admin.py index bafebb80..ee6ea6f1 100644 --- a/machines/admin.py +++ b/machines/admin.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -51,106 +51,127 @@ from .models import IpType, Machine, MachineType, Domain, IpList, Interface class MachineAdmin(VersionAdmin): """ Admin view of a Machine object """ + pass class Ipv6ListAdmin(VersionAdmin): """ Admin view of a Ipv6List object """ + pass class IpTypeAdmin(VersionAdmin): """ Admin view of a IpType object """ + pass class MachineTypeAdmin(VersionAdmin): """ Admin view of a MachineType object """ + pass class VlanAdmin(VersionAdmin): """ Admin view of a Vlan object """ + pass class ExtensionAdmin(VersionAdmin): """ Admin view of a Extension object """ + pass class SOAAdmin(VersionAdmin): """ Admin view of a SOA object """ + pass class MxAdmin(VersionAdmin): """ Admin view of a MX object """ + pass class NsAdmin(VersionAdmin): """ Admin view of a NS object """ + pass class TxtAdmin(VersionAdmin): """ Admin view of a TXT object """ + pass class DNameAdmin(VersionAdmin): """ Admin view of a DName object """ + pass class SrvAdmin(VersionAdmin): """ Admin view of a SRV object """ + pass class SshFpAdmin(VersionAdmin): """ Admin view of a SSHFP object """ + pass class NasAdmin(VersionAdmin): """ Admin view of a Nas object """ + pass class IpListAdmin(VersionAdmin): """ Admin view of a Ipv4List object """ + pass class OuverturePortAdmin(VersionAdmin): """ Admin view of a OuverturePort object """ + pass class OuverturePortListAdmin(VersionAdmin): """ Admin view of a OuverturePortList object """ + pass class InterfaceAdmin(VersionAdmin): """ Admin view of a Interface object """ - list_display = ('machine', 'type', 'mac_address', 'ipv4', 'details') + + list_display = ("machine", "machine_type", "mac_address", "ipv4", "details") class DomainAdmin(VersionAdmin): """ Admin view of a Domain object """ - list_display = ('interface_parent', 'name', 'extension', 'cname') + + list_display = ("interface_parent", "name", "extension", "cname") class ServiceAdmin(VersionAdmin): """ Admin view of a ServiceAdmin object """ - list_display = ('service_type', 'min_time_regen', 'regular_time_regen') + + list_display = ("service_type", "min_time_regen", "regular_time_regen") class RoleAdmin(VersionAdmin): """ Admin view of a RoleAdmin object """ + pass diff --git a/machines/forms.py b/machines/forms.py index 94b9293a..80379bc3 100644 --- a/machines/forms.py +++ b/machines/forms.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # Copyright © 2017 Maël Kervella # @@ -70,19 +70,19 @@ class EditMachineForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): class Meta: model = Machine - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(EditMachineForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['name'].label = _("Machine name") + self.fields["name"].label = _("Machine name") class NewMachineForm(EditMachineForm): """Creation d'une machine, ne renseigne que le nom""" class Meta(EditMachineForm.Meta): - fields = ['name'] + fields = ["name"] class EditInterfaceForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): @@ -90,39 +90,38 @@ class EditInterfaceForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): class Meta: model = Interface - fields = ['machine', 'type', 'ipv4', 'mac_address', 'details'] + fields = ["machine", "machine_type", "ipv4", "mac_address", "details"] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) - user = kwargs.get('user') + 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 = _("MAC address") - self.fields['type'].label = _("Machine type") - self.fields['type'].empty_label = _("Select a machine type") + self.fields["mac_address"].label = _("MAC address") + self.fields["machine_type"].label = _("Machine type") + self.fields["machine_type"].empty_label = _("Select a machine type") if "ipv4" in self.fields: - self.fields['ipv4'].empty_label = _("Automatic IPv4 assignment") - self.fields['ipv4'].queryset = IpList.objects.filter( - interface__isnull=True - ) - can_use_all_iptype, _reason = IpType.can_use_all(user) + self.fields["ipv4"].empty_label = _("Automatic IPv4 assignment") + self.fields["ipv4"].queryset = IpList.objects.filter(interface__isnull=True) + can_use_all_iptype, _reason, _permissions = IpType.can_use_all(user) if not can_use_all_iptype: - self.fields['ipv4'].queryset = IpList.objects.filter( + 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( + self.fields["ipv4"].queryset = IpList.objects.filter( interface__isnull=True ) # Add it's own address - self.fields['ipv4'].queryset |= IpList.objects.filter( + self.fields["ipv4"].queryset |= IpList.objects.filter( interface=self.instance ) if "machine" in self.fields: - self.fields['machine'].queryset = Machine.objects.all() \ - .select_related('user') - can_use_all_machinetype, _reason = MachineType.can_use_all(user) + self.fields["machine"].queryset = Machine.objects.all().select_related( + "user" + ) + can_use_all_machinetype, _reason, _permissions = MachineType.can_use_all(user) if not can_use_all_machinetype: - self.fields['type'].queryset = MachineType.objects.filter( + self.fields["machine_type"].queryset = MachineType.objects.filter( ip_type__in=IpType.objects.filter(need_infra=False) ) @@ -132,56 +131,57 @@ class AddInterfaceForm(EditInterfaceForm): affiche ou non l'ensemble des ip disponibles""" class Meta(EditInterfaceForm.Meta): - fields = ['type', 'ipv4', 'mac_address', 'details'] + fields = ["machine_type", "ipv4", "mac_address", "details"] -class AliasForm(FormRevMixin, ModelForm): +class AliasForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): """Ajout d'un alias (et edition), CNAME, contenant nom et extension""" class Meta: model = Domain - fields = ['name', 'extension'] + fields = ["name", "extension", "ttl"] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) - user = kwargs.pop('user') + prefix = kwargs.pop("prefix", self.Meta.model.__name__) + user = kwargs["user"] super(AliasForm, self).__init__(*args, prefix=prefix, **kwargs) - can_use_all, _reason = Extension.can_use_all(user) + can_use_all, _reason, _permissions = Extension.can_use_all(user) if not can_use_all: - self.fields['extension'].queryset = Extension.objects.filter( + self.fields["extension"].queryset = Extension.objects.filter( need_infra=False ) -class DomainForm(FormRevMixin, ModelForm): +class DomainForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): """Ajout et edition d'un enregistrement de nom, relié à interface""" class Meta: model = Domain - fields = ['name'] + fields = ["name", "ttl"] def __init__(self, *args, **kwargs): - if 'user' in kwargs: - user = kwargs.pop('user') - initial = kwargs.get('initial', {}) - initial['name'] = user.get_next_domain_name() - kwargs['initial'] = initial - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + if "user" in kwargs: + user = kwargs["user"] + initial = kwargs.get("initial", {}) + initial["name"] = user.get_next_domain_name() + kwargs["initial"] = initial + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(DomainForm, self).__init__(*args, prefix=prefix, **kwargs) class DelAliasForm(FormRevMixin, Form): """Suppression d'un ou plusieurs objets alias""" + alias = forms.ModelMultipleChoiceField( queryset=Domain.objects.all(), label=_("Current aliases"), - widget=forms.CheckboxSelectMultiple + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - interface = kwargs.pop('interface') + interface = kwargs.pop("interface") super(DelAliasForm, self).__init__(*args, **kwargs) - self.fields['alias'].queryset = Domain.objects.filter( + self.fields["alias"].queryset = Domain.objects.filter( cname__in=Domain.objects.filter(interface_parent=interface) ) @@ -191,30 +191,31 @@ class MachineTypeForm(FormRevMixin, ModelForm): class Meta: model = MachineType - fields = ['type', 'ip_type'] + fields = ["name", "ip_type"] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(MachineTypeForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['type'].label = _("Machine type to add") - self.fields['ip_type'].label = _("Related IP type") + self.fields["name"].label = _("Machine type to add") + self.fields["ip_type"].label = _("Related IP type") class DelMachineTypeForm(FormRevMixin, Form): """Suppression d'un ou plusieurs machinetype""" + machinetypes = forms.ModelMultipleChoiceField( queryset=MachineType.objects.none(), label=_("Current machine types"), - widget=forms.CheckboxSelectMultiple + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) + instances = kwargs.pop("instances", None) super(DelMachineTypeForm, self).__init__(*args, **kwargs) if instances: - self.fields['machinetypes'].queryset = instances + self.fields["machinetypes"].queryset = instances else: - self.fields['machinetypes'].queryset = MachineType.objects.all() + self.fields["machinetypes"].queryset = MachineType.objects.all() class IpTypeForm(FormRevMixin, ModelForm): @@ -223,12 +224,12 @@ class IpTypeForm(FormRevMixin, ModelForm): class Meta: model = IpType - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(IpTypeForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['type'].label = _("IP type to add") + self.fields["name"].label = _("IP type to add") class EditIpTypeForm(IpTypeForm): @@ -236,27 +237,37 @@ class EditIpTypeForm(IpTypeForm): synchroniser les objets iplist""" class Meta(IpTypeForm.Meta): - fields = ['extension', 'type', 'need_infra', 'domaine_ip_network', 'domaine_ip_netmask', - 'prefix_v6', 'prefix_v6_length', - 'vlan', 'reverse_v4', 'reverse_v6', - 'ouverture_ports'] + fields = [ + "extension", + "name", + "need_infra", + "domaine_ip_network", + "domaine_ip_netmask", + "prefix_v6", + "prefix_v6_length", + "vlan", + "reverse_v4", + "reverse_v6", + "ouverture_ports", + ] class DelIpTypeForm(FormRevMixin, Form): """Suppression d'un ou plusieurs iptype""" + iptypes = forms.ModelMultipleChoiceField( queryset=IpType.objects.none(), label=_("Current IP types"), - widget=forms.CheckboxSelectMultiple + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) + instances = kwargs.pop("instances", None) super(DelIpTypeForm, self).__init__(*args, **kwargs) if instances: - self.fields['iptypes'].queryset = instances + self.fields["iptypes"].queryset = instances else: - self.fields['iptypes'].queryset = IpType.objects.all() + self.fields["iptypes"].queryset = IpType.objects.all() class ExtensionForm(FormRevMixin, ModelForm): @@ -264,33 +275,34 @@ class ExtensionForm(FormRevMixin, ModelForm): class Meta: model = Extension - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(ExtensionForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['name'].label = _("Extension to add") - self.fields['origin'].label = _("A record origin") - self.fields['origin_v6'].label = _("AAAA record origin") - self.fields['soa'].label = _("SOA record to use") - self.fields['dnssec'].label = _("Sign with DNSSEC") + self.fields["name"].label = _("Extension to add") + self.fields["origin"].label = _("A record origin") + self.fields["origin_v6"].label = _("AAAA record origin") + self.fields["soa"].label = _("SOA record to use") + self.fields["dnssec"].label = _("Sign with DNSSEC") class DelExtensionForm(FormRevMixin, Form): """Suppression d'une ou plusieurs extensions""" + extensions = forms.ModelMultipleChoiceField( queryset=Extension.objects.none(), label=_("Current extensions"), - widget=forms.CheckboxSelectMultiple + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) + instances = kwargs.pop("instances", None) super(DelExtensionForm, self).__init__(*args, **kwargs) if instances: - self.fields['extensions'].queryset = instances + self.fields["extensions"].queryset = instances else: - self.fields['extensions'].queryset = Extension.objects.all() + self.fields["extensions"].queryset = Extension.objects.all() class Ipv6ListForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): @@ -298,10 +310,10 @@ class Ipv6ListForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): class Meta: model = Ipv6List - fields = ['ipv6', 'slaac_ip'] + fields = ["ipv6", "slaac_ip"] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(Ipv6ListForm, self).__init__(*args, prefix=prefix, **kwargs) @@ -310,28 +322,29 @@ class SOAForm(FormRevMixin, ModelForm): class Meta: model = SOA - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(SOAForm, self).__init__(*args, prefix=prefix, **kwargs) class DelSOAForm(FormRevMixin, Form): """Suppression d'un ou plusieurs SOA""" + soa = forms.ModelMultipleChoiceField( queryset=SOA.objects.none(), label=_("Current SOA records"), - widget=forms.CheckboxSelectMultiple + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) + instances = kwargs.pop("instances", None) super(DelSOAForm, self).__init__(*args, **kwargs) if instances: - self.fields['soa'].queryset = instances + self.fields["soa"].queryset = instances else: - self.fields['soa'].queryset = SOA.objects.all() + self.fields["soa"].queryset = SOA.objects.all() class MxForm(FormRevMixin, ModelForm): @@ -339,31 +352,32 @@ class MxForm(FormRevMixin, ModelForm): class Meta: model = Mx - fields = ['zone', 'priority', 'name'] + fields = ["zone", "priority", "name", "ttl"] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(MxForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['name'].queryset = Domain.objects.exclude( + self.fields["name"].queryset = Domain.objects.exclude( interface_parent=None - ).select_related('extension') + ).select_related("extension") class DelMxForm(FormRevMixin, Form): """Suppression d'un ou plusieurs MX""" + mx = forms.ModelMultipleChoiceField( queryset=Mx.objects.none(), label=_("Current MX records"), - widget=forms.CheckboxSelectMultiple + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) + instances = kwargs.pop("instances", None) super(DelMxForm, self).__init__(*args, **kwargs) if instances: - self.fields['mx'].queryset = instances + self.fields["mx"].queryset = instances else: - self.fields['mx'].queryset = Mx.objects.all() + self.fields["mx"].queryset = Mx.objects.all() class NsForm(FormRevMixin, ModelForm): @@ -373,31 +387,32 @@ class NsForm(FormRevMixin, ModelForm): class Meta: model = Ns - fields = ['zone', 'ns'] + fields = ["zone", "ns", "ttl"] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(NsForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['ns'].queryset = Domain.objects.exclude( + self.fields["ns"].queryset = Domain.objects.exclude( interface_parent=None - ).select_related('extension') + ).select_related("extension") class DelNsForm(FormRevMixin, Form): """Suppresion d'un ou plusieurs NS""" + ns = forms.ModelMultipleChoiceField( queryset=Ns.objects.none(), label=_("Current NS records"), - widget=forms.CheckboxSelectMultiple + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) + instances = kwargs.pop("instances", None) super(DelNsForm, self).__init__(*args, **kwargs) if instances: - self.fields['ns'].queryset = instances + self.fields["ns"].queryset = instances else: - self.fields['ns'].queryset = Ns.objects.all() + self.fields["ns"].queryset = Ns.objects.all() class TxtForm(FormRevMixin, ModelForm): @@ -405,28 +420,29 @@ class TxtForm(FormRevMixin, ModelForm): class Meta: model = Txt - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(TxtForm, self).__init__(*args, prefix=prefix, **kwargs) class DelTxtForm(FormRevMixin, Form): """Suppression d'un ou plusieurs TXT""" + txt = forms.ModelMultipleChoiceField( queryset=Txt.objects.none(), label=_("Current TXT records"), - widget=forms.CheckboxSelectMultiple + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) + instances = kwargs.pop("instances", None) super(DelTxtForm, self).__init__(*args, **kwargs) if instances: - self.fields['txt'].queryset = instances + self.fields["txt"].queryset = instances else: - self.fields['txt'].queryset = Txt.objects.all() + self.fields["txt"].queryset = Txt.objects.all() class DNameForm(FormRevMixin, ModelForm): @@ -434,28 +450,29 @@ class DNameForm(FormRevMixin, ModelForm): class Meta: model = DName - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(DNameForm, self).__init__(*args, prefix=prefix, **kwargs) class DelDNameForm(FormRevMixin, Form): """Delete a set of DNAME entries""" + dnames = forms.ModelMultipleChoiceField( queryset=Txt.objects.none(), label=_("Current DNAME records"), - widget=forms.CheckboxSelectMultiple + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) + instances = kwargs.pop("instances", None) super(DelDNameForm, self).__init__(*args, **kwargs) if instances: - self.fields['dnames'].queryset = instances + self.fields["dnames"].queryset = instances else: - self.fields['dnames'].queryset = DName.objects.all() + self.fields["dnames"].queryset = DName.objects.all() class SrvForm(FormRevMixin, ModelForm): @@ -463,28 +480,29 @@ class SrvForm(FormRevMixin, ModelForm): class Meta: model = Srv - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(SrvForm, self).__init__(*args, prefix=prefix, **kwargs) class DelSrvForm(FormRevMixin, Form): """Suppression d'un ou plusieurs Srv""" + srv = forms.ModelMultipleChoiceField( queryset=Srv.objects.none(), label=_("Current SRV records"), - widget=forms.CheckboxSelectMultiple + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) + instances = kwargs.pop("instances", None) super(DelSrvForm, self).__init__(*args, **kwargs) if instances: - self.fields['srv'].queryset = instances + self.fields["srv"].queryset = instances else: - self.fields['srv'].queryset = Srv.objects.all() + self.fields["srv"].queryset = Srv.objects.all() class NasForm(FormRevMixin, ModelForm): @@ -493,28 +511,29 @@ class NasForm(FormRevMixin, ModelForm): class Meta: model = Nas - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(NasForm, self).__init__(*args, prefix=prefix, **kwargs) class DelNasForm(FormRevMixin, Form): """Suppression d'un ou plusieurs nas""" + nas = forms.ModelMultipleChoiceField( queryset=Nas.objects.none(), label=_("Current NAS devices"), - widget=forms.CheckboxSelectMultiple + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) + instances = kwargs.pop("instances", None) super(DelNasForm, self).__init__(*args, **kwargs) if instances: - self.fields['nas'].queryset = instances + self.fields["nas"].queryset = instances else: - self.fields['nas'].queryset = Nas.objects.all() + self.fields["nas"].queryset = Nas.objects.all() class RoleForm(FormRevMixin, ModelForm): @@ -522,32 +541,32 @@ class RoleForm(FormRevMixin, ModelForm): class Meta: model = Role - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(RoleForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['servers'].queryset = (Interface.objects.all() - .select_related( - 'domain__extension' - )) + self.fields["servers"].queryset = Interface.objects.all().select_related( + "domain__extension" + ) class DelRoleForm(FormRevMixin, Form): """Deletion of one or several roles.""" + role = forms.ModelMultipleChoiceField( queryset=Role.objects.none(), label=_("Current roles"), - widget=forms.CheckboxSelectMultiple + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) + instances = kwargs.pop("instances", None) super(DelRoleForm, self).__init__(*args, **kwargs) if instances: - self.fields['role'].queryset = instances + self.fields["role"].queryset = instances else: - self.fields['role'].queryset = Role.objects.all() + self.fields["role"].queryset = Role.objects.all() class ServiceForm(FormRevMixin, ModelForm): @@ -555,15 +574,14 @@ class ServiceForm(FormRevMixin, ModelForm): class Meta: model = Service - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(ServiceForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['servers'].queryset = (Interface.objects.all() - .select_related( - 'domain__extension' - )) + self.fields["servers"].queryset = Interface.objects.all().select_related( + "domain__extension" + ) def save(self, commit=True): # TODO : None of the parents of ServiceForm use the commit @@ -571,25 +589,26 @@ class ServiceForm(FormRevMixin, ModelForm): instance = super(ServiceForm, self).save(commit=False) if commit: instance.save() - instance.process_link(self.cleaned_data.get('servers')) + instance.process_link(self.cleaned_data.get("servers")) return instance class DelServiceForm(FormRevMixin, Form): """Suppression d'un ou plusieurs service""" + service = forms.ModelMultipleChoiceField( queryset=Service.objects.none(), label=_("Current services"), - widget=forms.CheckboxSelectMultiple + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) + instances = kwargs.pop("instances", None) super(DelServiceForm, self).__init__(*args, **kwargs) if instances: - self.fields['service'].queryset = instances + self.fields["service"].queryset = instances else: - self.fields['service'].queryset = Service.objects.all() + self.fields["service"].queryset = Service.objects.all() class VlanForm(FormRevMixin, ModelForm): @@ -597,39 +616,41 @@ class VlanForm(FormRevMixin, ModelForm): class Meta: model = Vlan - fields = ['vlan_id', 'name', 'comment'] + fields = ["vlan_id", "name", "comment"] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(VlanForm, self).__init__(*args, prefix=prefix, **kwargs) class EditOptionVlanForm(FormRevMixin, ModelForm): """Ajout d'un vlan : id, nom""" + class Meta: model = Vlan - fields = ['dhcp_snooping', 'dhcpv6_snooping', 'arp_protect', 'igmp', 'mld'] + fields = ["dhcp_snooping", "dhcpv6_snooping", "arp_protect", "igmp", "mld"] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(EditOptionVlanForm, self).__init__(*args, prefix=prefix, **kwargs) class DelVlanForm(FormRevMixin, Form): """Suppression d'un ou plusieurs vlans""" + vlan = forms.ModelMultipleChoiceField( queryset=Vlan.objects.none(), label=_("Current VLANs"), - widget=forms.CheckboxSelectMultiple + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) + instances = kwargs.pop("instances", None) super(DelVlanForm, self).__init__(*args, **kwargs) if instances: - self.fields['vlan'].queryset = instances + self.fields["vlan"].queryset = instances else: - self.fields['vlan'].queryset = Vlan.objects.all() + self.fields["vlan"].queryset = Vlan.objects.all() class EditOuverturePortConfigForm(FormRevMixin, ModelForm): @@ -638,14 +659,12 @@ class EditOuverturePortConfigForm(FormRevMixin, ModelForm): class Meta: model = Interface - fields = ['port_lists'] + fields = ["port_lists"] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(EditOuverturePortConfigForm, self).__init__( - *args, - prefix=prefix, - **kwargs + *args, prefix=prefix, **kwargs ) @@ -655,15 +674,11 @@ class EditOuverturePortListForm(FormRevMixin, ModelForm): class Meta: model = OuverturePortList - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(EditOuverturePortListForm, self).__init__( - *args, - prefix=prefix, - **kwargs - ) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) + super(EditOuverturePortListForm, self).__init__(*args, prefix=prefix, **kwargs) class SshFpForm(FormRevMixin, ModelForm): @@ -671,12 +686,8 @@ class SshFpForm(FormRevMixin, ModelForm): class Meta: model = SshFp - exclude = ('machine',) + exclude = ("machine",) def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(SshFpForm, self).__init__( - *args, - prefix=prefix, - **kwargs - ) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) + super(SshFpForm, self).__init__(*args, prefix=prefix, **kwargs) diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index bd36fb61..049c01ad 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-01-12 16:43+0100\n" +"POT-Creation-Date: 2019-11-20 01:24+0100\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -30,160 +30,160 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: acl.py:42 +#: machines/acl.py:44 msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: forms.py:78 +#: machines/forms.py:78 msgid "Machine name" msgstr "Nom de la machine" -#: forms.py:99 templates/machines/aff_machines.html:46 +#: machines/forms.py:99 machines/templates/machines/aff_machines.html:46 msgid "MAC address" msgstr "Adresse MAC" -#: forms.py:100 templates/machines/aff_machinetype.html:32 -#: templates/machines/machine.html:112 +#: machines/forms.py:100 machines/templates/machines/aff_machinetype.html:32 +#: machines/templates/machines/machine.html:112 msgid "Machine type" msgstr "Type de machine" -#: forms.py:101 +#: machines/forms.py:101 msgid "Select a machine type" msgstr "Sélectionnez un type de machine" -#: forms.py:103 +#: machines/forms.py:103 msgid "Automatic IPv4 assignment" msgstr "Assignation automatique IPv4" -#: forms.py:177 +#: machines/forms.py:177 msgid "Current aliases" msgstr "Alias actuels" -#: forms.py:199 +#: machines/forms.py:199 msgid "Machine type to add" msgstr "Type de machine à ajouter" -#: forms.py:200 +#: machines/forms.py:200 msgid "Related IP type" msgstr "Type d'IP relié" -#: forms.py:207 +#: machines/forms.py:208 msgid "Current machine types" msgstr "Types de machines actuels" -#: forms.py:231 +#: machines/forms.py:232 msgid "IP type to add" msgstr "Type d'IP à ajouter" -#: forms.py:249 +#: machines/forms.py:260 msgid "Current IP types" msgstr "Types d'IP actuels" -#: forms.py:272 +#: machines/forms.py:283 msgid "Extension to add" msgstr "Extension à ajouter" -#: forms.py:273 templates/machines/aff_extension.html:37 +#: machines/forms.py:284 machines/templates/machines/aff_extension.html:37 msgid "A record origin" msgstr "Enregistrement A origin" -#: forms.py:274 templates/machines/aff_extension.html:39 +#: machines/forms.py:285 machines/templates/machines/aff_extension.html:39 msgid "AAAA record origin" msgstr "Enregistrement AAAA origin" -#: forms.py:275 +#: machines/forms.py:286 msgid "SOA record to use" msgstr "Enregistrement SOA à utiliser" -#: forms.py:276 +#: machines/forms.py:287 msgid "Sign with DNSSEC" msgstr "Signer avec DNSSEC" -#: forms.py:283 +#: machines/forms.py:295 msgid "Current extensions" msgstr "Extensions actuelles" -#: forms.py:324 +#: machines/forms.py:337 msgid "Current SOA records" msgstr "Enregistrements SOA actuels" -#: forms.py:356 +#: machines/forms.py:370 msgid "Current MX records" msgstr "Enregistrements MX actuels" -#: forms.py:390 +#: machines/forms.py:405 msgid "Current NS records" msgstr "Enregistrements NS actuels" -#: forms.py:419 +#: machines/forms.py:435 msgid "Current TXT records" msgstr "Enregistrements TXT actuels" -#: forms.py:448 +#: machines/forms.py:465 msgid "Current DNAME records" msgstr "Enregistrements DNAME actuels" -#: forms.py:477 +#: machines/forms.py:495 msgid "Current SRV records" msgstr "Enregistrements SRV actuels" -#: forms.py:507 +#: machines/forms.py:526 msgid "Current NAS devices" msgstr "Dispositifs NAS actuels" -#: forms.py:540 +#: machines/forms.py:559 msgid "Current roles" msgstr "Rôles actuels" -#: forms.py:582 +#: machines/forms.py:601 msgid "Current services" msgstr "Services actuels" -#: forms.py:622 +#: machines/forms.py:643 msgid "Current VLANs" msgstr "VLANs actuels" -#: models.py:61 -msgid "Optional" -msgstr "Optionnel" +#: machines/models.py:71 +msgid "Optional." +msgstr "Optionnel." -#: models.py:69 +#: machines/models.py:77 msgid "Can view a machine object" msgstr "Peut voir un objet machine" -#: models.py:71 +#: machines/models.py:78 msgid "Can change the user of a machine" msgstr "Peut changer l'utilisateur d'une machine" -#: models.py:73 +#: machines/models.py:80 machines/views.py:317 msgid "machine" msgstr "machine" -#: models.py:74 +#: machines/models.py:81 msgid "machines" msgstr "machines" -#: models.py:107 +#: machines/models.py:114 msgid "You don't have the right to change the machine's user." msgstr "Vous n'avez pas le droit de changer l'utilisateur de la machine." -#: models.py:116 +#: machines/models.py:129 msgid "You don't have the right to view all the machines." msgstr "Vous n'avez pas le droit de voir toutes les machines." -#: models.py:130 +#: machines/models.py:144 msgid "Nonexistent user." msgstr "Utilisateur inexistant." -#: models.py:138 +#: machines/models.py:154 machines/models.py:1303 msgid "You don't have the right to add a machine." msgstr "Vous n'avez pas le droit d'ajouter une machine." -#: models.py:140 +#: machines/models.py:160 msgid "You don't have the right to add a machine to another user." msgstr "Vous n'avez pas le droit d'ajouter une machine à un autre utilisateur." -#: models.py:143 models.py:1182 +#: machines/models.py:168 machines/models.py:1320 #, python-format msgid "" "You reached the maximum number of interfaces that you are allowed to create " @@ -192,78 +192,79 @@ msgstr "" "Vous avez atteint le nombre maximal d'interfaces que vous pouvez créer vous-" "même (%s)." -#: models.py:162 models.py:1207 models.py:1224 models.py:1326 models.py:1343 +#: machines/models.py:189 machines/models.py:1352 machines/models.py:1371 +#: machines/models.py:1474 machines/models.py:1492 msgid "You don't have the right to edit a machine of another user." msgstr "" "Vous n'avez pas le droit de modifier une machine d'un autre utilisateur." -#: models.py:180 +#: machines/models.py:209 msgid "You don't have the right to delete a machine of another user." msgstr "" "Vous n'avez pas le droit de supprimer une machine d'une autre utilisateur." -#: models.py:192 +#: machines/models.py:228 machines/models.py:1762 msgid "You don't have the right to view other machines than yours." msgstr "Vous n'avez pas le droit de voir d'autres machines que les vôtres." -#: models.py:204 templates/machines/aff_machines.html:53 +#: machines/models.py:242 machines/templates/machines/aff_machines.html:53 msgid "No name" msgstr "Sans nom" -#: models.py:254 +#: machines/models.py:319 msgid "Can view a machine type object" msgstr "Peut voir un objet type de machine" -#: models.py:255 +#: machines/models.py:320 msgid "Can use all machine types" msgstr "Peut utiliser tous les types de machine" -#: models.py:257 +#: machines/models.py:322 msgid "machine type" msgstr "type de machine" -#: models.py:258 +#: machines/models.py:323 msgid "machine types" msgstr "types de machine" -#: models.py:276 +#: machines/models.py:343 msgid "You don't have the right to use all machine types." msgstr "Vous n'avez pas le droit d'utiliser tous les types de machine." -#: models.py:295 -msgid "Network containing the domain's IPv4 range (optional)" -msgstr "Réseau contenant la plage IPv4 du domaine (optionnel)" +#: machines/models.py:364 +msgid "Network containing the domain's IPv4 range (optional)." +msgstr "Réseau contenant la plage IPv4 du domaine (optionnel)." -#: models.py:303 -msgid "Netmask for the domain's IPv4 range" -msgstr "Masque de sous-réseau pour la plage IPv4 du domaine" +#: machines/models.py:369 +msgid "Netmask for the domain's IPv4 range." +msgstr "Masque de sous-réseau pour la plage IPv4 du domaine." -#: models.py:307 -msgid "Enable reverse DNS for IPv4" -msgstr "Activer DNS inverse pour IPv4" +#: machines/models.py:372 +msgid "Enable reverse DNS for IPv4." +msgstr "Activer DNS inverse pour IPv4." -#: models.py:323 -msgid "Enable reverse DNS for IPv6" -msgstr "Activer DNS inverse pour IPv6" +#: machines/models.py:379 +msgid "Enable reverse DNS for IPv6." +msgstr "Activer DNS inverse pour IPv6." -#: models.py:339 +#: machines/models.py:386 msgid "Can view an IP type object" msgstr "Peut voir un objet type d'IP" -#: models.py:340 +#: machines/models.py:387 msgid "Can use all IP types" msgstr "Peut utiliser tous les types d'IP" -#: models.py:342 templates/machines/aff_iptype.html:35 -#: templates/machines/machine.html:108 +#: machines/models.py:389 machines/templates/machines/aff_iptype.html:35 +#: machines/templates/machines/machine.html:108 msgid "IP type" msgstr "type d'IP" -#: models.py:343 +#: machines/models.py:390 msgid "IP types" msgstr "types d'IP" -#: models.py:446 +#: machines/models.py:499 msgid "" "One or several IP addresses from the range are affected, impossible to " "delete the range." @@ -271,21 +272,21 @@ msgstr "" "Une ou plusieurs adresses IP de la plage sont affectées, impossible de " "supprimer la plage." -#: models.py:488 +#: machines/models.py:546 msgid "Range end must be after range start..." msgstr "La fin de la plage doit être après le début..." -#: models.py:491 +#: machines/models.py:551 msgid "The range is too large, you can't create a larger one than a /16." msgstr "" "La plage est trop grande, vous ne pouvez pas en créer une plus grande " "qu'un /16." -#: models.py:496 +#: machines/models.py:559 msgid "The specified range is not disjoint from existing ranges." msgstr "La plage renseignée n'est pas disjointe des plages existantes." -#: models.py:504 +#: machines/models.py:573 msgid "" "If you specify a domain network or netmask, it must contain the domain's IP " "range." @@ -293,322 +294,336 @@ msgstr "" "Si vous renseignez un réseau ou masque de sous-réseau, il doit contenir la " "plage IP du domaine." -#: models.py:537 -msgid "v4 multicast management" -msgstr "gestion de multidiffusion v4" +#: machines/models.py:611 +msgid "v4 multicast management." +msgstr "gestion de multidiffusion v4." -#: models.py:541 -msgid "v6 multicast management" -msgstr "gestion de multidiffusion v6" +#: machines/models.py:612 +msgid "v6 multicast management." +msgstr "gestion de multidiffusion v6." -#: models.py:546 +#: machines/models.py:615 msgid "Can view a VLAN object" msgstr "Peut voir un objet VLAN" -#: models.py:548 templates/machines/machine.html:160 +#: machines/models.py:616 machines/templates/machines/machine.html:160 msgid "VLAN" msgstr "VLAN" -#: models.py:549 templates/machines/sidebar.html:57 +#: machines/models.py:617 machines/templates/machines/sidebar.html:57 msgid "VLANs" msgstr "VLANs" -#: models.py:562 +#: machines/models.py:629 msgid "MAC-address" msgstr "MAC-address" -#: models.py:585 +#: machines/models.py:644 msgid "Can view a NAS device object" msgstr "Peut voir un objet dispositif NAS" -#: models.py:587 templates/machines/machine.html:164 +#: machines/models.py:645 machines/templates/machines/machine.html:164 msgid "NAS device" msgstr "dispositif NAS" -#: models.py:588 templates/machines/sidebar.html:63 +#: machines/models.py:646 machines/templates/machines/sidebar.html:63 msgid "NAS devices" msgstr "dispositifs NAS" -#: models.py:602 -msgid "Contact email address for the zone" -msgstr "Adresse mail de contact pour la zone" +#: machines/models.py:660 +msgid "Contact email address for the zone." +msgstr "Adresse mail de contact pour la zone." -#: models.py:606 +#: machines/models.py:664 msgid "" "Seconds before the secondary DNS have to ask the primary DNS serial to " -"detect a modification" +"detect a modification." msgstr "" "Secondes avant que le DNS secondaire demande au DNS primaire le serial pour " -"détecter une modification" +"détecter une modification." -#: models.py:611 +#: machines/models.py:671 msgid "" "Seconds before the secondary DNS ask the serial again in case of a primary " -"DNS timeout" +"DNS timeout." msgstr "" "Secondes avant que le DNS secondaire demande le serial de nouveau dans le " -"cas d'un délai d'attente du DNS primaire" +"cas d'un délai d'attente du DNS primaire." -#: models.py:616 +#: machines/models.py:678 msgid "" "Seconds before the secondary DNS stop answering requests in case of primary " -"DNS timeout" +"DNS timeout." msgstr "" "Secondes avant que le DNS secondaire arrête de répondre aux requêtes dans le " -"cas d'un délai d'attente du DNS primaire" +"cas d'un délai d'attente du DNS primaire." -#: models.py:621 models.py:878 -msgid "Time to Live" +#: machines/models.py:683 machines/models.py:950 +msgid "Time To Live." msgstr "Temps de vie" -#: models.py:626 +#: machines/models.py:687 msgid "Can view an SOA record object" msgstr "Peut voir un objet enregistrement SOA" -#: models.py:628 templates/machines/aff_extension.html:36 -#: templates/machines/machine.html:120 +#: machines/models.py:688 machines/templates/machines/aff_extension.html:36 +#: machines/templates/machines/machine.html:120 msgid "SOA record" msgstr "enregistrement SOA" -#: models.py:629 +#: machines/models.py:689 msgid "SOA records" msgstr "enregistrements SOA" -#: models.py:668 +#: machines/models.py:728 msgid "SOA to edit" msgstr "SOA à modifier" -#: models.py:679 -msgid "Zone name, must begin with a dot (.example.org)" -msgstr "Nom de zone, doit commencer par un point (.example.org)" +#: machines/models.py:739 +msgid "Zone name, must begin with a dot (.example.org)." +msgstr "Nom de zone, doit commencer par un point (.example.org)." -#: models.py:687 -msgid "A record associated with the zone" -msgstr "Enregistrement A associé à la zone" +#: machines/models.py:747 +msgid "A record associated with the zone." +msgstr "Enregistrement A associé à la zone." -#: models.py:693 -msgid "AAAA record associated with the zone" -msgstr "Enregristrement AAAA associé avec la zone" +#: machines/models.py:753 +msgid "AAAA record associated with the zone." +msgstr "Enregristrement AAAA associé avec la zone." -#: models.py:701 -msgid "Should the zone be signed with DNSSEC" -msgstr "La zone doit-elle être signée avec DNSSEC" +#: machines/models.py:757 +msgid "Should the zone be signed with DNSSEC." +msgstr "La zone doit-elle être signée avec DNSSEC." -#: models.py:706 +#: machines/models.py:762 msgid "Can view an extension object" msgstr "Peut voir un objet extension" -#: models.py:707 +#: machines/models.py:763 msgid "Can use all extensions" msgstr "Peut utiliser toutes les extensions" -#: models.py:709 +#: machines/models.py:765 msgid "DNS extension" msgstr "extension DNS" -#: models.py:710 +#: machines/models.py:766 msgid "DNS extensions" msgstr "extensions DNS" -#: models.py:764 +#: machines/models.py:826 +msgid "You don't have the right to use all extensions." +msgstr "Vous n'avez pas le droit d'utiliser toutes les extensions." + +#: machines/models.py:835 msgid "An extension must begin with a dot." msgstr "Une extension doit commencer par un point." -#: models.py:778 +#: machines/models.py:848 machines/models.py:874 machines/models.py:898 +#: machines/models.py:921 machines/models.py:1587 +msgid "Time To Live (TTL)" +msgstr "Temps de vie (TTL)" + +#: machines/models.py:852 msgid "Can view an MX record object" msgstr "Peut voir un objet enregistrement MX" -#: models.py:780 templates/machines/machine.html:124 +#: machines/models.py:853 machines/templates/machines/machine.html:124 msgid "MX record" msgstr "enregistrement MX" -#: models.py:781 +#: machines/models.py:854 msgid "MX records" msgstr "enregistrements MX" -#: models.py:803 +#: machines/models.py:878 msgid "Can view an NS record object" msgstr "Peut voir un objet enregistrement NS" -#: models.py:805 templates/machines/machine.html:128 +#: machines/models.py:879 machines/templates/machines/machine.html:128 msgid "NS record" msgstr "enregistrement NS" -#: models.py:806 +#: machines/models.py:880 msgid "NS records" msgstr "enregistrements NS" -#: models.py:825 +#: machines/models.py:902 msgid "Can view a TXT record object" msgstr "Peut voir un objet enregistrement TXT" -#: models.py:827 templates/machines/machine.html:132 +#: machines/models.py:903 machines/templates/machines/machine.html:132 msgid "TXT record" msgstr "enregistrement TXT" -#: models.py:828 +#: machines/models.py:904 msgid "TXT records" msgstr "enregistrements TXT" -#: models.py:847 +#: machines/models.py:925 msgid "Can view a DNAME record object" msgstr "Peut voir un objet enregistrement DNAME" -#: models.py:849 templates/machines/machine.html:136 +#: machines/models.py:926 machines/templates/machines/machine.html:136 msgid "DNAME record" msgstr "enregistrement DNAME" -#: models.py:850 +#: machines/models.py:927 msgid "DNAME records" msgstr "enregistrements DNAME" -#: models.py:883 +#: machines/models.py:956 msgid "" "Priority of the target server (positive integer value, the lower it is, the " -"more the server will be used if available)" +"more the server will be used if available)." msgstr "" "Priorité du serveur cible (entier positif, plus il est bas, plus le serveur " -"sera utilisé si disponible)" +"sera utilisé si disponible)." -#: models.py:890 +#: machines/models.py:965 msgid "" "Relative weight for records with the same priority (integer value between 0 " -"and 65535)" +"and 65535)." msgstr "" "Poids relatif des enregistrements avec la même priorité (entier entre 0 et " -"65535)" +"65535)." -#: models.py:895 -msgid "TCP/UDP port" -msgstr "Port TCP/UDP" +#: machines/models.py:970 +msgid "TCP/UDP port." +msgstr "Port TCP/UDP." -#: models.py:900 -msgid "Target server" -msgstr "Serveur cible" +#: machines/models.py:973 +msgid "Target server." +msgstr "Serveur cible." -#: models.py:905 +#: machines/models.py:977 msgid "Can view an SRV record object" msgstr "Peut voir un objet enregistrement SRV" -#: models.py:907 templates/machines/machine.html:140 +#: machines/models.py:978 machines/templates/machines/machine.html:140 msgid "SRV record" msgstr "enregistrement SRV" -#: models.py:908 +#: machines/models.py:979 msgid "SRV records" msgstr "enregistrements SRV" -#: models.py:937 templates/machines/aff_sshfp.html:31 -msgid "SSH public key" -msgstr "Clé publique SSH" +#: machines/models.py:1030 +msgid "SSH public key." +msgstr "Clé publique SSH." -#: models.py:945 templates/machines/aff_sshfp.html:33 -#: templates/machines/aff_vlan.html:35 -msgid "Comment" -msgstr "Commentaire" +#: machines/models.py:1033 +msgid "Comment." +msgstr "Commentaire." -#: models.py:972 +#: machines/models.py:1056 msgid "Can view an SSHFP record object" msgstr "Peut voir un objet enregistrement SSHFP" -#: models.py:974 templates/machines/machine.html:144 +#: machines/models.py:1057 machines/templates/machines/machine.html:144 +#: machines/views.py:485 msgid "SSHFP record" msgstr "enregistrement SSHFP" -#: models.py:975 +#: machines/models.py:1058 msgid "SSHFP records" msgstr "enregistrements SSHFP" -#: models.py:1012 +#: machines/models.py:1093 msgid "Can view an interface object" msgstr "Peut voir un objet interface" -#: models.py:1014 +#: machines/models.py:1094 msgid "Can change the owner of an interface" msgstr "Peut changer l'utilisateur d'une interface" -#: models.py:1016 +#: machines/models.py:1096 machines/views.py:369 msgid "interface" msgstr "interface" -#: models.py:1017 +#: machines/models.py:1097 msgid "interfaces" msgstr "interfaces" -#: models.py:1111 +#: machines/models.py:1135 +msgid "Unknown vendor." +msgstr "Constructeur inconnu." + +#: machines/models.py:1199 msgid "The given MAC address is invalid." msgstr "L'adresse MAC indiquée est invalide." -#: models.py:1124 +#: machines/models.py:1208 +msgid "There are no IP addresses available in the slash." +msgstr "Il n'y a pas d'adresses IP disponibles dans le slash." + +#: machines/models.py:1260 msgid "The selected IP type is invalid." msgstr "Le type d'IP sélectionné est invalide." -#: models.py:1136 -msgid "There is no IP address available in the slash." -msgstr "Il n'y a pas d'adresse IP disponible dans le slash." +#: machines/models.py:1274 +msgid "MAC address already registered in this machine type/subnet." +msgstr "Adresse MAC déjà enregistrée dans ce type de machine/sous-réseau." -#: models.py:1154 +#: machines/models.py:1283 msgid "The IPv4 address and the machine type don't match." msgstr "L'adresse IPv4 et le type de machine ne correspondent pas." -#: models.py:1168 +#: machines/models.py:1298 msgid "Nonexistent machine." msgstr "Machine inexistante." -#: models.py:1172 -msgid "You can't add a machine." -msgstr "Vous ne pouvez pas ajouter une machine." - -#: models.py:1178 +#: machines/models.py:1311 msgid "" "You don't have the right to add an interface to a machine of another user." msgstr "" "Vous n'avez pas le droit d'ajouter une interface à une machine d'un autre " "utilisateur." -#: models.py:1192 -msgid "Permission required to edit the machine." -msgstr "Permission requise pour modifier la machine." +#: machines/models.py:1335 +msgid "You don't have the right to edit the machine." +msgstr "Vous n'avez pas le droit d'éditer une machine." -#: models.py:1236 models.py:1355 models.py:1565 +#: machines/models.py:1389 machines/models.py:1509 msgid "You don't have the right to view machines other than yours." msgstr "Vous n'avez pas le droit de voir d'autres machines que les vôtres." -#: models.py:1282 +#: machines/models.py:1417 msgid "Can view an IPv6 addresses list object" msgstr "Peut voir un objet list d'adresses IPv6" -#: models.py:1283 +#: machines/models.py:1420 msgid "Can change the SLAAC value of an IPv6 addresses list" msgstr "Peut modifier la valeur SLAAC d'une liste d'adresses IPv6" -#: models.py:1286 +#: machines/models.py:1423 machines/views.py:429 msgid "IPv6 addresses list" msgstr "Liste d'adresses IPv6" -#: models.py:1287 +#: machines/models.py:1424 msgid "IPv6 addresses lists" msgstr "Listes d'adresses IPv6" -#: models.py:1299 models.py:1513 +#: machines/models.py:1436 machines/models.py:1677 msgid "Nonexistent interface." msgstr "Interface inexistante." -#: models.py:1302 models.py:1520 +#: machines/models.py:1442 machines/models.py:1686 msgid "You don't have the right to add an alias to a machine of another user." msgstr "" "Vous n'avez pas le droit d'ajouter un alias à une machine d'un autre " "utilisateur." -#: models.py:1310 -msgid "Permission required to change the SLAAC value of an IPv6 address" -msgstr "Permission requise pour changer la valeur SLAAC d'une adresse IPv6." +#: machines/models.py:1455 +msgid "You don't have the right to change the SLAAC value of an IPv6 address." +msgstr "" +"Vous n'avez pas le droit de changer la valeur SLAAC d'une adresse IPv6." -#: models.py:1382 +#: machines/models.py:1541 msgid "A SLAAC IP address is already registered." msgstr "Une adresse IP SLAAC est déjà enregistrée." -#: models.py:1390 +#: machines/models.py:1555 msgid "" "The v6 prefix is incorrect and doesn't match the type associated with the " "machine." @@ -616,45 +631,49 @@ msgstr "" "Le préfixe v6 est incorrect et ne correspond pas au type associé à la " "machine." -#: models.py:1416 +#: machines/models.py:1580 msgid "Mandatory and unique, must not contain dots." msgstr "Obligatoire et unique, ne doit pas contenir de points." -#: models.py:1430 +#: machines/models.py:1595 msgid "Can view a domain object" msgstr "Peut voir un objet domaine" -#: models.py:1432 +#: machines/models.py:1596 +msgid "Can change the TTL of a domain object" +msgstr "Peut changer le TTL d'un objet domaine" + +#: machines/models.py:1598 msgid "domain" msgstr "domaine" -#: models.py:1433 +#: machines/models.py:1599 msgid "domains" msgstr "domaines" -#: models.py:1455 +#: machines/models.py:1621 msgid "You can't create a both A and CNAME record." msgstr "Vous ne pouvez pas créer un enregistrement à la fois A et CNAME." -#: models.py:1458 +#: machines/models.py:1624 msgid "You can't create a CNAME record pointing to itself." msgstr "Vous ne pouvez pas créer un enregistrement CNAME vers lui-même." -#: models.py:1466 +#: machines/models.py:1630 #, python-format msgid "The domain name %s is too long (over 63 characters)." msgstr "Le nom de domaine %s est trop long (plus de 63 caractères)." -#: models.py:1469 +#: machines/models.py:1634 #, python-format msgid "The domain name %s contains forbidden characters." msgstr "Le nom de domaine %s contient des caractères interdits." -#: models.py:1487 +#: machines/models.py:1651 msgid "Invalid extension." msgstr "Extension invalide." -#: models.py:1528 +#: machines/models.py:1702 #, python-format msgid "" "You reached the maximum number of alias that you are allowed to create " @@ -663,416 +682,456 @@ msgstr "" "Vous avez atteint le nombre maximal d'alias que vous pouvez créer vous-même " "(%s)." -#: models.py:1541 +#: machines/models.py:1723 msgid "You don't have the right to edit an alias of a machine of another user." msgstr "" "Vous n'avez pas le droit de modifier un alias d'une machine d'un autre " "utilisateur." -#: models.py:1553 +#: machines/models.py:1743 msgid "" "You don't have the right to delete an alias of a machine of another user." msgstr "" "Vous n'avez pas le droit de supprimer un alias d'une machine d'un autre " "utilisateur." -#: models.py:1581 +#: machines/models.py:1773 +msgid "You don't have the right to change the domain's TTL." +msgstr "Vous n'avez pas le droit de changer le TTL du domaine." + +#: machines/models.py:1790 msgid "Can view an IPv4 addresses list object" msgstr "Peut voir un object liste d'adresses IPv4" -#: models.py:1583 +#: machines/models.py:1791 msgid "IPv4 addresses list" msgstr "Liste d'adresses IPv4" -#: models.py:1584 +#: machines/models.py:1792 msgid "IPv4 addresses lists" msgstr "Listes d'adresses IPv4" -#: models.py:1595 +#: machines/models.py:1804 msgid "The IPv4 address and the range of the IP type don't match." msgstr "L'adresse IPv4 et la plage du type d'IP ne correspondent pas." -#: models.py:1613 +#: machines/models.py:1822 msgid "DHCP server" msgstr "Serveur DHCP" -#: models.py:1614 +#: machines/models.py:1823 msgid "Switches configuration server" msgstr "Serveur de configuration des commutateurs réseau" -#: models.py:1615 +#: machines/models.py:1824 msgid "Recursive DNS server" msgstr "Serveur DNS récursif" -#: models.py:1616 +#: machines/models.py:1825 msgid "NTP server" msgstr "Serveur NTP" -#: models.py:1617 +#: machines/models.py:1826 msgid "RADIUS server" msgstr "Serveur RADIUS" -#: models.py:1618 +#: machines/models.py:1827 msgid "Log server" msgstr "Serveur log" -#: models.py:1619 +#: machines/models.py:1828 msgid "LDAP master server" msgstr "Serveur LDAP maître" -#: models.py:1620 +#: machines/models.py:1829 msgid "LDAP backup server" msgstr "Serveur LDAP de secours" -#: models.py:1621 +#: machines/models.py:1830 msgid "SMTP server" msgstr "Serveur SMTP" -#: models.py:1622 +#: machines/models.py:1831 msgid "postgreSQL server" msgstr "Serveur postgreSQL" -#: models.py:1623 +#: machines/models.py:1832 msgid "mySQL server" msgstr "Serveur mySQL" -#: models.py:1624 +#: machines/models.py:1833 msgid "SQL client" msgstr "Client SQL" -#: models.py:1625 +#: machines/models.py:1834 msgid "Gateway" msgstr "Passerelle" -#: models.py:1639 +#: machines/models.py:1842 msgid "Can view a role object" msgstr "Peut voir un objet rôle" -#: models.py:1641 +#: machines/models.py:1843 msgid "server role" msgstr "rôle de serveur" -#: models.py:1642 +#: machines/models.py:1844 msgid "server roles" msgstr "rôles de serveur" -#: models.py:1676 +#: machines/models.py:1876 msgid "Minimal time before regeneration of the service." msgstr "Temps minimal avant régénération du service." -#: models.py:1680 +#: machines/models.py:1880 msgid "Maximal time before regeneration of the service." msgstr "Temps maximal avant régénération du service." -#: models.py:1686 +#: machines/models.py:1885 msgid "Can view a service object" msgstr "Peut voir un objet service" -#: models.py:1688 +#: machines/models.py:1886 msgid "service to generate (DHCP, DNS, ...)" msgstr "service à générer (DHCP, DNS, ...)" -#: models.py:1689 +#: machines/models.py:1887 msgid "services to generate (DHCP, DNS, ...)" msgstr "services à générer (DHCP, DNS, ...)" -#: models.py:1735 +#: machines/models.py:1931 msgid "Can view a service server link object" msgstr "Peut voir un objet lien service serveur" -#: models.py:1737 +#: machines/models.py:1933 msgid "link between service and server" msgstr "lien entre service et serveur" -#: models.py:1738 +#: machines/models.py:1934 msgid "links between service and server" msgstr "liens entre service et serveur" -#: models.py:1780 +#: machines/models.py:1975 msgid "Name of the ports configuration" msgstr "Nom de la configuration de ports" -#: models.py:1786 +#: machines/models.py:1980 msgid "Can view a ports opening list object" msgstr "Peut voir un objet liste d'ouverture de ports" -#: models.py:1789 +#: machines/models.py:1983 msgid "ports opening list" msgstr "liste d'ouverture de ports" -#: models.py:1790 +#: machines/models.py:1984 msgid "ports opening lists" msgstr "listes d'ouverture de ports" -#: models.py:1799 +#: machines/models.py:1995 msgid "You don't have the right to delete a ports opening list." msgstr "Vous n'avez pas le droit de supprimer une liste d'ouverture de ports." -#: models.py:1802 +#: machines/models.py:1999 msgid "This ports opening list is used." msgstr "Cette liste d'ouverture de ports est utilisée." -#: models.py:1875 +#: machines/models.py:2053 msgid "ports opening" msgstr "ouverture de ports" -#: models.py:1876 +#: machines/models.py:2054 msgid "ports openings" msgstr "ouvertures de ports" -#: templates/machines/aff_alias.html:32 +#: machines/templates/machines/aff_alias.html:32 msgid "Aliases" msgstr "Alias" -#: templates/machines/aff_dname.html:30 -msgid "Target zone" -msgstr "Cible" - -#: templates/machines/aff_dname.html:31 templates/machines/aff_mx.html:34 -#: templates/machines/aff_txt.html:33 -msgid "Record" -msgstr "Enregistrement" - -#: templates/machines/aff_extension.html:34 -#: templates/machines/aff_iptype.html:36 templates/machines/aff_srv.html:34 -#: templates/machines/machine.html:116 -msgid "Extension" -msgstr "Extension" - -#: templates/machines/aff_extension.html:35 -#: templates/machines/aff_iptype.html:37 -msgid "'infra' right required" -msgstr "droit 'infra' requis" - -#: templates/machines/aff_extension.html:41 -msgid "DNSSEC" -msgstr "DNSSEC" - -#: templates/machines/aff_iptype.html:38 -msgid "IPv4 range" -msgstr "Plage IPv4" - -#: templates/machines/aff_iptype.html:39 -msgid "v6 prefix" -msgstr "Préfixe v6" - -#: templates/machines/aff_iptype.html:40 -msgid "DNSSEC reverse v4/v6" -msgstr "DNSSEC inverse v4/v6" - -#: templates/machines/aff_iptype.html:41 -msgid "On VLAN(s)" -msgstr "Sur VLAN(s)" - -#: templates/machines/aff_iptype.html:42 -msgid "Default ports opening" -msgstr "Ouverture de ports par défaut" - -#: templates/machines/aff_ipv6.html:32 -msgid "IPv6 addresses" -msgstr "Adresses IPv6" - -#: templates/machines/aff_ipv6.html:33 -msgid "SLAAC" -msgstr "SLAAC" - -#: templates/machines/aff_machines.html:43 -msgid "DNS name" -msgstr "Nom DNS" - -#: templates/machines/aff_machines.html:45 -msgid "Type" -msgstr "Type" - -#: templates/machines/aff_machines.html:47 -msgid "IP address" -msgstr "Adresse IP" - -#: templates/machines/aff_machines.html:48 -msgid "Actions" -msgstr "Actions" - -#: templates/machines/aff_machines.html:54 -msgid "View the profile" -msgstr "Voir le profil" - -#: templates/machines/aff_machines.html:62 views.py:374 -msgid "Create an interface" -msgstr "Créer une interface" - -#: templates/machines/aff_machines.html:79 -msgid "Display the aliases" -msgstr "Afficher les alias" - -#: templates/machines/aff_machines.html:99 -msgid "Display the IPv6 address" -msgstr "Afficher les adresses IPv6" - -#: templates/machines/aff_machines.html:116 -msgid " Edit" -msgstr " Modifier" - -#: templates/machines/aff_machines.html:124 -msgid " Manage the aliases" -msgstr " Gérer les alias" - -#: templates/machines/aff_machines.html:132 -msgid " Manage the IPv6 addresses" -msgstr " Gérer les adresses IPv6" - -#: templates/machines/aff_machines.html:140 -msgid " Manage the SSH fingerprints" -msgstr " Gérer les empreintes SSH" - -#: templates/machines/aff_machines.html:148 -msgid " Manage the ports configuration" -msgstr " Gérer les configuration de ports" - -#: templates/machines/aff_machinetype.html:33 -msgid "Matching IP type" -msgstr "Type d'IP correspondant" - -#: templates/machines/aff_mx.html:32 templates/machines/aff_ns.html:32 -#: templates/machines/aff_txt.html:32 -msgid "Concerned zone" -msgstr "Zone concernée" - -#: templates/machines/aff_mx.html:33 templates/machines/aff_srv.html:36 -msgid "Priority" -msgstr "Priorité" - -#: templates/machines/aff_nas.html:33 templates/machines/aff_soa.html:32 -#: templates/machines/aff_vlan.html:34 -#: templates/machines/index_portlist.html:20 -msgid "Name" -msgstr "Nom" - -#: templates/machines/aff_nas.html:34 -msgid "NAS device type" -msgstr "Type de dispositif NAS" - -#: templates/machines/aff_nas.html:35 -msgid "Machine type linked to the NAS device" -msgstr "Type de machine lié au dispositif NAS" - -#: templates/machines/aff_nas.html:36 -msgid "Access mode" -msgstr "Mode d'accès" - -#: templates/machines/aff_nas.html:37 -msgid "MAC address auto capture" -msgstr "Capture automatique de l'adresse MAC" - -#: templates/machines/aff_ns.html:33 -msgid "Authoritarian interface for the concerned zone" -msgstr "Interface authoritaire pour la zone concernée" - -#: templates/machines/aff_role.html:33 -msgid "Role name" -msgstr "Nom du rôle" - -#: templates/machines/aff_role.html:34 -msgid "Specific role" -msgstr "Rôle spécifique" - -#: templates/machines/aff_role.html:35 -msgid "Servers" -msgstr "Serveurs" - -#: templates/machines/aff_servers.html:31 -#: templates/machines/aff_service.html:32 -msgid "Service name" -msgstr "Nom du service" - -#: templates/machines/aff_servers.html:32 -msgid "Server" -msgstr "Serveur" - -#: templates/machines/aff_servers.html:33 -msgid "Last regeneration" -msgstr "Dernière régénération" - -#: templates/machines/aff_servers.html:34 -msgid "Regeneration required" -msgstr "Régénération requise" - -#: templates/machines/aff_servers.html:35 -msgid "Regeneration activated" -msgstr "Régénération activée" - -#: templates/machines/aff_service.html:33 -msgid "Minimal time before regeneration" -msgstr "Temps minimal avant régénération" - -#: templates/machines/aff_service.html:34 -msgid "Maximal time before regeneration" -msgstr "Temps maximal avant régénération" - -#: templates/machines/aff_service.html:35 -msgid "Included servers" -msgstr "Serveurs inclus" - -#: templates/machines/aff_service.html:36 -msgid "Ask for regeneration" -msgstr "Demander la régénération" - -#: templates/machines/aff_soa.html:33 -msgid "Mail" -msgstr "Mail" - -#: templates/machines/aff_soa.html:34 -msgid "Refresh" -msgstr "Rafraichissement" - -#: templates/machines/aff_soa.html:35 -msgid "Retry" -msgstr "Relance" - -#: templates/machines/aff_soa.html:36 -msgid "Expire" -msgstr "Expiration" - -#: templates/machines/aff_soa.html:37 templates/machines/aff_srv.html:35 +#: machines/templates/machines/aff_alias.html:33 +#: machines/templates/machines/aff_dname.html:32 +#: machines/templates/machines/aff_mx.html:35 +#: machines/templates/machines/aff_ns.html:34 +#: machines/templates/machines/aff_soa.html:37 +#: machines/templates/machines/aff_srv.html:35 +#: machines/templates/machines/aff_txt.html:34 msgid "TTL" msgstr "Temps de vie" -#: templates/machines/aff_srv.html:32 templates/machines/machine.html:152 +#: machines/templates/machines/aff_dname.html:30 +msgid "Target zone" +msgstr "Cible" + +#: machines/templates/machines/aff_dname.html:31 +#: machines/templates/machines/aff_mx.html:34 +#: machines/templates/machines/aff_txt.html:33 +msgid "Record" +msgstr "Enregistrement" + +#: machines/templates/machines/aff_extension.html:34 +#: machines/templates/machines/aff_iptype.html:36 +#: machines/templates/machines/aff_srv.html:34 +#: machines/templates/machines/machine.html:116 +msgid "Extension" +msgstr "Extension" + +#: machines/templates/machines/aff_extension.html:35 +#: machines/templates/machines/aff_iptype.html:37 +msgid "\"infra\" right required" +msgstr "droit « infra » requis" + +#: machines/templates/machines/aff_extension.html:41 +msgid "DNSSEC" +msgstr "DNSSEC" + +#: machines/templates/machines/aff_iptype.html:38 +msgid "IPv4 range" +msgstr "Plage IPv4" + +#: machines/templates/machines/aff_iptype.html:39 +msgid "v6 prefix" +msgstr "Préfixe v6" + +#: machines/templates/machines/aff_iptype.html:40 +msgid "DNSSEC reverse v4/v6" +msgstr "DNSSEC inverse v4/v6" + +#: machines/templates/machines/aff_iptype.html:41 +msgid "On VLAN(s)" +msgstr "Sur VLAN(s)" + +#: machines/templates/machines/aff_iptype.html:42 +msgid "Default ports opening" +msgstr "Ouverture de ports par défaut" + +#: machines/templates/machines/aff_ipv6.html:32 +msgid "IPv6 addresses" +msgstr "Adresses IPv6" + +#: machines/templates/machines/aff_ipv6.html:33 +msgid "SLAAC" +msgstr "SLAAC" + +#: machines/templates/machines/aff_machines.html:43 +msgid "DNS name" +msgstr "Nom DNS" + +#: machines/templates/machines/aff_machines.html:45 +msgid "Type" +msgstr "Type" + +#: machines/templates/machines/aff_machines.html:47 +msgid "IP address" +msgstr "Adresse IP" + +#: machines/templates/machines/aff_machines.html:48 +msgid "Actions" +msgstr "Actions" + +#: machines/templates/machines/aff_machines.html:54 +msgid "View the profile" +msgstr "Voir le profil" + +#: machines/templates/machines/aff_machines.html:58 +msgid "Deactivated" +msgstr "Désactivée" + +#: machines/templates/machines/aff_machines.html:67 +msgid "Create an interface" +msgstr "Créer une interface" + +#: machines/templates/machines/aff_machines.html:84 +msgid "Display the aliases" +msgstr "Afficher les alias" + +#: machines/templates/machines/aff_machines.html:98 +msgid "Display the vendor" +msgstr "Afficher le constructeur" + +#: machines/templates/machines/aff_machines.html:109 +msgid "Display the IPv6 address" +msgstr "Afficher les adresses IPv6" + +#: machines/templates/machines/aff_machines.html:126 machines/views.py:299 +#: machines/views.py:413 machines/views.py:469 machines/views.py:520 +#: machines/views.py:583 machines/views.py:646 machines/views.py:708 +#: machines/views.py:760 machines/views.py:812 machines/views.py:864 +#: machines/views.py:919 machines/views.py:971 machines/views.py:1033 +#: machines/views.py:1089 machines/views.py:1141 machines/views.py:1207 +#: machines/views.py:1259 machines/views.py:1575 +msgid "Edit" +msgstr "Modifier" + +#: machines/templates/machines/aff_machines.html:134 +msgid "Manage the aliases" +msgstr "Gérer les alias" + +#: machines/templates/machines/aff_machines.html:142 +msgid "Manage the IPv6 addresses" +msgstr "Gérer les adresses IPv6" + +#: machines/templates/machines/aff_machines.html:150 +msgid "Manage the SSH fingerprints" +msgstr "Gérer les empreintes SSH" + +#: machines/templates/machines/aff_machines.html:158 +msgid "Manage the ports configuration" +msgstr "Gérer les configuration de ports" + +#: machines/templates/machines/aff_machinetype.html:33 +msgid "Matching IP type" +msgstr "Type d'IP correspondant" + +#: machines/templates/machines/aff_mx.html:32 +#: machines/templates/machines/aff_ns.html:32 +#: machines/templates/machines/aff_txt.html:32 +msgid "Concerned zone" +msgstr "Zone concernée" + +#: machines/templates/machines/aff_mx.html:33 +#: machines/templates/machines/aff_srv.html:36 +msgid "Priority" +msgstr "Priorité" + +#: machines/templates/machines/aff_nas.html:33 +#: machines/templates/machines/aff_soa.html:32 +#: machines/templates/machines/aff_vlan.html:34 +#: machines/templates/machines/index_portlist.html:20 +msgid "Name" +msgstr "Nom" + +#: machines/templates/machines/aff_nas.html:34 +msgid "NAS device type" +msgstr "Type de dispositif NAS" + +#: machines/templates/machines/aff_nas.html:35 +msgid "Machine type linked to the NAS device" +msgstr "Type de machine lié au dispositif NAS" + +#: machines/templates/machines/aff_nas.html:36 +msgid "Access mode" +msgstr "Mode d'accès" + +#: machines/templates/machines/aff_nas.html:37 +msgid "MAC address auto capture" +msgstr "Capture automatique de l'adresse MAC" + +#: machines/templates/machines/aff_ns.html:33 +msgid "Authoritarian interface for the concerned zone" +msgstr "Interface authoritaire pour la zone concernée" + +#: machines/templates/machines/aff_role.html:33 +msgid "Role name" +msgstr "Nom du rôle" + +#: machines/templates/machines/aff_role.html:34 +msgid "Specific role" +msgstr "Rôle spécifique" + +#: machines/templates/machines/aff_role.html:35 +msgid "Servers" +msgstr "Serveurs" + +#: machines/templates/machines/aff_servers.html:31 +#: machines/templates/machines/aff_service.html:32 +msgid "Service name" +msgstr "Nom du service" + +#: machines/templates/machines/aff_servers.html:32 +msgid "Server" +msgstr "Serveur" + +#: machines/templates/machines/aff_servers.html:33 +msgid "Last regeneration" +msgstr "Dernière régénération" + +#: machines/templates/machines/aff_servers.html:34 +msgid "Regeneration asked" +msgstr "Régénération demandée" + +#: machines/templates/machines/aff_servers.html:35 +msgid "Regeneration needed" +msgstr "Régénération requise" + +#: machines/templates/machines/aff_service.html:33 +msgid "Minimal time before regeneration" +msgstr "Temps minimal avant régénération" + +#: machines/templates/machines/aff_service.html:34 +msgid "Maximal time before regeneration" +msgstr "Temps maximal avant régénération" + +#: machines/templates/machines/aff_service.html:35 +msgid "Included servers" +msgstr "Serveurs inclus" + +#: machines/templates/machines/aff_service.html:36 +msgid "Ask for regeneration" +msgstr "Demander la régénération" + +#: machines/templates/machines/aff_soa.html:33 +msgid "Mail" +msgstr "Mail" + +#: machines/templates/machines/aff_soa.html:34 +msgid "Refresh" +msgstr "Rafraichissement" + +#: machines/templates/machines/aff_soa.html:35 +msgid "Retry" +msgstr "Relance" + +#: machines/templates/machines/aff_soa.html:36 +msgid "Expire" +msgstr "Expiration" + +#: machines/templates/machines/aff_srv.html:32 +#: machines/templates/machines/machine.html:152 msgid "Service" msgstr "Service" -#: templates/machines/aff_srv.html:33 +#: machines/templates/machines/aff_srv.html:33 msgid "Protocol" msgstr "Protocole" -#: templates/machines/aff_srv.html:37 +#: machines/templates/machines/aff_srv.html:37 msgid "Weight" msgstr "Poids" -#: templates/machines/aff_srv.html:38 +#: machines/templates/machines/aff_srv.html:38 msgid "Port" msgstr "Port" -#: templates/machines/aff_srv.html:39 +#: machines/templates/machines/aff_srv.html:39 msgid "Target" msgstr "Cible" -#: templates/machines/aff_sshfp.html:32 +#: machines/templates/machines/aff_sshfp.html:31 +msgid "SSH public key" +msgstr "Clé publique SSH" + +#: machines/templates/machines/aff_sshfp.html:32 msgid "Algorithm used" msgstr "Algorithme utilisé" -#: templates/machines/aff_vlan.html:33 +#: machines/templates/machines/aff_sshfp.html:33 +#: machines/templates/machines/aff_vlan.html:35 +msgid "Comment" +msgstr "Commentaire" + +#: machines/templates/machines/aff_vlan.html:33 msgid "ID" msgstr "ID" -#: templates/machines/aff_vlan.html:36 templates/machines/sidebar.html:51 +#: machines/templates/machines/aff_vlan.html:36 +#: machines/templates/machines/sidebar.html:51 msgid "IP ranges" msgstr "Plages d'IP" -#: templates/machines/delete.html:29 +#: machines/templates/machines/delete.html:29 msgid "Deletion of machines" msgstr "Suppression de machines" -#: templates/machines/delete.html:35 +#: machines/templates/machines/delete.html:35 #, python-format msgid "" "Warning: are you sure you want to delete this object %(objet_name)s " @@ -1081,167 +1140,168 @@ msgstr "" "Attention : voulez-vous vraiment supprimer cet objet %(objet_name)s " "( %(objet)s ) ?" -#: templates/machines/delete.html:36 +#: machines/templates/machines/delete.html:36 +#: machines/templates/machines/edit_portlist.html:52 msgid "Confirm" msgstr "Confirmer" -#: templates/machines/edit_portlist.html:29 templates/machines/index.html:29 -#: templates/machines/index.html:32 templates/machines/index_alias.html:29 -#: templates/machines/index_extension.html:30 -#: templates/machines/index_iptype.html:30 -#: templates/machines/index_ipv6.html:30 -#: templates/machines/index_machinetype.html:31 -#: templates/machines/index_nas.html:31 -#: templates/machines/index_portlist.html:8 -#: templates/machines/index_portlist.html:25 -#: templates/machines/index_role.html:30 -#: templates/machines/index_service.html:30 -#: templates/machines/index_sshfp.html:28 templates/machines/index_vlan.html:30 -#: templates/machines/machine.html:31 templates/machines/sidebar.html:33 +#: machines/templates/machines/edit_portlist.html:29 +#: machines/templates/machines/index.html:29 +#: machines/templates/machines/index.html:32 +#: machines/templates/machines/index_alias.html:29 +#: machines/templates/machines/index_extension.html:30 +#: machines/templates/machines/index_iptype.html:30 +#: machines/templates/machines/index_ipv6.html:30 +#: machines/templates/machines/index_machinetype.html:31 +#: machines/templates/machines/index_nas.html:31 +#: machines/templates/machines/index_portlist.html:8 +#: machines/templates/machines/index_portlist.html:25 +#: machines/templates/machines/index_role.html:30 +#: machines/templates/machines/index_service.html:30 +#: machines/templates/machines/index_sshfp.html:28 +#: machines/templates/machines/index_vlan.html:30 +#: machines/templates/machines/machine.html:31 +#: machines/templates/machines/sidebar.html:33 msgid "Machines" msgstr "Machines" -#: templates/machines/edit_portlist.html:50 +#: machines/templates/machines/edit_portlist.html:50 msgid "Add a port" msgstr "Ajouter un port" -#: templates/machines/edit_portlist.html:53 -msgid "Create or edit" -msgstr "Créer ou modifier" - -#: templates/machines/index_alias.html:32 +#: machines/templates/machines/index_alias.html:32 msgid "List of the aliases of the interface" msgstr "Liste des alias de l'interface" -#: templates/machines/index_alias.html:34 -msgid " Add an alias" -msgstr " Ajouter un alias" +#: machines/templates/machines/index_alias.html:34 +msgid "Add an alias" +msgstr "Ajouter un alias" -#: templates/machines/index_alias.html:36 -msgid " Delete one or several aliases" -msgstr " Supprimer un ou plusieurs alias" +#: machines/templates/machines/index_alias.html:36 +msgid "Delete one or several aliases" +msgstr "Supprimer un ou plusieurs alias" -#: templates/machines/index_extension.html:33 +#: machines/templates/machines/index_extension.html:33 msgid "List of extensions" msgstr "Liste des extensions" -#: templates/machines/index_extension.html:37 -msgid " Add an extension" -msgstr " Ajouter une extension" +#: machines/templates/machines/index_extension.html:36 +msgid "Add an extension" +msgstr "Ajouter une extension" -#: templates/machines/index_extension.html:40 -msgid " Delete one or several extensions" -msgstr " Supprimer une ou plusieurs extensions" +#: machines/templates/machines/index_extension.html:39 +msgid "Delete one or several extensions" +msgstr "Supprimer une ou plusieurs extensions" -#: templates/machines/index_extension.html:44 +#: machines/templates/machines/index_extension.html:43 msgid "List of SOA records" msgstr "Liste des enregistrements SOA" -#: templates/machines/index_extension.html:47 -msgid " Add an SOA record" -msgstr " Ajouter un enregistrement SOA" +#: machines/templates/machines/index_extension.html:46 +msgid "Add an SOA record" +msgstr "Ajouter un enregistrement SOA" -#: templates/machines/index_extension.html:51 -msgid " Delete one or several SOA records" -msgstr " Supprimer un ou plusieurs enregistrements SOA" +#: machines/templates/machines/index_extension.html:50 +msgid "Delete one or several SOA records" +msgstr "Supprimer un ou plusieurs enregistrements SOA" -#: templates/machines/index_extension.html:55 +#: machines/templates/machines/index_extension.html:54 msgid "List of MX records" msgstr "Liste des enregistrements MX" -#: templates/machines/index_extension.html:58 -msgid " Add an MX record" -msgstr " Ajouter un enregistrement MX" +#: machines/templates/machines/index_extension.html:57 +msgid "Add an MX record" +msgstr "Ajouter un enregistrement MX" -#: templates/machines/index_extension.html:62 -msgid " Delete one or several MX records" -msgstr " Supprimer un ou plusieurs enregistrements MX" +#: machines/templates/machines/index_extension.html:61 +msgid "Delete one or several MX records" +msgstr "Supprimer un ou plusieurs enregistrements MX" -#: templates/machines/index_extension.html:66 +#: machines/templates/machines/index_extension.html:65 msgid "List of NS records" msgstr "Liste des enregistrements NS" -#: templates/machines/index_extension.html:69 -msgid " Add an NS record" -msgstr " Ajouter un enregistrement NS" +#: machines/templates/machines/index_extension.html:68 +msgid "Add an NS record" +msgstr "Ajouter un enregistrement NS" -#: templates/machines/index_extension.html:73 -msgid " Delete one or several NS records" -msgstr " Supprimer un ou plusieurs enregistrements NS" +#: machines/templates/machines/index_extension.html:72 +msgid "Delete one or several NS records" +msgstr "Supprimer un ou plusieurs enregistrements NS" -#: templates/machines/index_extension.html:77 +#: machines/templates/machines/index_extension.html:76 msgid "List of TXT records" msgstr "Liste des enregistrements TXT" -#: templates/machines/index_extension.html:80 -msgid " Add a TXT record" -msgstr " Ajouter un enregistrement TXT" +#: machines/templates/machines/index_extension.html:79 +msgid "Add a TXT record" +msgstr "Ajouter un enregistrement TXT" -#: templates/machines/index_extension.html:84 -msgid " Delete one or several TXT records" -msgstr " Supprimer un ou plusieurs enregistrements TXT" +#: machines/templates/machines/index_extension.html:83 +msgid "Delete one or several TXT records" +msgstr "Supprimer un ou plusieurs enregistrements TXT" -#: templates/machines/index_extension.html:88 +#: machines/templates/machines/index_extension.html:87 msgid "List of DNAME records" msgstr "Liste des enregistrements DNAME" -#: templates/machines/index_extension.html:91 -msgid " Add a DNAME record" -msgstr " Ajouter un enregistrement DNAME" +#: machines/templates/machines/index_extension.html:90 +msgid "Add a DNAME record" +msgstr "Ajouter un enregistrement DNAME" -#: templates/machines/index_extension.html:95 -msgid " Delete one or several DNAME records" -msgstr " Supprimer un ou plusieurs enregistrements DNAME" +#: machines/templates/machines/index_extension.html:94 +msgid "Delete one or several DNAME records" +msgstr "Supprimer un ou plusieurs enregistrements DNAME" -#: templates/machines/index_extension.html:99 +#: machines/templates/machines/index_extension.html:98 msgid "List of SRV records" msgstr "Liste des enregistrements SRV" -#: templates/machines/index_extension.html:102 -msgid " Add an SRV record" -msgstr " Ajouter un enregistrement SRV" +#: machines/templates/machines/index_extension.html:101 +msgid "Add an SRV record" +msgstr "Ajouter un enregistrement SRV" -#: templates/machines/index_extension.html:106 -msgid " Delete one or several SRV records" -msgstr " Supprimer un ou plusieurs enregistrements SRV" +#: machines/templates/machines/index_extension.html:105 +msgid "Delete one or several SRV records" +msgstr "Supprimer un ou plusieurs enregistrements SRV" -#: templates/machines/index_iptype.html:33 +#: machines/templates/machines/index_iptype.html:33 msgid "List of IP types" msgstr "Liste des types d'IP" -#: templates/machines/index_iptype.html:36 -msgid " Add an IP type" -msgstr " Ajouter un type d'IP" +#: machines/templates/machines/index_iptype.html:36 +msgid "Add an IP type" +msgstr "Ajouter un type d'IP" -#: templates/machines/index_iptype.html:40 -msgid " Delete one or several IP types" -msgstr " Supprimer un ou plusieurs types d'IP" +#: machines/templates/machines/index_iptype.html:40 +msgid "Delete one or several IP types" +msgstr "Supprimer un ou plusieurs types d'IP" -#: templates/machines/index_ipv6.html:33 +#: machines/templates/machines/index_ipv6.html:33 msgid "List of the IPv6 addresses of the interface" msgstr "Liste des adresses IPv6 de l'interface" -#: templates/machines/index_ipv6.html:36 -msgid " Add an IPv6 address" -msgstr " Ajouter une adresse IPv6" +#: machines/templates/machines/index_ipv6.html:36 +msgid "Add an IPv6 address" +msgstr "Ajouter une adresse IPv6" -#: templates/machines/index_machinetype.html:34 +#: machines/templates/machines/index_machinetype.html:34 msgid "List of machine types" msgstr "Liste des types de machine" -#: templates/machines/index_machinetype.html:37 -msgid " Add a machine type" -msgstr " Ajouter un type de machine" +#: machines/templates/machines/index_machinetype.html:37 +msgid "Add a machine type" +msgstr "Ajouter un type de machine" -#: templates/machines/index_machinetype.html:41 -msgid " Delete one or several machine types" -msgstr " Supprimer un ou plusieurs types de machine" +#: machines/templates/machines/index_machinetype.html:41 +msgid "Delete one or several machine types" +msgstr "Supprimer un ou plusieurs types de machine" -#: templates/machines/index_nas.html:34 +#: machines/templates/machines/index_nas.html:34 msgid "List of NAS devices" msgstr "Liste des dispositifs NAS" -#: templates/machines/index_nas.html:35 +#: machines/templates/machines/index_nas.html:35 msgid "" "The NAS device type and machine type are linked. It is useful for MAC " "address auto capture by RADIUS, and allows to choose the machine type to " @@ -1252,210 +1312,196 @@ msgstr "" "type de machine à assigner aux machines en fonction du type de dispositif " "NAS." -#: templates/machines/index_nas.html:38 -msgid " Add a NAS device type" -msgstr " Ajouter un type de dispositif NAS" +#: machines/templates/machines/index_nas.html:38 +msgid "Add a NAS device type" +msgstr "Ajouter un type de dispositif NAS" -#: templates/machines/index_nas.html:42 -msgid " Delete one or several NAS device types" -msgstr " Supprimer un ou plusieurs types de dispositif NAS" +#: machines/templates/machines/index_nas.html:42 +msgid "Delete one or several NAS device types" +msgstr "Supprimer un ou plusieurs types de dispositif NAS" -#: templates/machines/index_portlist.html:11 +#: machines/templates/machines/index_portlist.html:11 msgid "List of ports configurations" msgstr "Liste des configurations de ports" -#: templates/machines/index_portlist.html:14 -msgid " Add a configuration" -msgstr " Ajouter une configuration" +#: machines/templates/machines/index_portlist.html:14 +msgid "Add a configuration" +msgstr "Ajouter une configuration" -#: templates/machines/index_portlist.html:21 +#: machines/templates/machines/index_portlist.html:21 msgid "TCP (input)" msgstr "TCP (entrée)" -#: templates/machines/index_portlist.html:22 +#: machines/templates/machines/index_portlist.html:22 msgid "TCP (output)" msgstr "TCP (sortie)" -#: templates/machines/index_portlist.html:23 +#: machines/templates/machines/index_portlist.html:23 msgid "UDP (input)" msgstr "UDP (entrée)" -#: templates/machines/index_portlist.html:24 +#: machines/templates/machines/index_portlist.html:24 msgid "UDP (output)" msgstr "UDP (sortie)" -#: templates/machines/index_role.html:33 +#: machines/templates/machines/index_role.html:33 msgid "List of roles" msgstr "Liste des rôles" -#: templates/machines/index_role.html:36 -msgid " Add a role" -msgstr " Ajouter un rôle" +#: machines/templates/machines/index_role.html:36 +msgid "Add a role" +msgstr "Ajouter un rôle" -#: templates/machines/index_role.html:39 -msgid " Delete one or several roles" -msgstr " Supprimer un ou plusieurs rôles" +#: machines/templates/machines/index_role.html:39 +msgid "Delete one or several roles" +msgstr "Supprimer un ou plusieurs rôles" -#: templates/machines/index_service.html:33 +#: machines/templates/machines/index_service.html:33 msgid "List of services" msgstr "Liste des services" -#: templates/machines/index_service.html:36 -msgid " Add a service" -msgstr " Ajouter un service" +#: machines/templates/machines/index_service.html:36 +msgid "Add a service" +msgstr "Ajouter un service" -#: templates/machines/index_service.html:39 -msgid " Delete one or several services" -msgstr " Supprimer un ou plusieurs services" +#: machines/templates/machines/index_service.html:39 +msgid "Delete one or several services" +msgstr "Supprimer un ou plusieurs services" -#: templates/machines/index_service.html:42 +#: machines/templates/machines/index_service.html:42 msgid "States of servers" msgstr "États des serveurs" -#: templates/machines/index_sshfp.html:31 +#: machines/templates/machines/index_sshfp.html:31 msgid "SSH fingerprints" msgstr "Empreintes SSH" -#: templates/machines/index_sshfp.html:34 -msgid " Add an SSH fingerprint" -msgstr " Ajouter une empreinte SSH" +#: machines/templates/machines/index_sshfp.html:34 +msgid "Add an SSH fingerprint" +msgstr "Ajouter une empreinte SSH" -#: templates/machines/index_vlan.html:33 +#: machines/templates/machines/index_vlan.html:33 msgid "List of VLANs" msgstr "Liste des VLANs" -#: templates/machines/index_vlan.html:36 -msgid " Add a VLAN" -msgstr " Ajouter un VLAN" +#: machines/templates/machines/index_vlan.html:36 +msgid "Add a VLAN" +msgstr "Ajouter un VLAN" -#: templates/machines/index_vlan.html:39 -msgid " Delete one or several VLANs" -msgstr " Supprimer un ou plusieurs VLANs" +#: machines/templates/machines/index_vlan.html:39 +msgid "Delete one or several VLANs" +msgstr "Supprimer un ou plusieurs VLANs" -#: templates/machines/machine.html:92 +#: machines/templates/machines/machine.html:92 msgid "Machine" msgstr "Machine" -#: templates/machines/machine.html:96 +#: machines/templates/machines/machine.html:96 msgid "Interface" msgstr "Interface" -#: templates/machines/machine.html:104 +#: machines/templates/machines/machine.html:104 msgid "Domain" msgstr "Domaine" -#: templates/machines/machine.html:148 +#: machines/templates/machines/machine.html:148 msgid "Alias" msgstr "Alias" -#: templates/machines/machine.html:168 +#: machines/templates/machines/machine.html:168 msgid "IPv6 address" msgstr "Adresse IPv6" -#: templates/machines/sidebar.html:39 +#: machines/templates/machines/sidebar.html:39 msgid "Machine types" msgstr "Types de machine" -#: templates/machines/sidebar.html:45 +#: machines/templates/machines/sidebar.html:45 msgid "Extensions and zones" msgstr "Extensions et zones" -#: templates/machines/sidebar.html:69 +#: machines/templates/machines/sidebar.html:69 msgid "Services (DHCP, DNS, ...)" msgstr "Services (DHCP, DNS, ...)" -#: templates/machines/sidebar.html:75 +#: machines/templates/machines/sidebar.html:75 msgid "Server roles" msgstr "Rôles de serveur" -#: templates/machines/sidebar.html:81 +#: machines/templates/machines/sidebar.html:81 msgid "Ports openings" msgstr "Ouvertures de ports" -#: views.py:155 +#: machines/views.py:153 msgid "Select a machine type first." msgstr "Sélectionnez un type de machine d'abord." -#: views.py:257 +#: machines/views.py:244 msgid "The machine was created." msgstr "La machine a été créée." -#: views.py:269 -msgid "Create a machine" -msgstr "Créer une machine" +#: machines/views.py:253 machines/views.py:348 machines/views.py:389 +#: machines/views.py:447 machines/views.py:501 machines/views.py:566 +#: machines/views.py:629 machines/views.py:691 machines/views.py:743 +#: machines/views.py:795 machines/views.py:847 machines/views.py:902 +#: machines/views.py:954 machines/views.py:1011 machines/views.py:1072 +#: machines/views.py:1124 machines/views.py:1190 machines/views.py:1242 +msgid "Add" +msgstr "Ajouter" -#: views.py:309 +#: machines/views.py:285 msgid "The machine was edited." msgstr "La machine a été modifiée." -#: views.py:321 views.py:445 views.py:511 views.py:567 views.py:629 -#: views.py:690 views.py:748 views.py:805 views.py:862 views.py:919 -#: views.py:977 views.py:1034 views.py:1106 views.py:1169 views.py:1226 -#: views.py:1292 views.py:1349 -msgid "Edit" -msgstr "Modifier" - -#: views.py:334 +#: machines/views.py:312 msgid "The machine was deleted." msgstr "La machine a été supprimée." -#: views.py:363 +#: machines/views.py:338 msgid "The interface was created." msgstr "L'interface a été créée." -#: views.py:390 +#: machines/views.py:364 msgid "The interface was deleted." msgstr "L'interface a été supprimée." -#: views.py:415 +#: machines/views.py:384 msgid "The IPv6 addresses list was created." msgstr "La liste d'adresses IPv6 a été créée." -#: views.py:421 -msgid "Create an IPv6 addresses list" -msgstr "Créer une liste d'adresses IPv6" - -#: views.py:439 +#: machines/views.py:405 msgid "The IPv6 addresses list was edited." msgstr "La liste d'adresses IPv6 a été modifiée." -#: views.py:458 +#: machines/views.py:424 msgid "The IPv6 addresses list was deleted." msgstr "La liste d'adresses IPv6 a été supprimée." -#: views.py:482 +#: machines/views.py:442 msgid "The SSHFP record was created." msgstr "L'enregistrement SSHFP a été créé." -#: views.py:488 -msgid "Create a SSHFP record" -msgstr "Créer un enregistrement SSHFP" - -#: views.py:505 +#: machines/views.py:461 msgid "The SSHFP record was edited." msgstr "L'enregistrement SSHFP a été modifié." -#: views.py:524 +#: machines/views.py:480 msgid "The SSHFP record was deleted." msgstr "L'enregistrement SSHFP a été supprimé." -#: views.py:545 +#: machines/views.py:498 msgid "The IP type was created." msgstr "Le type d'IP a été créé." -#: views.py:548 -msgid "Create an IP type" -msgstr "Créer un type d'IP" - -#: views.py:564 +#: machines/views.py:517 msgid "The IP type was edited." msgstr "Le type d'IP a été modifié." -#: views.py:583 +#: machines/views.py:536 msgid "The IP type was deleted." msgstr "Le type d'IP a été supprimé." -#: views.py:587 +#: machines/views.py:542 #, python-format msgid "" "The IP type %s is assigned to at least one machine, you can't delete it." @@ -1463,29 +1509,27 @@ msgstr "" "Le type d'IP %s est assigné à au moins une machine, vous ne pouvez pas le " "supprimer." -#: views.py:592 views.py:654 views.py:715 views.py:772 views.py:829 -#: views.py:886 views.py:944 views.py:1001 views.py:1058 views.py:1136 -#: views.py:1193 views.py:1250 views.py:1316 views.py:1373 +#: machines/views.py:550 machines/views.py:613 machines/views.py:675 +#: machines/views.py:729 machines/views.py:781 machines/views.py:833 +#: machines/views.py:886 machines/views.py:940 machines/views.py:992 +#: machines/views.py:1056 machines/views.py:1110 machines/views.py:1165 +#: machines/views.py:1228 machines/views.py:1280 msgid "Delete" msgstr "Supprimer" -#: views.py:605 +#: machines/views.py:563 msgid "The machine type was created." msgstr "Le type de machine a été créé." -#: views.py:608 -msgid "Create a machine type" -msgstr "Créer un type de machine" - -#: views.py:626 +#: machines/views.py:580 msgid "The machine type was edited." msgstr "Le type de machine a été modifié." -#: views.py:645 +#: machines/views.py:599 msgid "The machine type was deleted." msgstr "Le type de machine a été supprimé." -#: views.py:649 +#: machines/views.py:605 #, python-format msgid "" "The machine type %s is assigned to at least one machine, you can't delete it." @@ -1493,23 +1537,19 @@ msgstr "" "Le type de machine %s est assigné à au moins un machine, vous ne pouvez pas " "le supprimer." -#: views.py:667 +#: machines/views.py:626 msgid "The extension was created." msgstr "L'extension a été créée." -#: views.py:670 -msgid "Create an extension" -msgstr "Créer une extension" - -#: views.py:687 +#: machines/views.py:643 msgid "The extension was edited." msgstr "L'extension a été modifiée." -#: views.py:706 +#: machines/views.py:662 msgid "The extension was deleted." msgstr "L'extension a été supprimée." -#: views.py:710 +#: machines/views.py:668 #, python-format msgid "" "The extension %s is assigned to at least one machine type, you can't delete " @@ -1518,260 +1558,214 @@ msgstr "" "L'extension %s est assignée à au moins un type de machine, vous ne pouvez " "pas le supprimer." -#: views.py:728 +#: machines/views.py:688 msgid "The SOA record was created." msgstr "L'enregistrement SOA a été créé." -#: views.py:731 -msgid "Create an SOA record" -msgstr "Créer un enregistrement SOA" - -#: views.py:745 +#: machines/views.py:705 msgid "The SOA record was edited." msgstr "L'enregistrement SOA a été modifié." -#: views.py:764 +#: machines/views.py:722 msgid "The SOA record was deleted." msgstr "L'enregistrement SOA a été supprimé." -#: views.py:768 +#: machines/views.py:725 #, python-format msgid "Error: the SOA record %s can't be deleted." msgstr "Erreur : l'enregistrement SOA %s ne peut pas être supprimé." -#: views.py:785 +#: machines/views.py:740 msgid "The MX record was created." msgstr "L'enregistrement MX a été créé." -#: views.py:788 -msgid "Create an MX record" -msgstr "Créer un enregistrement MX" - -#: views.py:802 +#: machines/views.py:757 msgid "The MX record was edited." msgstr "L'enregistrement MX a été modifié." -#: views.py:821 +#: machines/views.py:774 msgid "The MX record was deleted." msgstr "L'enregistrement MX a été supprimé." -#: views.py:825 +#: machines/views.py:777 #, python-format msgid "Error: the MX record %s can't be deleted." msgstr "Erreur : l'enregistrement MX %s ne peut pas être supprimé." -#: views.py:842 +#: machines/views.py:792 msgid "The NS record was created." msgstr "L'enregistrement NS a été créé." -#: views.py:845 -msgid "Create an NS record" -msgstr "Créer un enregistrement NS" - -#: views.py:859 +#: machines/views.py:809 msgid "The NS record was edited." msgstr "L'enregistrement NS a été modifié." -#: views.py:878 +#: machines/views.py:826 msgid "The NS record was deleted." msgstr "L'enregistrement NS a été supprimé." -#: views.py:882 +#: machines/views.py:829 #, python-format msgid "Error: the NS record %s can't be deleted." msgstr "Erreur : l'enregistrement NS %s ne peut pas être supprimé." -#: views.py:899 +#: machines/views.py:844 msgid "The DNAME record was created." msgstr "L'enregistrement DNAME a été créé." -#: views.py:902 -msgid "Create a DNAME record" -msgstr "Créer un enregistrement DNAME" - -#: views.py:916 +#: machines/views.py:861 msgid "The DNAME record was edited." msgstr "L'enregistrement DNAME a été modifié." -#: views.py:935 +#: machines/views.py:878 msgid "The DNAME record was deleted." msgstr "L'enregistrement DNAME a été supprimé." -#: views.py:939 +#: machines/views.py:882 #, python-format msgid "Error: the DNAME record %s can't be deleted." msgstr "Erreur : l'enregistrement DNAME %s ne peut pas être supprimé." -#: views.py:957 +#: machines/views.py:899 msgid "The TXT record was created." msgstr "L'enregistrement TXT a été créé." -#: views.py:960 -msgid "Create a TXT record" -msgstr "Créer un enregistrement TXT" - -#: views.py:974 +#: machines/views.py:916 msgid "The TXT record was edited." msgstr "L'enregistrement TXT a été modifié." -#: views.py:993 +#: machines/views.py:933 msgid "The TXT record was deleted." msgstr "L'enregistrement TXT a été supprimé." -#: views.py:997 +#: machines/views.py:936 #, python-format msgid "Error: the TXT record %s can't be deleted." msgstr "Erreur : l'enregistrement %s ne peut pas être supprimé." -#: views.py:1014 +#: machines/views.py:951 msgid "The SRV record was created." msgstr "L'enregistrement SRV a été créé." -#: views.py:1017 -msgid "Create an SRV record" -msgstr "Créer un enregistrement SRV" - -#: views.py:1031 +#: machines/views.py:968 msgid "The SRV record was edited." msgstr "L'enregistrement SRV a été modifié." -#: views.py:1050 +#: machines/views.py:985 msgid "The SRV record was deleted." msgstr "L'enregistrement SRV a été supprimé." -#: views.py:1054 +#: machines/views.py:988 #, python-format msgid "Error: the SRV record %s can't be deleted." msgstr "Erreur : l'enregistrement SRV %s ne peut pas être supprimé." -#: views.py:1074 +#: machines/views.py:1006 msgid "The alias was created." msgstr "L'alias a été créé." -#: views.py:1080 -msgid "Create an alias" -msgstr "Créer un alias" - -#: views.py:1098 +#: machines/views.py:1025 msgid "The alias was edited." msgstr "L'alias a été modifié." -#: views.py:1124 +#: machines/views.py:1047 #, python-format msgid "The alias %s was deleted." msgstr "L'alias %s a été supprimé." -#: views.py:1129 +#: machines/views.py:1050 #, python-format msgid "Error: the alias %s can't be deleted." msgstr "Erreur : l'alias %s ne peut pas être supprimé." -#: views.py:1149 +#: machines/views.py:1069 msgid "The role was created." msgstr "Le rôle a été créé." -#: views.py:1152 -msgid "Create a role" -msgstr "Créer un rôle" - -#: views.py:1166 +#: machines/views.py:1086 msgid "The role was edited." msgstr "Le rôle a été modifié." -#: views.py:1185 +#: machines/views.py:1103 msgid "The role was deleted." msgstr "Le rôle a été supprimé." -#: views.py:1189 +#: machines/views.py:1106 #, python-format msgid "Error: the role %s can't be deleted." msgstr "Erreur : le rôle %s ne peut pas être supprimé." -#: views.py:1206 +#: machines/views.py:1121 msgid "The service was created." msgstr "Le service a été créé." -#: views.py:1209 -msgid "Create a service" -msgstr "Créer un service" - -#: views.py:1223 +#: machines/views.py:1138 msgid "The service was edited." msgstr "Le service a été modifié." -#: views.py:1242 +#: machines/views.py:1157 msgid "The service was deleted." msgstr "Le service a été supprimé." -#: views.py:1246 +#: machines/views.py:1161 #, python-format msgid "Error: the service %s can't be deleted." msgstr "Erreur : le service %s ne peut pas être supprimé." -#: views.py:1272 +#: machines/views.py:1187 msgid "The VLAN was created." msgstr "Le VLAN a été créé." -#: views.py:1275 -msgid "Create a VLAN" -msgstr "Créer un VLAN" - -#: views.py:1289 +#: machines/views.py:1204 msgid "The VLAN was edited." msgstr "Le VLAN a été modifié." -#: views.py:1308 +#: machines/views.py:1221 msgid "The VLAN was deleted." msgstr "Le VLAN a été supprimé." -#: views.py:1312 +#: machines/views.py:1224 #, python-format msgid "Error: the VLAN %s can't be deleted." msgstr "Erreur : le VLAN %s ne peut pas être supprimé." -#: views.py:1329 +#: machines/views.py:1239 msgid "The NAS device was created." msgstr "Le dispositif NAS a été créé." -#: views.py:1332 -msgid "Create a NAS device" -msgstr "Créer un dispositif NAS" - -#: views.py:1346 +#: machines/views.py:1256 msgid "The NAS device was edited." msgstr "Le dispositif NAS a été modifié." -#: views.py:1365 +#: machines/views.py:1273 msgid "The NAS device was deleted." msgstr "Le dispositif NAS a été supprimé." -#: views.py:1369 +#: machines/views.py:1276 #, python-format msgid "Error: the NAS device %s can't be deleted." msgstr "Erreur : le dispositif NAS %s ne peut pas être supprimé." -#: views.py:1625 +#: machines/views.py:1502 msgid "The ports list was edited." msgstr "La liste de ports a été modifiée." -#: views.py:1639 +#: machines/views.py:1516 msgid "The ports list was deleted." msgstr "La liste de ports a été supprimée." -#: views.py:1664 +#: machines/views.py:1541 msgid "The ports list was created." msgstr "La liste de ports a été créée." -#: views.py:1682 -msgid "Warning: the IPv4 isn't public, the opening won't have effect in v4." +#: machines/views.py:1561 +msgid "" +"Warning: the IP address is not public, the opening won't have any effect in " +"v4." msgstr "" -"Attention : l'adresse IPv4 n'est pas publique, l'ouverture n'aura pas " -"d'effet en v4." +"Attention : l'adresse IP n'est pas publique, l'ouverture n'aura pas d'effet " +"en v4." -#: views.py:1692 +#: machines/views.py:1572 msgid "The ports configuration was edited." msgstr "La configuration de ports a été modifiée." - -#: views.py:1695 -msgid "Edit the configuration" -msgstr "Modifier la configuration" diff --git a/machines/migrations/0001_initial.py b/machines/migrations/0001_initial.py index b9f1bb9f..e253acdd 100644 --- a/machines/migrations/0001_initial.py +++ b/machines/migrations/0001_initial.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,32 +29,50 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('users', '0005_auto_20160702_0006'), - ] + dependencies = [("users", "0005_auto_20160702_0006")] operations = [ migrations.CreateModel( - name='Machine', + name="Machine", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ) ], ), migrations.CreateModel( - name='MachineType', + name="MachineType", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('type', models.CharField(max_length=255)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("type", models.CharField(max_length=255)), ], ), migrations.AddField( - model_name='machine', - name='type', - field=models.ForeignKey(to='machines.MachineType', on_delete=django.db.models.deletion.PROTECT), + model_name="machine", + name="type", + field=models.ForeignKey( + to="machines.MachineType", on_delete=django.db.models.deletion.PROTECT + ), ), migrations.AddField( - model_name='machine', - name='user', - field=models.ForeignKey(to='users.User', on_delete=django.db.models.deletion.PROTECT), + model_name="machine", + name="user", + field=models.ForeignKey( + to="users.User", on_delete=django.db.models.deletion.PROTECT + ), ), ] diff --git a/machines/migrations/0002_auto_20160703_1444.py b/machines/migrations/0002_auto_20160703_1444.py index e8203b19..83d820a4 100644 --- a/machines/migrations/0002_auto_20160703_1444.py +++ b/machines/migrations/0002_auto_20160703_1444.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -30,36 +30,57 @@ import macaddress.fields class Migration(migrations.Migration): - dependencies = [ - ('machines', '0001_initial'), - ] + dependencies = [("machines", "0001_initial")] operations = [ migrations.CreateModel( - name='Interface', + name="Interface", fields=[ - ('id', models.AutoField(serialize=False, verbose_name='ID', auto_created=True, primary_key=True)), - ('ipv6', models.GenericIPAddressField(protocol='IPv6')), - ('mac_address', macaddress.fields.MACAddressField(integer=True)), - ('details', models.CharField(max_length=255)), - ('name', models.CharField(max_length=255, blank=True, unique=True)), + ( + "id", + models.AutoField( + serialize=False, + verbose_name="ID", + auto_created=True, + primary_key=True, + ), + ), + ("ipv6", models.GenericIPAddressField(protocol="IPv6")), + ("mac_address", macaddress.fields.MACAddressField(integer=True)), + ("details", models.CharField(max_length=255)), + ("name", models.CharField(max_length=255, blank=True, unique=True)), ], ), migrations.CreateModel( - name='IpList', + name="IpList", fields=[ - ('id', models.AutoField(serialize=False, verbose_name='ID', auto_created=True, primary_key=True)), - ('ipv4', models.GenericIPAddressField(protocol='IPv4')), + ( + "id", + models.AutoField( + serialize=False, + verbose_name="ID", + auto_created=True, + primary_key=True, + ), + ), + ("ipv4", models.GenericIPAddressField(protocol="IPv4")), ], ), migrations.AddField( - model_name='interface', - name='ipv4', - field=models.OneToOneField(null=True, to='machines.IpList', blank=True, on_delete=django.db.models.deletion.PROTECT), + model_name="interface", + name="ipv4", + field=models.OneToOneField( + null=True, + to="machines.IpList", + blank=True, + on_delete=django.db.models.deletion.PROTECT, + ), ), migrations.AddField( - model_name='interface', - name='machine', - field=models.ForeignKey(to='machines.Machine', on_delete=django.db.models.deletion.PROTECT), + model_name="interface", + name="machine", + field=models.ForeignKey( + to="machines.Machine", on_delete=django.db.models.deletion.PROTECT + ), ), ] diff --git a/machines/migrations/0003_auto_20160703_1450.py b/machines/migrations/0003_auto_20160703_1450.py index 89a1862f..33c3642e 100644 --- a/machines/migrations/0003_auto_20160703_1450.py +++ b/machines/migrations/0003_auto_20160703_1450.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,14 +29,12 @@ import macaddress.fields class Migration(migrations.Migration): - dependencies = [ - ('machines', '0002_auto_20160703_1444'), - ] + dependencies = [("machines", "0002_auto_20160703_1444")] operations = [ migrations.AlterField( - model_name='interface', - name='mac_address', + model_name="interface", + name="mac_address", field=macaddress.fields.MACAddressField(integer=True, unique=True), - ), + ) ] diff --git a/machines/migrations/0004_auto_20160703_1451.py b/machines/migrations/0004_auto_20160703_1451.py index 7e8c344e..df36fb0f 100644 --- a/machines/migrations/0004_auto_20160703_1451.py +++ b/machines/migrations/0004_auto_20160703_1451.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0003_auto_20160703_1450'), - ] + dependencies = [("machines", "0003_auto_20160703_1450")] operations = [ migrations.AlterField( - model_name='iplist', - name='ipv4', - field=models.GenericIPAddressField(protocol='IPv4', unique=True), - ), + model_name="iplist", + name="ipv4", + field=models.GenericIPAddressField(protocol="IPv4", unique=True), + ) ] diff --git a/machines/migrations/0005_auto_20160703_1523.py b/machines/migrations/0005_auto_20160703_1523.py index 10ca1acb..481a113d 100644 --- a/machines/migrations/0005_auto_20160703_1523.py +++ b/machines/migrations/0005_auto_20160703_1523.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,19 +28,15 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0004_auto_20160703_1451'), - ] + dependencies = [("machines", "0004_auto_20160703_1451")] operations = [ - migrations.RenameField( - model_name='interface', - old_name='name', - new_name='dns', - ), + migrations.RenameField(model_name="interface", old_name="name", new_name="dns"), migrations.AddField( - model_name='machine', - name='name', - field=models.CharField(blank=True, unique=True, max_length=255, help_text='Optionnel'), + model_name="machine", + name="name", + field=models.CharField( + blank=True, unique=True, max_length=255, help_text="Optionnel" + ), ), ] diff --git a/machines/migrations/0006_auto_20160703_1813.py b/machines/migrations/0006_auto_20160703_1813.py index fb4b2adf..19ce0a75 100644 --- a/machines/migrations/0006_auto_20160703_1813.py +++ b/machines/migrations/0006_auto_20160703_1813.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,19 +28,17 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0005_auto_20160703_1523'), - ] + dependencies = [("machines", "0005_auto_20160703_1523")] operations = [ migrations.AlterField( - model_name='interface', - name='details', + model_name="interface", + name="details", field=models.CharField(max_length=255, blank=True), ), migrations.AlterField( - model_name='interface', - name='dns', + model_name="interface", + name="dns", field=models.CharField(max_length=255, unique=True), ), ] diff --git a/machines/migrations/0007_auto_20160703_1816.py b/machines/migrations/0007_auto_20160703_1816.py index a1cff4cd..be74d8b7 100644 --- a/machines/migrations/0007_auto_20160703_1816.py +++ b/machines/migrations/0007_auto_20160703_1816.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0006_auto_20160703_1813'), - ] + dependencies = [("machines", "0006_auto_20160703_1813")] operations = [ migrations.AlterField( - model_name='interface', - name='ipv6', - field=models.GenericIPAddressField(null=True, protocol='IPv6'), - ), + model_name="interface", + name="ipv6", + field=models.GenericIPAddressField(null=True, protocol="IPv6"), + ) ] diff --git a/machines/migrations/0008_remove_interface_ipv6.py b/machines/migrations/0008_remove_interface_ipv6.py index c96f7188..f4ebdfbb 100644 --- a/machines/migrations/0008_remove_interface_ipv6.py +++ b/machines/migrations/0008_remove_interface_ipv6.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,13 +28,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0007_auto_20160703_1816'), - ] + dependencies = [("machines", "0007_auto_20160703_1816")] - operations = [ - migrations.RemoveField( - model_name='interface', - name='ipv6', - ), - ] + operations = [migrations.RemoveField(model_name="interface", name="ipv6")] diff --git a/machines/migrations/0009_auto_20160703_2358.py b/machines/migrations/0009_auto_20160703_2358.py index 454cfb91..682f490e 100644 --- a/machines/migrations/0009_auto_20160703_2358.py +++ b/machines/migrations/0009_auto_20160703_2358.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,14 +29,14 @@ import macaddress.fields class Migration(migrations.Migration): - dependencies = [ - ('machines', '0008_remove_interface_ipv6'), - ] + dependencies = [("machines", "0008_remove_interface_ipv6")] operations = [ migrations.AlterField( - model_name='interface', - name='mac_address', - field=macaddress.fields.MACAddressField(integer=False, max_length=17, unique=True), - ), + model_name="interface", + name="mac_address", + field=macaddress.fields.MACAddressField( + integer=False, max_length=17, unique=True + ), + ) ] diff --git a/machines/migrations/0010_auto_20160704_0104.py b/machines/migrations/0010_auto_20160704_0104.py index 87ff48d2..79ffc90e 100644 --- a/machines/migrations/0010_auto_20160704_0104.py +++ b/machines/migrations/0010_auto_20160704_0104.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,18 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0009_auto_20160703_2358'), - ] + dependencies = [("machines", "0009_auto_20160703_2358")] operations = [ migrations.AlterField( - model_name='machine', - name='name', - field=models.CharField(blank=True, unique=True, max_length=255, help_text='Optionnel', null=True), - ), + model_name="machine", + name="name", + field=models.CharField( + blank=True, + unique=True, + max_length=255, + help_text="Optionnel", + null=True, + ), + ) ] diff --git a/machines/migrations/0011_auto_20160704_0105.py b/machines/migrations/0011_auto_20160704_0105.py index a595ee49..b780b951 100644 --- a/machines/migrations/0011_auto_20160704_0105.py +++ b/machines/migrations/0011_auto_20160704_0105.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0010_auto_20160704_0104'), - ] + dependencies = [("machines", "0010_auto_20160704_0104")] operations = [ migrations.AlterField( - model_name='machine', - name='name', - field=models.CharField(help_text='Optionnel', blank=True, null=True, max_length=255), - ), + model_name="machine", + name="name", + field=models.CharField( + help_text="Optionnel", blank=True, null=True, max_length=255 + ), + ) ] diff --git a/machines/migrations/0012_auto_20160704_0118.py b/machines/migrations/0012_auto_20160704_0118.py index 7a22e166..222b55a5 100644 --- a/machines/migrations/0012_auto_20160704_0118.py +++ b/machines/migrations/0012_auto_20160704_0118.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0011_auto_20160704_0105'), - ] + dependencies = [("machines", "0011_auto_20160704_0105")] operations = [ migrations.AlterField( - model_name='interface', - name='dns', - field=models.CharField(max_length=255, help_text='Obligatoire et unique', unique=True), - ), + model_name="interface", + name="dns", + field=models.CharField( + max_length=255, help_text="Obligatoire et unique", unique=True + ), + ) ] diff --git a/machines/migrations/0013_auto_20160705_1014.py b/machines/migrations/0013_auto_20160705_1014.py index edd636b7..5ee18020 100644 --- a/machines/migrations/0013_auto_20160705_1014.py +++ b/machines/migrations/0013_auto_20160705_1014.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,19 +28,19 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0012_auto_20160704_0118'), - ] + dependencies = [("machines", "0012_auto_20160704_0118")] operations = [ migrations.AddField( - model_name='machine', - name='active', - field=models.BooleanField(default=True), + model_name="machine", name="active", field=models.BooleanField(default=True) ), migrations.AlterField( - model_name='interface', - name='dns', - field=models.CharField(max_length=255, unique=True, help_text='Obligatoire et unique, doit se terminer en .rez et ne pas comporter de points'), + model_name="interface", + name="dns", + field=models.CharField( + max_length=255, + unique=True, + help_text="Obligatoire et unique, doit se terminer en .rez et ne pas comporter de points", + ), ), ] diff --git a/machines/migrations/0014_auto_20160706_1220.py b/machines/migrations/0014_auto_20160706_1220.py index dbfe0edb..2d3fc351 100644 --- a/machines/migrations/0014_auto_20160706_1220.py +++ b/machines/migrations/0014_auto_20160706_1220.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,14 +29,16 @@ import machines.models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0013_auto_20160705_1014'), - ] + dependencies = [("machines", "0013_auto_20160705_1014")] operations = [ migrations.AlterField( - model_name='interface', - name='dns', - field=models.CharField(unique=True, max_length=255, help_text="Obligatoire et unique, doit se terminer en .rez et ne pas comporter d'autres points"), - ), + model_name="interface", + name="dns", + field=models.CharField( + unique=True, + max_length=255, + help_text="Obligatoire et unique, doit se terminer en .rez et ne pas comporter d'autres points", + ), + ) ] diff --git a/machines/migrations/0015_auto_20160707_0105.py b/machines/migrations/0015_auto_20160707_0105.py index e12409f6..19022f55 100644 --- a/machines/migrations/0015_auto_20160707_0105.py +++ b/machines/migrations/0015_auto_20160707_0105.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,14 +29,16 @@ import machines.models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0014_auto_20160706_1220'), - ] + dependencies = [("machines", "0014_auto_20160706_1220")] operations = [ migrations.AlterField( - model_name='interface', - name='dns', - field=models.CharField(unique=True, help_text="Obligatoire et unique, doit se terminer en .example et ne pas comporter d'autres points", max_length=255), - ), + model_name="interface", + name="dns", + field=models.CharField( + unique=True, + help_text="Obligatoire et unique, doit se terminer en .example et ne pas comporter d'autres points", + max_length=255, + ), + ) ] diff --git a/machines/migrations/0016_auto_20160708_1633.py b/machines/migrations/0016_auto_20160708_1633.py index 6269e717..1b4a6bd0 100644 --- a/machines/migrations/0016_auto_20160708_1633.py +++ b/machines/migrations/0016_auto_20160708_1633.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -30,26 +30,41 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0015_auto_20160707_0105'), - ] + dependencies = [("machines", "0015_auto_20160707_0105")] operations = [ migrations.CreateModel( - name='Extension', + name="Extension", fields=[ - ('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=False, auto_created=True)), - ('name', models.CharField(max_length=255)), + ( + "id", + models.AutoField( + primary_key=True, + verbose_name="ID", + serialize=False, + auto_created=True, + ), + ), + ("name", models.CharField(max_length=255)), ], ), migrations.AlterField( - model_name='interface', - name='dns', - field=models.CharField(unique=True, max_length=255, help_text="Obligatoire et unique, doit se terminer en .rez et ne pas comporter d'autres points"), + model_name="interface", + name="dns", + field=models.CharField( + unique=True, + max_length=255, + help_text="Obligatoire et unique, doit se terminer en .rez et ne pas comporter d'autres points", + ), ), migrations.AddField( - model_name='machinetype', - name='extension', - field=models.ForeignKey(null=True, blank=True, on_delete=django.db.models.deletion.PROTECT, to='machines.Extension'), + model_name="machinetype", + name="extension", + field=models.ForeignKey( + null=True, + blank=True, + on_delete=django.db.models.deletion.PROTECT, + to="machines.Extension", + ), ), ] diff --git a/machines/migrations/0017_auto_20160708_1645.py b/machines/migrations/0017_auto_20160708_1645.py index 2dbe8daa..455070e4 100644 --- a/machines/migrations/0017_auto_20160708_1645.py +++ b/machines/migrations/0017_auto_20160708_1645.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,15 +29,17 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0016_auto_20160708_1633'), - ] + dependencies = [("machines", "0016_auto_20160708_1633")] operations = [ migrations.AlterField( - model_name='machinetype', - name='extension', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, default=1, to='machines.Extension'), + model_name="machinetype", + name="extension", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + default=1, + to="machines.Extension", + ), preserve_default=False, - ), + ) ] diff --git a/machines/migrations/0018_auto_20160708_1813.py b/machines/migrations/0018_auto_20160708_1813.py index 40ad7af3..bf2806cc 100644 --- a/machines/migrations/0018_auto_20160708_1813.py +++ b/machines/migrations/0018_auto_20160708_1813.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,16 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0017_auto_20160708_1645'), - ] + dependencies = [("machines", "0017_auto_20160708_1645")] operations = [ migrations.AlterField( - model_name='interface', - name='dns', - field=models.CharField(help_text="Obligatoire et unique, doit se terminer en .rez et ne pas comporter d'autres points", unique=True, max_length=255), - ), + model_name="interface", + name="dns", + field=models.CharField( + help_text="Obligatoire et unique, doit se terminer en .rez et ne pas comporter d'autres points", + unique=True, + max_length=255, + ), + ) ] diff --git a/machines/migrations/0019_auto_20160718_1141.py b/machines/migrations/0019_auto_20160718_1141.py index 1c7189c4..6990d85f 100644 --- a/machines/migrations/0019_auto_20160718_1141.py +++ b/machines/migrations/0019_auto_20160718_1141.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0018_auto_20160708_1813'), - ] + dependencies = [("machines", "0018_auto_20160708_1813")] operations = [ migrations.AlterField( - model_name='interface', - name='machine', - field=models.ForeignKey(to='machines.Machine'), - ), + model_name="interface", + name="machine", + field=models.ForeignKey(to="machines.Machine"), + ) ] diff --git a/machines/migrations/0020_auto_20160718_1849.py b/machines/migrations/0020_auto_20160718_1849.py index 669eb7f0..925e9cd5 100644 --- a/machines/migrations/0020_auto_20160718_1849.py +++ b/machines/migrations/0020_auto_20160718_1849.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,19 +29,18 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0019_auto_20160718_1141'), - ] + dependencies = [("machines", "0019_auto_20160718_1141")] operations = [ - migrations.RemoveField( - model_name='machine', - name='type', - ), + migrations.RemoveField(model_name="machine", name="type"), migrations.AddField( - model_name='interface', - name='type', - field=models.ForeignKey(to='machines.MachineType', default=1, on_delete=django.db.models.deletion.PROTECT), + model_name="interface", + name="type", + field=models.ForeignKey( + to="machines.MachineType", + default=1, + on_delete=django.db.models.deletion.PROTECT, + ), preserve_default=False, ), ] diff --git a/machines/migrations/0021_auto_20161006_1943.py b/machines/migrations/0021_auto_20161006_1943.py index b69efbf0..688fe503 100644 --- a/machines/migrations/0021_auto_20161006_1943.py +++ b/machines/migrations/0021_auto_20161006_1943.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0020_auto_20160718_1849'), - ] + dependencies = [("machines", "0020_auto_20160718_1849")] operations = [ migrations.AlterField( - model_name='interface', - name='dns', + model_name="interface", + name="dns", field=models.CharField(unique=True, max_length=255), - ), + ) ] diff --git a/machines/migrations/0022_auto_20161011_1829.py b/machines/migrations/0022_auto_20161011_1829.py index 5d513091..bae2d679 100644 --- a/machines/migrations/0022_auto_20161011_1829.py +++ b/machines/migrations/0022_auto_20161011_1829.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,16 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0021_auto_20161006_1943'), - ] + dependencies = [("machines", "0021_auto_20161006_1943")] operations = [ migrations.AlterField( - model_name='interface', - name='dns', - field=models.CharField(help_text="Obligatoire et unique, doit se terminer par exemple en .rez et ne pas comporter d'autres points", unique=True, max_length=255), - ), + model_name="interface", + name="dns", + field=models.CharField( + help_text="Obligatoire et unique, doit se terminer par exemple en .rez et ne pas comporter d'autres points", + unique=True, + max_length=255, + ), + ) ] diff --git a/machines/migrations/0023_iplist_ip_type.py b/machines/migrations/0023_iplist_ip_type.py index d15294b8..23f1a0f7 100644 --- a/machines/migrations/0023_iplist_ip_type.py +++ b/machines/migrations/0023_iplist_ip_type.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,15 +29,17 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0022_auto_20161011_1829'), - ] + dependencies = [("machines", "0022_auto_20161011_1829")] operations = [ migrations.AddField( - model_name='iplist', - name='ip_type', - field=models.ForeignKey(to='machines.MachineType', on_delete=django.db.models.deletion.PROTECT, default=1), + model_name="iplist", + name="ip_type", + field=models.ForeignKey( + to="machines.MachineType", + on_delete=django.db.models.deletion.PROTECT, + default=1, + ), preserve_default=False, - ), + ) ] diff --git a/machines/migrations/0024_machinetype_need_infra.py b/machines/migrations/0024_machinetype_need_infra.py index 529e8ddc..4caf9fa0 100644 --- a/machines/migrations/0024_machinetype_need_infra.py +++ b/machines/migrations/0024_machinetype_need_infra.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0023_iplist_ip_type'), - ] + dependencies = [("machines", "0023_iplist_ip_type")] operations = [ migrations.AddField( - model_name='machinetype', - name='need_infra', + model_name="machinetype", + name="need_infra", field=models.BooleanField(default=False), - ), + ) ] diff --git a/machines/migrations/0025_auto_20161023_0038.py b/machines/migrations/0025_auto_20161023_0038.py index d37dcf8d..285e9271 100644 --- a/machines/migrations/0025_auto_20161023_0038.py +++ b/machines/migrations/0025_auto_20161023_0038.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,36 +29,49 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0024_machinetype_need_infra'), - ] + dependencies = [("machines", "0024_machinetype_need_infra")] operations = [ migrations.CreateModel( - name='IpType', + name="IpType", fields=[ - ('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)), - ('type', models.CharField(max_length=255)), - ('need_infra', models.BooleanField(default=False)), - ('extension', models.ForeignKey(to='machines.Extension', on_delete=django.db.models.deletion.PROTECT)), + ( + "id", + models.AutoField( + verbose_name="ID", + primary_key=True, + serialize=False, + auto_created=True, + ), + ), + ("type", models.CharField(max_length=255)), + ("need_infra", models.BooleanField(default=False)), + ( + "extension", + models.ForeignKey( + to="machines.Extension", + on_delete=django.db.models.deletion.PROTECT, + ), + ), ], ), - migrations.RemoveField( - model_name='machinetype', - name='extension', - ), - migrations.RemoveField( - model_name='machinetype', - name='need_infra', - ), + migrations.RemoveField(model_name="machinetype", name="extension"), + migrations.RemoveField(model_name="machinetype", name="need_infra"), migrations.AlterField( - model_name='iplist', - name='ip_type', - field=models.ForeignKey(to='machines.IpType', on_delete=django.db.models.deletion.PROTECT), + model_name="iplist", + name="ip_type", + field=models.ForeignKey( + to="machines.IpType", on_delete=django.db.models.deletion.PROTECT + ), ), migrations.AddField( - model_name='machinetype', - name='ip_type', - field=models.ForeignKey(to='machines.IpType', null=True, blank=True, on_delete=django.db.models.deletion.PROTECT), + model_name="machinetype", + name="ip_type", + field=models.ForeignKey( + to="machines.IpType", + null=True, + blank=True, + on_delete=django.db.models.deletion.PROTECT, + ), ), ] diff --git a/machines/migrations/0026_auto_20161026_1348.py b/machines/migrations/0026_auto_20161026_1348.py index 69dd1b67..22911933 100644 --- a/machines/migrations/0026_auto_20161026_1348.py +++ b/machines/migrations/0026_auto_20161026_1348.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,16 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0025_auto_20161023_0038'), - ] + dependencies = [("machines", "0025_auto_20161023_0038")] operations = [ migrations.AlterField( - model_name='interface', - name='dns', - field=models.CharField(unique=True, max_length=255, help_text='Obligatoire et unique, ne doit pas comporter de points'), - ), + model_name="interface", + name="dns", + field=models.CharField( + unique=True, + max_length=255, + help_text="Obligatoire et unique, ne doit pas comporter de points", + ), + ) ] diff --git a/machines/migrations/0027_alias.py b/machines/migrations/0027_alias.py index 99b18752..c4d85cd7 100644 --- a/machines/migrations/0027_alias.py +++ b/machines/migrations/0027_alias.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,17 +28,30 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0026_auto_20161026_1348'), - ] + dependencies = [("machines", "0026_auto_20161026_1348")] operations = [ migrations.CreateModel( - name='Alias', + name="Alias", fields=[ - ('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')), - ('alias', models.CharField(max_length=255, help_text='Obligatoire et unique, ne doit pas comporter de points', unique=True)), - ('interface_parent', models.ForeignKey(to='machines.Interface')), + ( + "id", + models.AutoField( + primary_key=True, + serialize=False, + auto_created=True, + verbose_name="ID", + ), + ), + ( + "alias", + models.CharField( + max_length=255, + help_text="Obligatoire et unique, ne doit pas comporter de points", + unique=True, + ), + ), + ("interface_parent", models.ForeignKey(to="machines.Interface")), ], - ), + ) ] diff --git a/machines/migrations/0028_iptype_domaine_ip.py b/machines/migrations/0028_iptype_domaine_ip.py index d524514f..d0bfa5b4 100644 --- a/machines/migrations/0028_iptype_domaine_ip.py +++ b/machines/migrations/0028_iptype_domaine_ip.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0027_alias'), - ] + dependencies = [("machines", "0027_alias")] operations = [ migrations.AddField( - model_name='iptype', - name='domaine_ip', - field=models.GenericIPAddressField(blank=True, unique=True, null=True, protocol='IPv4'), - ), + model_name="iptype", + name="domaine_ip", + field=models.GenericIPAddressField( + blank=True, unique=True, null=True, protocol="IPv4" + ), + ) ] diff --git a/machines/migrations/0029_iptype_domaine_range.py b/machines/migrations/0029_iptype_domaine_range.py index bdd823d4..30159868 100644 --- a/machines/migrations/0029_iptype_domaine_range.py +++ b/machines/migrations/0029_iptype_domaine_range.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,14 +29,19 @@ import django.core.validators class Migration(migrations.Migration): - dependencies = [ - ('machines', '0028_iptype_domaine_ip'), - ] + dependencies = [("machines", "0028_iptype_domaine_ip")] operations = [ migrations.AddField( - model_name='iptype', - name='domaine_range', - field=models.IntegerField(null=True, validators=[django.core.validators.MinValueValidator(8), django.core.validators.MaxValueValidator(32)], blank=True), - ), + model_name="iptype", + name="domaine_range", + field=models.IntegerField( + null=True, + validators=[ + django.core.validators.MinValueValidator(8), + django.core.validators.MaxValueValidator(32), + ], + blank=True, + ), + ) ] diff --git a/machines/migrations/0030_auto_20161118_1730.py b/machines/migrations/0030_auto_20161118_1730.py index 6f21e8a7..e958fe3e 100644 --- a/machines/migrations/0030_auto_20161118_1730.py +++ b/machines/migrations/0030_auto_20161118_1730.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,24 +29,28 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0029_iptype_domaine_range'), - ] + dependencies = [("machines", "0029_iptype_domaine_range")] operations = [ migrations.AddField( - model_name='alias', - name='extension', - field=models.ForeignKey(to='machines.Extension', default=1, on_delete=django.db.models.deletion.PROTECT), + model_name="alias", + name="extension", + field=models.ForeignKey( + to="machines.Extension", + default=1, + on_delete=django.db.models.deletion.PROTECT, + ), preserve_default=False, ), migrations.AlterField( - model_name='alias', - name='alias', - field=models.CharField(max_length=255, help_text='Obligatoire et unique, ne doit pas comporter de points'), + model_name="alias", + name="alias", + field=models.CharField( + max_length=255, + help_text="Obligatoire et unique, ne doit pas comporter de points", + ), ), migrations.AlterUniqueTogether( - name='alias', - unique_together=set([('alias', 'extension')]), + name="alias", unique_together=set([("alias", "extension")]) ), ] diff --git a/machines/migrations/0031_auto_20161119_1709.py b/machines/migrations/0031_auto_20161119_1709.py index de995806..ae2ff82f 100644 --- a/machines/migrations/0031_auto_20161119_1709.py +++ b/machines/migrations/0031_auto_20161119_1709.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -30,36 +30,78 @@ import django.core.validators class Migration(migrations.Migration): - dependencies = [ - ('machines', '0030_auto_20161118_1730'), - ] + dependencies = [("machines", "0030_auto_20161118_1730")] operations = [ migrations.CreateModel( - name='Mx', + name="Mx", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, verbose_name='ID', serialize=False)), - ('priority', models.IntegerField(unique=True)), - ('name', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to='machines.Alias')), - ('zone', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='machines.Extension')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + verbose_name="ID", + serialize=False, + ), + ), + ("priority", models.IntegerField(unique=True)), + ( + "name", + models.OneToOneField( + on_delete=django.db.models.deletion.PROTECT, to="machines.Alias" + ), + ), + ( + "zone", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to="machines.Extension", + ), + ), ], ), migrations.CreateModel( - name='Ns', + name="Ns", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, verbose_name='ID', serialize=False)), - ('interface', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to='machines.Interface')), - ('zone', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='machines.Extension')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + verbose_name="ID", + serialize=False, + ), + ), + ( + "interface", + models.OneToOneField( + on_delete=django.db.models.deletion.PROTECT, + to="machines.Interface", + ), + ), + ( + "zone", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to="machines.Extension", + ), + ), ], ), migrations.AlterField( - model_name='iptype', - name='domaine_ip', - field=models.GenericIPAddressField(protocol='IPv4'), + model_name="iptype", + name="domaine_ip", + field=models.GenericIPAddressField(protocol="IPv4"), ), migrations.AlterField( - model_name='iptype', - name='domaine_range', - field=models.IntegerField(validators=[django.core.validators.MinValueValidator(8), django.core.validators.MaxValueValidator(32)]), + model_name="iptype", + name="domaine_range", + field=models.IntegerField( + validators=[ + django.core.validators.MinValueValidator(8), + django.core.validators.MaxValueValidator(32), + ] + ), ), ] diff --git a/machines/migrations/0032_auto_20161119_1850.py b/machines/migrations/0032_auto_20161119_1850.py index 0d2f30f8..7a788b42 100644 --- a/machines/migrations/0032_auto_20161119_1850.py +++ b/machines/migrations/0032_auto_20161119_1850.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,19 +29,22 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0031_auto_20161119_1709'), - ] + dependencies = [("machines", "0031_auto_20161119_1709")] operations = [ migrations.AddField( - model_name='extension', - name='origin', - field=models.OneToOneField(null=True, to='machines.IpList', blank=True, on_delete=django.db.models.deletion.PROTECT), + model_name="extension", + name="origin", + field=models.OneToOneField( + null=True, + to="machines.IpList", + blank=True, + on_delete=django.db.models.deletion.PROTECT, + ), ), migrations.AlterField( - model_name='extension', - name='name', + model_name="extension", + name="name", field=models.CharField(max_length=255, unique=True), ), ] diff --git a/machines/migrations/0033_extension_need_infra.py b/machines/migrations/0033_extension_need_infra.py index d88f4d18..1f42f153 100644 --- a/machines/migrations/0033_extension_need_infra.py +++ b/machines/migrations/0033_extension_need_infra.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0032_auto_20161119_1850'), - ] + dependencies = [("machines", "0032_auto_20161119_1850")] operations = [ migrations.AddField( - model_name='extension', - name='need_infra', + model_name="extension", + name="need_infra", field=models.BooleanField(default=False), - ), + ) ] diff --git a/machines/migrations/0034_iplist_need_infra.py b/machines/migrations/0034_iplist_need_infra.py index 297e4279..0e0dad85 100644 --- a/machines/migrations/0034_iplist_need_infra.py +++ b/machines/migrations/0034_iplist_need_infra.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0033_extension_need_infra'), - ] + dependencies = [("machines", "0033_extension_need_infra")] operations = [ migrations.AddField( - model_name='iplist', - name='need_infra', + model_name="iplist", + name="need_infra", field=models.BooleanField(default=False), - ), + ) ] diff --git a/machines/migrations/0035_auto_20161224_1201.py b/machines/migrations/0035_auto_20161224_1201.py index 81564fe9..2f46f048 100644 --- a/machines/migrations/0035_auto_20161224_1201.py +++ b/machines/migrations/0035_auto_20161224_1201.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,10 +28,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0034_iplist_need_infra'), - ] + dependencies = [("machines", "0034_iplist_need_infra")] - operations = [ - migrations.RenameModel('Alias', 'Domain') - ] + operations = [migrations.RenameModel("Alias", "Domain")] diff --git a/machines/migrations/0036_auto_20161224_1204.py b/machines/migrations/0036_auto_20161224_1204.py index 3b585b36..80ee84be 100644 --- a/machines/migrations/0036_auto_20161224_1204.py +++ b/machines/migrations/0036_auto_20161224_1204.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,23 +28,16 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0035_auto_20161224_1201'), - ] + dependencies = [("machines", "0035_auto_20161224_1201")] operations = [ - migrations.RenameField( - model_name='domain', - old_name='alias', - new_name='name', - ), + migrations.RenameField(model_name="domain", old_name="alias", new_name="name"), migrations.AlterField( - model_name='domain', - name='interface_parent', - field=models.ForeignKey(to='machines.Interface', null=True, blank=True), + model_name="domain", + name="interface_parent", + field=models.ForeignKey(to="machines.Interface", null=True, blank=True), ), migrations.AlterUniqueTogether( - name='domain', - unique_together=set([('name', 'extension')]), + name="domain", unique_together=set([("name", "extension")]) ), ] diff --git a/machines/migrations/0037_domain_cname.py b/machines/migrations/0037_domain_cname.py index b7dd198e..08570133 100644 --- a/machines/migrations/0037_domain_cname.py +++ b/machines/migrations/0037_domain_cname.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,17 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0036_auto_20161224_1204'), - ] + dependencies = [("machines", "0036_auto_20161224_1204")] operations = [ migrations.AddField( - model_name='domain', - name='cname', - field=models.OneToOneField(related_name='related_domain', null=True, to='machines.Domain', blank=True), - ), + model_name="domain", + name="cname", + field=models.OneToOneField( + related_name="related_domain", + null=True, + to="machines.Domain", + blank=True, + ), + ) ] diff --git a/machines/migrations/0038_auto_20161224_1721.py b/machines/migrations/0038_auto_20161224_1721.py index 574b8e58..0056c308 100644 --- a/machines/migrations/0038_auto_20161224_1721.py +++ b/machines/migrations/0038_auto_20161224_1721.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,17 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0037_domain_cname'), - ] + dependencies = [("machines", "0037_domain_cname")] operations = [ migrations.AlterField( - model_name='domain', - name='cname', - field=models.ForeignKey(null=True, to='machines.Domain', related_name='related_domain', blank=True), - ), + model_name="domain", + name="cname", + field=models.ForeignKey( + null=True, + to="machines.Domain", + related_name="related_domain", + blank=True, + ), + ) ] diff --git a/machines/migrations/0039_auto_20161224_1732.py b/machines/migrations/0039_auto_20161224_1732.py index 836571b2..8136fc9c 100644 --- a/machines/migrations/0039_auto_20161224_1732.py +++ b/machines/migrations/0039_auto_20161224_1732.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0038_auto_20161224_1721'), - ] + dependencies = [("machines", "0038_auto_20161224_1721")] operations = [ migrations.AlterField( - model_name='domain', - name='interface_parent', - field=models.OneToOneField(blank=True, null=True, to='machines.Interface'), - ), + model_name="domain", + name="interface_parent", + field=models.OneToOneField(blank=True, null=True, to="machines.Interface"), + ) ] diff --git a/machines/migrations/0040_remove_interface_dns.py b/machines/migrations/0040_remove_interface_dns.py index 2171f6b2..232e65e7 100644 --- a/machines/migrations/0040_remove_interface_dns.py +++ b/machines/migrations/0040_remove_interface_dns.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,13 +28,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0039_auto_20161224_1732'), - ] + dependencies = [("machines", "0039_auto_20161224_1732")] - operations = [ - migrations.RemoveField( - model_name='interface', - name='dns', - ), - ] + operations = [migrations.RemoveField(model_name="interface", name="dns")] diff --git a/machines/migrations/0041_remove_ns_interface.py b/machines/migrations/0041_remove_ns_interface.py index 15a2c87e..8906abd5 100644 --- a/machines/migrations/0041_remove_ns_interface.py +++ b/machines/migrations/0041_remove_ns_interface.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,13 +28,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0040_remove_interface_dns'), - ] + dependencies = [("machines", "0040_remove_interface_dns")] - operations = [ - migrations.RemoveField( - model_name='ns', - name='interface', - ), - ] + operations = [migrations.RemoveField(model_name="ns", name="interface")] diff --git a/machines/migrations/0042_ns_ns.py b/machines/migrations/0042_ns_ns.py index 00212836..1c0d27d1 100644 --- a/machines/migrations/0042_ns_ns.py +++ b/machines/migrations/0042_ns_ns.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,15 +29,17 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0041_remove_ns_interface'), - ] + dependencies = [("machines", "0041_remove_ns_interface")] operations = [ migrations.AddField( - model_name='ns', - name='ns', - field=models.OneToOneField(to='machines.Domain', default=1, on_delete=django.db.models.deletion.PROTECT), + model_name="ns", + name="ns", + field=models.OneToOneField( + to="machines.Domain", + default=1, + on_delete=django.db.models.deletion.PROTECT, + ), preserve_default=False, - ), + ) ] diff --git a/machines/migrations/0043_auto_20170721_0350.py b/machines/migrations/0043_auto_20170721_0350.py index 497367bf..607d6949 100644 --- a/machines/migrations/0043_auto_20170721_0350.py +++ b/machines/migrations/0043_auto_20170721_0350.py @@ -8,18 +8,15 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0042_ns_ns'), - ] + dependencies = [("machines", "0042_ns_ns")] operations = [ - migrations.RemoveField( - model_name='iplist', - name='need_infra', - ), + migrations.RemoveField(model_name="iplist", name="need_infra"), migrations.AlterField( - model_name='iplist', - name='ip_type', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='machines.IpType'), + model_name="iplist", + name="ip_type", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="machines.IpType" + ), ), ] diff --git a/machines/migrations/0044_auto_20170808_0233.py b/machines/migrations/0044_auto_20170808_0233.py index 864505a2..9028a865 100644 --- a/machines/migrations/0044_auto_20170808_0233.py +++ b/machines/migrations/0044_auto_20170808_0233.py @@ -8,32 +8,63 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0043_auto_20170721_0350'), - ] + dependencies = [("machines", "0043_auto_20170721_0350")] operations = [ migrations.CreateModel( - name='Service', + name="Service", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('service_type', models.CharField(blank=True, max_length=255, unique=True)), - ('time_regen', models.DurationField()), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "service_type", + models.CharField(blank=True, max_length=255, unique=True), + ), + ("time_regen", models.DurationField()), ], ), migrations.CreateModel( - name='Service_link', + name="Service_link", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('last_regen', models.DateTimeField()), - ('asked_regen', models.BooleanField()), - ('server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='machines.Interface')), - ('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='machines.Service')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("last_regen", models.DateTimeField()), + ("asked_regen", models.BooleanField()), + ( + "server", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="machines.Interface", + ), + ), + ( + "service", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="machines.Service", + ), + ), ], ), migrations.AddField( - model_name='service', - name='servers', - field=models.ManyToManyField(through='machines.Service_link', to='machines.Interface'), + model_name="service", + name="servers", + field=models.ManyToManyField( + through="machines.Service_link", to="machines.Interface" + ), ), ] diff --git a/machines/migrations/0045_auto_20170808_0348.py b/machines/migrations/0045_auto_20170808_0348.py index 16a6bb0a..b44a1a8a 100644 --- a/machines/migrations/0045_auto_20170808_0348.py +++ b/machines/migrations/0045_auto_20170808_0348.py @@ -7,19 +7,17 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0044_auto_20170808_0233'), - ] + dependencies = [("machines", "0044_auto_20170808_0233")] operations = [ migrations.AlterField( - model_name='service_link', - name='asked_regen', + model_name="service_link", + name="asked_regen", field=models.BooleanField(default=False), ), migrations.AlterField( - model_name='service_link', - name='last_regen', + model_name="service_link", + name="last_regen", field=models.DateTimeField(auto_now_add=True), ), ] diff --git a/machines/migrations/0046_auto_20170808_1423.py b/machines/migrations/0046_auto_20170808_1423.py index b7ffbced..86a69c6d 100644 --- a/machines/migrations/0046_auto_20170808_1423.py +++ b/machines/migrations/0046_auto_20170808_1423.py @@ -8,14 +8,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0045_auto_20170808_0348'), - ] + dependencies = [("machines", "0045_auto_20170808_0348")] operations = [ migrations.AlterField( - model_name='service', - name='time_regen', + model_name="service", + name="time_regen", field=models.DurationField(default=datetime.timedelta(0, 60)), - ), + ) ] diff --git a/machines/migrations/0047_auto_20170809_0606.py b/machines/migrations/0047_auto_20170809_0606.py index 65cd3d1f..99337be2 100644 --- a/machines/migrations/0047_auto_20170809_0606.py +++ b/machines/migrations/0047_auto_20170809_0606.py @@ -8,23 +8,24 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0046_auto_20170808_1423'), - ] + dependencies = [("machines", "0046_auto_20170808_1423")] operations = [ - migrations.RemoveField( - model_name='service', - name='time_regen', + migrations.RemoveField(model_name="service", name="time_regen"), + migrations.AddField( + model_name="service", + name="min_time_regen", + field=models.DurationField( + default=datetime.timedelta(0, 60), + help_text="Temps minimal avant nouvelle génération du service", + ), ), migrations.AddField( - model_name='service', - name='min_time_regen', - field=models.DurationField(default=datetime.timedelta(0, 60), help_text='Temps minimal avant nouvelle génération du service'), - ), - migrations.AddField( - model_name='service', - name='regular_time_regen', - field=models.DurationField(default=datetime.timedelta(0, 3600), help_text='Temps maximal avant nouvelle génération du service'), + model_name="service", + name="regular_time_regen", + field=models.DurationField( + default=datetime.timedelta(0, 3600), + help_text="Temps maximal avant nouvelle génération du service", + ), ), ] diff --git a/machines/migrations/0048_auto_20170823_2315.py b/machines/migrations/0048_auto_20170823_2315.py index 0c0a48f3..28b1fd48 100644 --- a/machines/migrations/0048_auto_20170823_2315.py +++ b/machines/migrations/0048_auto_20170823_2315.py @@ -8,14 +8,17 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0047_auto_20170809_0606'), - ] + dependencies = [("machines", "0047_auto_20170809_0606")] operations = [ migrations.AlterField( - model_name='iptype', - name='domaine_range', - field=models.IntegerField(validators=[django.core.validators.MinValueValidator(16), django.core.validators.MaxValueValidator(32)]), - ), + model_name="iptype", + name="domaine_range", + field=models.IntegerField( + validators=[ + django.core.validators.MinValueValidator(16), + django.core.validators.MaxValueValidator(32), + ] + ), + ) ] diff --git a/machines/migrations/0049_vlan.py b/machines/migrations/0049_vlan.py index 20a6905b..fa892bb7 100644 --- a/machines/migrations/0049_vlan.py +++ b/machines/migrations/0049_vlan.py @@ -7,18 +7,24 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0048_auto_20170823_2315'), - ] + dependencies = [("machines", "0048_auto_20170823_2315")] operations = [ migrations.CreateModel( - name='Vlan', + name="Vlan", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('vlan_id', models.IntegerField()), - ('name', models.CharField(max_length=256)), - ('comment', models.CharField(max_length=256)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("vlan_id", models.IntegerField()), + ("name", models.CharField(max_length=256)), + ("comment", models.CharField(max_length=256)), ], - ), + ) ] diff --git a/machines/migrations/0050_auto_20170826_0022.py b/machines/migrations/0050_auto_20170826_0022.py index 0f49a08b..a528fb8c 100644 --- a/machines/migrations/0050_auto_20170826_0022.py +++ b/machines/migrations/0050_auto_20170826_0022.py @@ -7,14 +7,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0049_vlan'), - ] + dependencies = [("machines", "0049_vlan")] operations = [ migrations.AlterField( - model_name='vlan', - name='comment', + model_name="vlan", + name="comment", field=models.CharField(blank=True, max_length=256), - ), + ) ] diff --git a/machines/migrations/0051_iptype_vlan.py b/machines/migrations/0051_iptype_vlan.py index 27b641b2..66570cae 100644 --- a/machines/migrations/0051_iptype_vlan.py +++ b/machines/migrations/0051_iptype_vlan.py @@ -8,14 +8,17 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0050_auto_20170826_0022'), - ] + dependencies = [("machines", "0050_auto_20170826_0022")] operations = [ migrations.AddField( - model_name='iptype', - name='vlan', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='machines.Vlan'), - ), + model_name="iptype", + name="vlan", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="machines.Vlan", + ), + ) ] diff --git a/machines/migrations/0052_auto_20170828_2322.py b/machines/migrations/0052_auto_20170828_2322.py index b8ab65ca..4a3bfc1e 100644 --- a/machines/migrations/0052_auto_20170828_2322.py +++ b/machines/migrations/0052_auto_20170828_2322.py @@ -7,24 +7,19 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0051_iptype_vlan'), - ] + dependencies = [("machines", "0051_iptype_vlan")] operations = [ migrations.RenameField( - model_name='iptype', - old_name='domaine_ip', - new_name='domaine_ip_start', - ), - migrations.RemoveField( - model_name='iptype', - name='domaine_range', + model_name="iptype", old_name="domaine_ip", new_name="domaine_ip_start" ), + migrations.RemoveField(model_name="iptype", name="domaine_range"), migrations.AddField( - model_name='iptype', - name='domaine_ip_stop', - field=models.GenericIPAddressField(default='255.255.254.254', protocol='IPv4'), + model_name="iptype", + name="domaine_ip_stop", + field=models.GenericIPAddressField( + default="255.255.254.254", protocol="IPv4" + ), preserve_default=False, ), ] diff --git a/machines/migrations/0053_text.py b/machines/migrations/0053_text.py index ee7679fc..4697dab9 100644 --- a/machines/migrations/0053_text.py +++ b/machines/migrations/0053_text.py @@ -7,17 +7,23 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0052_auto_20170828_2322'), - ] + dependencies = [("machines", "0052_auto_20170828_2322")] operations = [ migrations.CreateModel( - name='Text', + name="Text", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('field1', models.CharField(max_length=255)), - ('field2', models.CharField(max_length=255)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("field1", models.CharField(max_length=255)), + ("field2", models.CharField(max_length=255)), ], - ), + ) ] diff --git a/machines/migrations/0054_text_zone.py b/machines/migrations/0054_text_zone.py index f3447f35..31250fc6 100644 --- a/machines/migrations/0054_text_zone.py +++ b/machines/migrations/0054_text_zone.py @@ -8,15 +8,15 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0053_text'), - ] + dependencies = [("machines", "0053_text")] operations = [ migrations.AddField( - model_name='text', - name='zone', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='machines.Extension'), + model_name="text", + name="zone", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, to="machines.Extension" + ), preserve_default=False, - ), + ) ] diff --git a/machines/migrations/0055_nas.py b/machines/migrations/0055_nas.py index 9e0466ae..260a894f 100644 --- a/machines/migrations/0055_nas.py +++ b/machines/migrations/0055_nas.py @@ -8,18 +8,38 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0054_text_zone'), - ] + dependencies = [("machines", "0054_text_zone")] operations = [ migrations.CreateModel( - name='Nas', + name="Nas", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, unique=True)), - ('machine_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='machinetype_on_nas', to='machines.MachineType')), - ('nas_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='nas_type', to='machines.MachineType')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255, unique=True)), + ( + "machine_type", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="machinetype_on_nas", + to="machines.MachineType", + ), + ), + ( + "nas_type", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="nas_type", + to="machines.MachineType", + ), + ), ], - ), + ) ] diff --git a/machines/migrations/0056_nas_port_access_mode.py b/machines/migrations/0056_nas_port_access_mode.py index 20e5ba42..a714af49 100644 --- a/machines/migrations/0056_nas_port_access_mode.py +++ b/machines/migrations/0056_nas_port_access_mode.py @@ -7,14 +7,16 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0055_nas'), - ] + dependencies = [("machines", "0055_nas")] operations = [ migrations.AddField( - model_name='nas', - name='port_access_mode', - field=models.CharField(choices=[('802.1X', '802.1X'), ('Mac-address', 'Mac-address')], default='802.1X', max_length=32), - ), + model_name="nas", + name="port_access_mode", + field=models.CharField( + choices=[("802.1X", "802.1X"), ("Mac-address", "Mac-address")], + default="802.1X", + max_length=32, + ), + ) ] diff --git a/machines/migrations/0057_nas_autocapture_mac.py b/machines/migrations/0057_nas_autocapture_mac.py index feefa25f..37b2f68a 100644 --- a/machines/migrations/0057_nas_autocapture_mac.py +++ b/machines/migrations/0057_nas_autocapture_mac.py @@ -7,14 +7,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0056_nas_port_access_mode'), - ] + dependencies = [("machines", "0056_nas_port_access_mode")] operations = [ migrations.AddField( - model_name='nas', - name='autocapture_mac', + model_name="nas", + name="autocapture_mac", field=models.BooleanField(default=False), - ), + ) ] diff --git a/machines/migrations/0058_auto_20171002_0350.py b/machines/migrations/0058_auto_20171002_0350.py index bc6b2508..ca452b10 100644 --- a/machines/migrations/0058_auto_20171002_0350.py +++ b/machines/migrations/0058_auto_20171002_0350.py @@ -8,36 +8,68 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0057_nas_autocapture_mac'), - ] + dependencies = [("machines", "0057_nas_autocapture_mac")] operations = [ migrations.CreateModel( - name='OuverturePort', + name="OuverturePort", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('begin', models.IntegerField()), - ('end', models.IntegerField()), - ('protocole', models.CharField(choices=[('T', 'TCP'), ('U', 'UDP')], default='T', max_length=1)), - ('io', models.CharField(choices=[('I', 'IN'), ('O', 'OUT')], default='O', max_length=1)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("begin", models.IntegerField()), + ("end", models.IntegerField()), + ( + "protocole", + models.CharField( + choices=[("T", "TCP"), ("U", "UDP")], default="T", max_length=1 + ), + ), + ( + "io", + models.CharField( + choices=[("I", "IN"), ("O", "OUT")], default="O", max_length=1 + ), + ), ], ), migrations.CreateModel( - name='OuverturePortList', + name="OuverturePortList", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(help_text='Nom de la configuration des ports.', max_length=255)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField( + help_text="Nom de la configuration des ports.", max_length=255 + ), + ), ], ), migrations.AddField( - model_name='ouvertureport', - name='port_list', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='machines.OuverturePortList'), + model_name="ouvertureport", + name="port_list", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="machines.OuverturePortList", + ), ), migrations.AddField( - model_name='interface', - name='port_lists', - field=models.ManyToManyField(blank=True, to='machines.OuverturePortList'), + model_name="interface", + name="port_lists", + field=models.ManyToManyField(blank=True, to="machines.OuverturePortList"), ), ] diff --git a/machines/migrations/0059_iptype_prefix_v6.py b/machines/migrations/0059_iptype_prefix_v6.py index 464fc5e6..7df10904 100644 --- a/machines/migrations/0059_iptype_prefix_v6.py +++ b/machines/migrations/0059_iptype_prefix_v6.py @@ -7,14 +7,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0058_auto_20171002_0350'), - ] + dependencies = [("machines", "0058_auto_20171002_0350")] operations = [ migrations.AddField( - model_name='iptype', - name='prefix_v6', - field=models.GenericIPAddressField(blank=True, null=True, protocol='IPv6'), - ), + model_name="iptype", + name="prefix_v6", + field=models.GenericIPAddressField(blank=True, null=True, protocol="IPv6"), + ) ] diff --git a/machines/migrations/0060_iptype_ouverture_ports.py b/machines/migrations/0060_iptype_ouverture_ports.py index e35f398f..5515acb5 100644 --- a/machines/migrations/0060_iptype_ouverture_ports.py +++ b/machines/migrations/0060_iptype_ouverture_ports.py @@ -8,14 +8,17 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0059_iptype_prefix_v6'), - ] + dependencies = [("machines", "0059_iptype_prefix_v6")] operations = [ migrations.AddField( - model_name='iptype', - name='ouverture_ports', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='machines.OuverturePortList'), - ), + model_name="iptype", + name="ouverture_ports", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="machines.OuverturePortList", + ), + ) ] diff --git a/machines/migrations/0061_auto_20171015_2033.py b/machines/migrations/0061_auto_20171015_2033.py index 6153bbd0..b89099f4 100644 --- a/machines/migrations/0061_auto_20171015_2033.py +++ b/machines/migrations/0061_auto_20171015_2033.py @@ -8,29 +8,33 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0060_iptype_ouverture_ports'), - ] + dependencies = [("machines", "0060_iptype_ouverture_ports")] operations = [ migrations.AlterField( - model_name='mx', - name='priority', + model_name="mx", + name="priority", field=models.PositiveIntegerField(unique=True), ), migrations.AlterField( - model_name='ouvertureport', - name='begin', - field=models.PositiveIntegerField(validators=[django.core.validators.MaxValueValidator(65535)]), + model_name="ouvertureport", + name="begin", + field=models.PositiveIntegerField( + validators=[django.core.validators.MaxValueValidator(65535)] + ), ), migrations.AlterField( - model_name='ouvertureport', - name='end', - field=models.PositiveIntegerField(validators=[django.core.validators.MaxValueValidator(65535)]), + model_name="ouvertureport", + name="end", + field=models.PositiveIntegerField( + validators=[django.core.validators.MaxValueValidator(65535)] + ), ), migrations.AlterField( - model_name='vlan', - name='vlan_id', - field=models.PositiveIntegerField(validators=[django.core.validators.MaxValueValidator(4095)]), + model_name="vlan", + name="vlan_id", + field=models.PositiveIntegerField( + validators=[django.core.validators.MaxValueValidator(4095)] + ), ), ] diff --git a/machines/migrations/0062_extension_origin_v6.py b/machines/migrations/0062_extension_origin_v6.py index 1c3d869a..d78fb49c 100644 --- a/machines/migrations/0062_extension_origin_v6.py +++ b/machines/migrations/0062_extension_origin_v6.py @@ -7,14 +7,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0061_auto_20171015_2033'), - ] + dependencies = [("machines", "0061_auto_20171015_2033")] operations = [ migrations.AddField( - model_name='extension', - name='origin_v6', - field=models.GenericIPAddressField(blank=True, null=True, protocol='IPv6'), - ), + model_name="extension", + name="origin_v6", + field=models.GenericIPAddressField(blank=True, null=True, protocol="IPv6"), + ) ] diff --git a/machines/migrations/0063_auto_20171020_0040.py b/machines/migrations/0063_auto_20171020_0040.py index 0e049881..8b9b7d24 100644 --- a/machines/migrations/0063_auto_20171020_0040.py +++ b/machines/migrations/0063_auto_20171020_0040.py @@ -10,26 +10,66 @@ import machines.models class Migration(migrations.Migration): dependencies = [ - ('machines', '0062_extension_origin_v6'), - ('reversion', '0001_squashed_0004_auto_20160611_1202') + ("machines", "0062_extension_origin_v6"), + ("reversion", "0001_squashed_0004_auto_20160611_1202"), ] operations = [ migrations.CreateModel( - name='SOA', + name="SOA", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('mail', models.EmailField(help_text='Email du contact pour la zone', max_length=254)), - ('refresh', models.PositiveIntegerField(default=86400, help_text='Secondes avant que les DNS secondaires doivent demander le serial du DNS primaire pour détecter une modification')), - ('retry', models.PositiveIntegerField(default=7200, help_text='Secondes avant que les DNS secondaires fassent une nouvelle demande de serial en cas de timeout du DNS primaire')), - ('expire', models.PositiveIntegerField(default=3600000, help_text='Secondes après lesquelles les DNS secondaires arrêtent de de répondre aux requêtes en cas de timeout du DNS primaire')), - ('ttl', models.PositiveIntegerField(default=172800, help_text='Time To Live')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)), + ( + "mail", + models.EmailField( + help_text="Email du contact pour la zone", max_length=254 + ), + ), + ( + "refresh", + models.PositiveIntegerField( + default=86400, + help_text="Secondes avant que les DNS secondaires doivent demander le serial du DNS primaire pour détecter une modification", + ), + ), + ( + "retry", + models.PositiveIntegerField( + default=7200, + help_text="Secondes avant que les DNS secondaires fassent une nouvelle demande de serial en cas de timeout du DNS primaire", + ), + ), + ( + "expire", + models.PositiveIntegerField( + default=3600000, + help_text="Secondes après lesquelles les DNS secondaires arrêtent de de répondre aux requêtes en cas de timeout du DNS primaire", + ), + ), + ( + "ttl", + models.PositiveIntegerField( + default=172800, help_text="Time To Live" + ), + ), ], ), migrations.AddField( - model_name='extension', - name='soa', - field=models.ForeignKey(default=machines.models.SOA.new_default_soa, on_delete=django.db.models.deletion.CASCADE, to='machines.SOA'), + model_name="extension", + name="soa", + field=models.ForeignKey( + default=machines.models.SOA.new_default_soa, + on_delete=django.db.models.deletion.CASCADE, + to="machines.SOA", + ), ), ] diff --git a/machines/migrations/0064_auto_20171115_0253.py b/machines/migrations/0064_auto_20171115_0253.py index a028e2cb..0c644b46 100644 --- a/machines/migrations/0064_auto_20171115_0253.py +++ b/machines/migrations/0064_auto_20171115_0253.py @@ -7,14 +7,10 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0063_auto_20171020_0040'), - ] + dependencies = [("machines", "0063_auto_20171020_0040")] operations = [ migrations.AlterField( - model_name='text', - name='field2', - field=models.TextField(max_length=2047), - ), + model_name="text", name="field2", field=models.TextField(max_length=2047) + ) ] diff --git a/machines/migrations/0065_auto_20171115_1514.py b/machines/migrations/0065_auto_20171115_1514.py index c8c5699b..d5b11c9a 100644 --- a/machines/migrations/0065_auto_20171115_1514.py +++ b/machines/migrations/0065_auto_20171115_1514.py @@ -7,13 +7,6 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('machines', '0064_auto_20171115_0253'), - ] + dependencies = [("machines", "0064_auto_20171115_0253")] - operations = [ - migrations.RenameModel( - old_name='Text', - new_name='Txt', - ), - ] + operations = [migrations.RenameModel(old_name="Text", new_name="Txt")] diff --git a/machines/migrations/0066_srv.py b/machines/migrations/0066_srv.py index 94b7d0ce..895806b7 100644 --- a/machines/migrations/0066_srv.py +++ b/machines/migrations/0066_srv.py @@ -9,23 +9,68 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0065_auto_20171115_1514'), - ] + dependencies = [("machines", "0065_auto_20171115_1514")] operations = [ migrations.CreateModel( - name='Srv', + name="Srv", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('service', models.CharField(max_length=31)), - ('protocole', models.CharField(choices=[('TCP', 'TCP'), ('UDP', 'UDP')], default='TCP', max_length=3)), - ('ttl', models.PositiveIntegerField(default=172800, help_text='Time To Live')), - ('priority', models.PositiveIntegerField(validators=[django.core.validators.MaxValueValidator(65535)])), - ('weight', models.PositiveIntegerField(validators=[django.core.validators.MaxValueValidator(65535)])), - ('port', models.PositiveIntegerField(validators=[django.core.validators.MaxValueValidator(65535)])), - ('extension', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='machines.Extension')), - ('target', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='machines.Domain')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("service", models.CharField(max_length=31)), + ( + "protocole", + models.CharField( + choices=[("TCP", "TCP"), ("UDP", "UDP")], + default="TCP", + max_length=3, + ), + ), + ( + "ttl", + models.PositiveIntegerField( + default=172800, help_text="Time To Live" + ), + ), + ( + "priority", + models.PositiveIntegerField( + validators=[django.core.validators.MaxValueValidator(65535)] + ), + ), + ( + "weight", + models.PositiveIntegerField( + validators=[django.core.validators.MaxValueValidator(65535)] + ), + ), + ( + "port", + models.PositiveIntegerField( + validators=[django.core.validators.MaxValueValidator(65535)] + ), + ), + ( + "extension", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to="machines.Extension", + ), + ), + ( + "target", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to="machines.Domain", + ), + ), ], - ), + ) ] diff --git a/machines/migrations/0067_auto_20171116_0152.py b/machines/migrations/0067_auto_20171116_0152.py index dec7f865..095210de 100644 --- a/machines/migrations/0067_auto_20171116_0152.py +++ b/machines/migrations/0067_auto_20171116_0152.py @@ -9,29 +9,40 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0066_srv'), - ] + dependencies = [("machines", "0066_srv")] operations = [ migrations.AlterField( - model_name='srv', - name='port', - field=models.PositiveIntegerField(help_text='Port (tcp/udp)', validators=[django.core.validators.MaxValueValidator(65535)]), + model_name="srv", + name="port", + field=models.PositiveIntegerField( + help_text="Port (tcp/udp)", + validators=[django.core.validators.MaxValueValidator(65535)], + ), ), migrations.AlterField( - model_name='srv', - name='priority', - field=models.PositiveIntegerField(help_text="La priorité du serveur cible (valeur entière non négative, plus elle est faible, plus ce serveur sera utilisé s'il est disponible)", validators=[django.core.validators.MaxValueValidator(65535)]), + model_name="srv", + name="priority", + field=models.PositiveIntegerField( + help_text="La priorité du serveur cible (valeur entière non négative, plus elle est faible, plus ce serveur sera utilisé s'il est disponible)", + validators=[django.core.validators.MaxValueValidator(65535)], + ), ), migrations.AlterField( - model_name='srv', - name='target', - field=models.ForeignKey(help_text='Serveur cible', on_delete=django.db.models.deletion.PROTECT, to='machines.Domain'), + model_name="srv", + name="target", + field=models.ForeignKey( + help_text="Serveur cible", + on_delete=django.db.models.deletion.PROTECT, + to="machines.Domain", + ), ), migrations.AlterField( - model_name='srv', - name='weight', - field=models.PositiveIntegerField(help_text='Poids relatif pour les enregistrements de même priorité (valeur entière de 0 à 65535)', validators=[django.core.validators.MaxValueValidator(65535)]), + model_name="srv", + name="weight", + field=models.PositiveIntegerField( + help_text="Poids relatif pour les enregistrements de même priorité (valeur entière de 0 à 65535)", + validators=[django.core.validators.MaxValueValidator(65535)], + ), ), ] diff --git a/machines/migrations/0068_auto_20171116_0252.py b/machines/migrations/0068_auto_20171116_0252.py index 781f1038..1cf0b6f0 100644 --- a/machines/migrations/0068_auto_20171116_0252.py +++ b/machines/migrations/0068_auto_20171116_0252.py @@ -9,34 +9,55 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0067_auto_20171116_0152'), - ] + dependencies = [("machines", "0067_auto_20171116_0152")] operations = [ migrations.AlterField( - model_name='extension', - name='name', - field=models.CharField(help_text='Nom de la zone, doit commencer par un point (.example.org)', max_length=255, unique=True), + model_name="extension", + name="name", + field=models.CharField( + help_text="Nom de la zone, doit commencer par un point (.example.org)", + max_length=255, + unique=True, + ), ), migrations.AlterField( - model_name='extension', - name='origin', - field=models.OneToOneField(blank=True, help_text='Enregistrement A associé à la zone', null=True, on_delete=django.db.models.deletion.PROTECT, to='machines.IpList'), + model_name="extension", + name="origin", + field=models.OneToOneField( + blank=True, + help_text="Enregistrement A associé à la zone", + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="machines.IpList", + ), ), migrations.AlterField( - model_name='extension', - name='origin_v6', - field=models.GenericIPAddressField(blank=True, help_text='Enregistremen AAAA associé à la zone', null=True, protocol='IPv6'), + model_name="extension", + name="origin_v6", + field=models.GenericIPAddressField( + blank=True, + help_text="Enregistremen AAAA associé à la zone", + null=True, + protocol="IPv6", + ), ), migrations.AlterField( - model_name='srv', - name='priority', - field=models.PositiveIntegerField(default=0, help_text="La priorité du serveur cible (valeur entière non négative, plus elle est faible, plus ce serveur sera utilisé s'il est disponible)", validators=[django.core.validators.MaxValueValidator(65535)]), + model_name="srv", + name="priority", + field=models.PositiveIntegerField( + default=0, + help_text="La priorité du serveur cible (valeur entière non négative, plus elle est faible, plus ce serveur sera utilisé s'il est disponible)", + validators=[django.core.validators.MaxValueValidator(65535)], + ), ), migrations.AlterField( - model_name='srv', - name='weight', - field=models.PositiveIntegerField(default=0, help_text='Poids relatif pour les enregistrements de même priorité (valeur entière de 0 à 65535)', validators=[django.core.validators.MaxValueValidator(65535)]), + model_name="srv", + name="weight", + field=models.PositiveIntegerField( + default=0, + help_text="Poids relatif pour les enregistrements de même priorité (valeur entière de 0 à 65535)", + validators=[django.core.validators.MaxValueValidator(65535)], + ), ), ] diff --git a/machines/migrations/0069_auto_20171116_0822.py b/machines/migrations/0069_auto_20171116_0822.py index 90d4b309..8b880635 100644 --- a/machines/migrations/0069_auto_20171116_0822.py +++ b/machines/migrations/0069_auto_20171116_0822.py @@ -7,14 +7,17 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0068_auto_20171116_0252'), - ] + dependencies = [("machines", "0068_auto_20171116_0252")] operations = [ migrations.AlterField( - model_name='extension', - name='origin_v6', - field=models.GenericIPAddressField(blank=True, help_text='Enregistrement AAAA associé à la zone', null=True, protocol='IPv6'), - ), + model_name="extension", + name="origin_v6", + field=models.GenericIPAddressField( + blank=True, + help_text="Enregistrement AAAA associé à la zone", + null=True, + protocol="IPv6", + ), + ) ] diff --git a/machines/migrations/0070_auto_20171231_1947.py b/machines/migrations/0070_auto_20171231_1947.py index ef441d01..52fec594 100644 --- a/machines/migrations/0070_auto_20171231_1947.py +++ b/machines/migrations/0070_auto_20171231_1947.py @@ -7,73 +7,101 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('machines', '0069_auto_20171116_0822'), - ] + dependencies = [("machines", "0069_auto_20171116_0822")] operations = [ migrations.AlterModelOptions( - name='domain', - options={'permissions': (('view_domain', 'Peut voir un objet domain'),)}, + 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'))}, + 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'),)}, + name="interface", + options={ + "permissions": (("view_interface", "Peut voir un objet interface"),) + }, ), migrations.AlterModelOptions( - name='iplist', - options={'permissions': (('view_iplist', 'Peut voir un objet iplist'),)}, + 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'))}, + 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"))}, + 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"))}, + 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'),)}, + name="mx", options={"permissions": (("view_mx", "Peut voir un objet mx"),)} ), migrations.AlterModelOptions( - name='nas', - options={'permissions': (('view_nas', 'Peut voir un objet Nas'),)}, + name="nas", + options={"permissions": (("view_nas", "Peut voir un objet Nas"),)}, ), migrations.AlterModelOptions( - name='ns', - options={'permissions': (('view_nx', 'Peut voir un objet nx'),)}, + name="ns", options={"permissions": (("view_nx", "Peut voir un objet nx"),)} ), migrations.AlterModelOptions( - name='ouvertureportlist', - options={'permissions': (('view_ouvertureportlist', 'Peut voir un objet ouvertureport'),)}, + name="ouvertureportlist", + options={ + "permissions": ( + ("view_ouvertureportlist", "Peut voir un objet ouvertureport"), + ) + }, ), migrations.AlterModelOptions( - name='service', - options={'permissions': (('view_service', 'Peut voir un objet service'),)}, + name="service", + options={"permissions": (("view_service", "Peut voir un objet service"),)}, ), migrations.AlterModelOptions( - name='soa', - options={'permissions': (('view_soa', 'Peut voir un objet soa'),)}, + name="soa", + options={"permissions": (("view_soa", "Peut voir un objet soa"),)}, ), migrations.AlterModelOptions( - name='srv', - options={'permissions': (('view_soa', 'Peut voir un objet soa'),)}, + name="srv", + options={"permissions": (("view_soa", "Peut voir un objet soa"),)}, ), migrations.AlterModelOptions( - name='txt', - options={'permissions': (('view_txt', 'Peut voir un objet txt'),)}, + name="txt", + options={"permissions": (("view_txt", "Peut voir un objet txt"),)}, ), migrations.AlterModelOptions( - name='vlan', - options={'permissions': (('view_vlan', 'Peut voir un objet vlan'),)}, + name="vlan", + options={"permissions": (("view_vlan", "Peut voir un objet vlan"),)}, ), ] diff --git a/machines/migrations/0071_auto_20171231_2100.py b/machines/migrations/0071_auto_20171231_2100.py index 776edd82..1545f0b1 100644 --- a/machines/migrations/0071_auto_20171231_2100.py +++ b/machines/migrations/0071_auto_20171231_2100.py @@ -7,13 +7,10 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('machines', '0070_auto_20171231_1947'), - ] + dependencies = [("machines", "0070_auto_20171231_1947")] operations = [ migrations.AlterModelOptions( - name='ns', - options={'permissions': (('view_ns', 'Peut voir un objet ns'),)}, - ), + name="ns", options={"permissions": (("view_ns", "Peut voir un objet ns"),)} + ) ] diff --git a/machines/migrations/0072_auto_20180108_1822.py b/machines/migrations/0072_auto_20180108_1822.py index 64999764..c3cd72da 100644 --- a/machines/migrations/0072_auto_20180108_1822.py +++ b/machines/migrations/0072_auto_20180108_1822.py @@ -7,13 +7,19 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('machines', '0071_auto_20171231_2100'), - ] + 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"))}, - ), + name="interface", + options={ + "permissions": ( + ("view_interface", "Peut voir un objet interface"), + ( + "change_interface_machine", + "Peut changer le propriétaire d'une interface", + ), + ) + }, + ) ] diff --git a/machines/migrations/0073_auto_20180128_2203.py b/machines/migrations/0073_auto_20180128_2203.py index b09b9c47..a79689fe 100644 --- a/machines/migrations/0073_auto_20180128_2203.py +++ b/machines/migrations/0073_auto_20180128_2203.py @@ -8,22 +8,33 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0072_auto_20180108_1822'), - ] + dependencies = [("machines", "0072_auto_20180108_1822")] operations = [ migrations.CreateModel( - name='Ipv6List', + 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')), + ( + "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')]), + name="ipv6list", unique_together=set([("interface", "slaac_ip")]) ), ] diff --git a/machines/migrations/0074_auto_20180129_0352.py b/machines/migrations/0074_auto_20180129_0352.py index 298f2a8e..f61ded5e 100644 --- a/machines/migrations/0074_auto_20180129_0352.py +++ b/machines/migrations/0074_auto_20180129_0352.py @@ -7,13 +7,19 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('machines', '0073_auto_20180128_2203'), - ] + 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'))}, - ), + name="ipv6list", + options={ + "permissions": ( + ("view_ipv6list", "Peut voir un objet ipv6"), + ( + "change_ipv6list_slaac_ip", + "Peut changer la valeur slaac sur une ipv6", + ), + ) + }, + ) ] diff --git a/machines/migrations/0075_auto_20180130_0052.py b/machines/migrations/0075_auto_20180130_0052.py index 473824b8..befd100c 100644 --- a/machines/migrations/0075_auto_20180130_0052.py +++ b/machines/migrations/0075_auto_20180130_0052.py @@ -7,13 +7,8 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('machines', '0074_auto_20180129_0352'), - ] + dependencies = [("machines", "0074_auto_20180129_0352")] operations = [ - migrations.AlterUniqueTogether( - name='ipv6list', - unique_together=set([]), - ), + migrations.AlterUniqueTogether(name="ipv6list", unique_together=set([])) ] diff --git a/machines/migrations/0076_auto_20180130_1623.py b/machines/migrations/0076_auto_20180130_1623.py index 9aab328b..906e11e2 100644 --- a/machines/migrations/0076_auto_20180130_1623.py +++ b/machines/migrations/0076_auto_20180130_1623.py @@ -8,14 +8,16 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0075_auto_20180130_0052'), - ] + 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'), - ), + model_name="ipv6list", + name="interface", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="ipv6list", + to="machines.Interface", + ), + ) ] diff --git a/machines/migrations/0077_auto_20180409_2243.py b/machines/migrations/0077_auto_20180409_2243.py index 3ac63072..33114d33 100644 --- a/machines/migrations/0077_auto_20180409_2243.py +++ b/machines/migrations/0077_auto_20180409_2243.py @@ -8,14 +8,18 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0076_auto_20180130_1623'), - ] + dependencies = [("machines", "0076_auto_20180130_1623")] operations = [ migrations.AlterField( - model_name='extension', - name='origin', - field=models.ForeignKey(blank=True, help_text='Enregistrement A associé à la zone', null=True, on_delete=django.db.models.deletion.PROTECT, to='machines.IpList'), - ), + model_name="extension", + name="origin", + field=models.ForeignKey( + blank=True, + help_text="Enregistrement A associé à la zone", + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="machines.IpList", + ), + ) ] diff --git a/machines/migrations/0078_auto_20180415_1252.py b/machines/migrations/0078_auto_20180415_1252.py index 909ae847..161b9303 100644 --- a/machines/migrations/0078_auto_20180415_1252.py +++ b/machines/migrations/0078_auto_20180415_1252.py @@ -8,14 +8,16 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0077_auto_20180409_2243'), - ] + dependencies = [("machines", "0077_auto_20180409_2243")] operations = [ migrations.AlterField( - model_name='srv', - name='priority', - field=models.PositiveIntegerField(default=0, help_text="La priorité du serveur cible (valeur entière non négative, plus elle est faible, plus ce serveur sera utilisé s'il est disponible)", validators=[django.core.validators.MaxValueValidator(65535)]), - ), + model_name="srv", + name="priority", + field=models.PositiveIntegerField( + default=0, + help_text="La priorité du serveur cible (valeur entière non négative, plus elle est faible, plus ce serveur sera utilisé s'il est disponible)", + validators=[django.core.validators.MaxValueValidator(65535)], + ), + ) ] diff --git a/machines/migrations/0079_auto_20180416_0107.py b/machines/migrations/0079_auto_20180416_0107.py index df73f74b..a2244ada 100644 --- a/machines/migrations/0079_auto_20180416_0107.py +++ b/machines/migrations/0079_auto_20180416_0107.py @@ -4,33 +4,31 @@ from __future__ import unicode_literals from django.db import migrations + def rename_permission_soa_to_srv(apps, schema_editor): - Permission = apps.get_model('auth', 'Permission') + Permission = apps.get_model("auth", "Permission") # The Permission called 'view_soa' but in the Srv object try: to_rename = Permission.objects.get( - codename='view_soa', - content_type__model='srv' + codename="view_soa", content_type__model="srv" ) except Permission.DoesNotExist: # The permission is missing so no problem pass else: - to_rename.name = 'Peut voir un object srv' - to_rename.codename = 'view_srv' + to_rename.name = "Peut voir un object srv" + to_rename.codename = "view_srv" to_rename.save() class Migration(migrations.Migration): - dependencies = [ - ('machines', '0078_auto_20180415_1252'), - ] + dependencies = [("machines", "0078_auto_20180415_1252")] operations = [ migrations.RunPython(rename_permission_soa_to_srv), migrations.AlterModelOptions( - name='srv', - options={'permissions': (('view_srv', 'Peut voir un objet srv'),)}, + name="srv", + options={"permissions": (("view_srv", "Peut voir un objet srv"),)}, ), ] diff --git a/machines/migrations/0080_auto_20180502_2334.py b/machines/migrations/0080_auto_20180502_2334.py index 7097dde2..997fc151 100644 --- a/machines/migrations/0080_auto_20180502_2334.py +++ b/machines/migrations/0080_auto_20180502_2334.py @@ -8,14 +8,14 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0079_auto_20180416_0107'), - ] + dependencies = [("machines", "0079_auto_20180416_0107")] operations = [ migrations.AlterField( - model_name='ns', - name='ns', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='machines.Domain'), - ), + model_name="ns", + name="ns", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, to="machines.Domain" + ), + ) ] diff --git a/machines/migrations/0081_auto_20180521_1413.py b/machines/migrations/0081_auto_20180521_1413.py index 7948796a..7e5509aa 100644 --- a/machines/migrations/0081_auto_20180521_1413.py +++ b/machines/migrations/0081_auto_20180521_1413.py @@ -8,14 +8,14 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0080_auto_20180502_2334'), - ] + dependencies = [("machines", "0080_auto_20180502_2334")] operations = [ migrations.AlterField( - model_name='extension', - name='soa', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='machines.SOA'), - ), + model_name="extension", + name="soa", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="machines.SOA" + ), + ) ] diff --git a/machines/migrations/0082_auto_20180525_2209.py b/machines/migrations/0082_auto_20180525_2209.py index 1da2370c..3691946d 100644 --- a/machines/migrations/0082_auto_20180525_2209.py +++ b/machines/migrations/0082_auto_20180525_2209.py @@ -7,13 +7,15 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('machines', '0081_auto_20180521_1413'), - ] + dependencies = [("machines", "0081_auto_20180521_1413")] operations = [ migrations.AlterModelOptions( - name='service_link', - options={'permissions': (('view_service_link', 'Peut voir un objet service_link'),)}, - ), + name="service_link", + options={ + "permissions": ( + ("view_service_link", "Peut voir un objet service_link"), + ) + }, + ) ] diff --git a/machines/migrations/0083_remove_duplicate_rights.py b/machines/migrations/0083_remove_duplicate_rights.py index 05ad2938..a4f4ec32 100644 --- a/machines/migrations/0083_remove_duplicate_rights.py +++ b/machines/migrations/0083_remove_duplicate_rights.py @@ -3,14 +3,14 @@ from __future__ import unicode_literals from django.db import migrations + def remove_permission_alias(apps, schema_editor): - Permission = apps.get_model('auth', 'Permission') - for codename in ['add_alias', 'change_alias', 'delete_alias']: + Permission = apps.get_model("auth", "Permission") + for codename in ["add_alias", "change_alias", "delete_alias"]: # Retrieve the wrong permission try: to_remove = Permission.objects.get( - codename=codename, - content_type__model='domain' + codename=codename, content_type__model="domain" ) except Permission.DoesNotExist: # The permission is missing so no problem @@ -20,13 +20,12 @@ def remove_permission_alias(apps, schema_editor): def remove_permission_text(apps, schema_editor): - Permission = apps.get_model('auth', 'Permission') - for codename in ['add_text', 'change_text', 'delete_text']: + Permission = apps.get_model("auth", "Permission") + for codename in ["add_text", "change_text", "delete_text"]: # Retrieve the wrong permission try: to_remove = Permission.objects.get( - codename=codename, - content_type__model='txt' + codename=codename, content_type__model="txt" ) except Permission.DoesNotExist: # The permission is missing so no problem @@ -37,9 +36,7 @@ def remove_permission_text(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('machines', '0082_auto_20180525_2209'), - ] + dependencies = [("machines", "0082_auto_20180525_2209")] operations = [ migrations.RunPython(remove_permission_text), diff --git a/machines/migrations/0084_dname.py b/machines/migrations/0084_dname.py index 4cbeb492..37dfe623 100644 --- a/machines/migrations/0084_dname.py +++ b/machines/migrations/0084_dname.py @@ -9,23 +9,35 @@ import re2o.mixins class Migration(migrations.Migration): - dependencies = [ - ('machines', '0083_remove_duplicate_rights'), - ] + dependencies = [("machines", "0083_remove_duplicate_rights")] operations = [ migrations.CreateModel( - name='DName', + name="DName", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('alias', models.CharField(max_length=255)), - ('zone', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='machines.Extension')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("alias", models.CharField(max_length=255)), + ( + "zone", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to="machines.Extension", + ), + ), ], options={ - 'permissions': (('view_dname', 'Can see a dname object'),), - 'verbose_name': 'DNAME entry', - 'verbose_name_plural': 'DNAME entries' + "permissions": (("view_dname", "Can see a dname object"),), + "verbose_name": "DNAME entry", + "verbose_name_plural": "DNAME entries", }, bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), - ), + ) ] diff --git a/machines/migrations/0085_sshfingerprint.py b/machines/migrations/0085_sshfingerprint.py index 47f11d07..74ca610f 100644 --- a/machines/migrations/0085_sshfingerprint.py +++ b/machines/migrations/0085_sshfingerprint.py @@ -9,25 +9,57 @@ import re2o.mixins class Migration(migrations.Migration): - dependencies = [ - ('machines', '0084_dname'), - ] + dependencies = [("machines", "0084_dname")] operations = [ migrations.CreateModel( - name='SshFp', + name="SshFp", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('pub_key_entry', models.TextField(help_text='SSH public key', max_length=2048)), - ('algo', models.CharField(choices=[('ssh-rsa', 'ssh-rsa'), ('ssh-ed25519', 'ssh-ed25519'), ('ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp256'), ('ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp384'), ('ecdsa-sha2-nistp521', 'ecdsa-sha2-nistp521')], max_length=32)), - ('comment', models.CharField(blank=True, help_text='Comment', max_length=255, null=True)), - ('machine', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='machines.Machine')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "pub_key_entry", + models.TextField(help_text="SSH public key", max_length=2048), + ), + ( + "algo", + models.CharField( + choices=[ + ("ssh-rsa", "ssh-rsa"), + ("ssh-ed25519", "ssh-ed25519"), + ("ecdsa-sha2-nistp256", "ecdsa-sha2-nistp256"), + ("ecdsa-sha2-nistp384", "ecdsa-sha2-nistp384"), + ("ecdsa-sha2-nistp521", "ecdsa-sha2-nistp521"), + ], + max_length=32, + ), + ), + ( + "comment", + models.CharField( + blank=True, help_text="Comment", max_length=255, null=True + ), + ), + ( + "machine", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="machines.Machine", + ), + ), ], options={ - 'verbose_name': 'SSHFP record', - 'verbose_name_plural': 'SSHFP records', - 'permissions': (('view_sshfp', 'Can see an SSHFP record'),), + "verbose_name": "SSHFP record", + "verbose_name_plural": "SSHFP records", + "permissions": (("view_sshfp", "Can see an SSHFP record"),), }, bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), - ), + ) ] diff --git a/machines/migrations/0086_role.py b/machines/migrations/0086_role.py index a23de26f..c0956a5f 100644 --- a/machines/migrations/0086_role.py +++ b/machines/migrations/0086_role.py @@ -8,20 +8,51 @@ import re2o.mixins class Migration(migrations.Migration): - dependencies = [ - ('machines', '0085_sshfingerprint'), - ] + dependencies = [("machines", "0085_sshfingerprint")] operations = [ migrations.CreateModel( - name='Role', + name="Role", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('role_type', models.CharField(max_length=255, unique=True)), - ('servers', models.ManyToManyField(to='machines.Interface')), - ('specific_role', models.CharField(blank=True, choices=[('dhcp-server', 'DHCP server'), ('switch-conf-server', 'Switches configuration server'), ('dns-recursif-server', 'Recursive DNS server'), ('ntp-server', 'NTP server'), ('radius-server', 'Radius server'), ('log-server', 'Log server'), ('ldap-master-server', 'LDAP master server'), ('ldap-backup-server', 'LDAP backup server'), ('smtp-server', 'SMTP server'), ('postgresql-server', 'postgreSQL server'), ('mysql-server', 'mySQL server'), ('sql-client', 'SQL client'), ('gateway', 'Gatewaw')], max_length=32, null=True)) + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("role_type", models.CharField(max_length=255, unique=True)), + ("servers", models.ManyToManyField(to="machines.Interface")), + ( + "specific_role", + models.CharField( + blank=True, + choices=[ + ("dhcp-server", "DHCP server"), + ("switch-conf-server", "Switches configuration server"), + ("dns-recursif-server", "Recursive DNS server"), + ("ntp-server", "NTP server"), + ("radius-server", "Radius server"), + ("log-server", "Log server"), + ("ldap-master-server", "LDAP master server"), + ("ldap-backup-server", "LDAP backup server"), + ("smtp-server", "SMTP server"), + ("postgresql-server", "postgreSQL server"), + ("mysql-server", "mySQL server"), + ("sql-client", "SQL client"), + ("gateway", "Gatewaw"), + ], + max_length=32, + null=True, + ), + ), ], - options={'permissions': (('view_role', 'Can view a role.'),), 'verbose_name': 'Server role'}, + options={ + "permissions": (("view_role", "Can view a role."),), + "verbose_name": "Server role", + }, bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), - ), + ) ] diff --git a/machines/migrations/0087_dnssec.py b/machines/migrations/0087_dnssec.py index cc2a25ec..de505ea5 100644 --- a/machines/migrations/0087_dnssec.py +++ b/machines/migrations/0087_dnssec.py @@ -7,19 +7,21 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0086_role'), - ] + dependencies = [("machines", "0086_role")] operations = [ migrations.AddField( - model_name='iptype', - name='dnssec_reverse_v4', - field=models.BooleanField(default=False, help_text='Activer DNSSEC sur le reverse DNS IPv4'), + model_name="iptype", + name="dnssec_reverse_v4", + field=models.BooleanField( + default=False, help_text="Activer DNSSEC sur le reverse DNS IPv4" + ), ), migrations.AddField( - model_name='iptype', - name='dnssec_reverse_v6', - field=models.BooleanField(default=False, help_text='Activer DNSSEC sur le reverse DNS IPv6'), + model_name="iptype", + name="dnssec_reverse_v6", + field=models.BooleanField( + default=False, help_text="Activer DNSSEC sur le reverse DNS IPv6" + ), ), ] diff --git a/machines/migrations/0088_iptype_prefix_v6_length.py b/machines/migrations/0088_iptype_prefix_v6_length.py index e061167c..5380eb41 100644 --- a/machines/migrations/0088_iptype_prefix_v6_length.py +++ b/machines/migrations/0088_iptype_prefix_v6_length.py @@ -8,14 +8,18 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0087_dnssec'), - ] + dependencies = [("machines", "0087_dnssec")] operations = [ migrations.AddField( - model_name='iptype', - name='prefix_v6_length', - field=models.IntegerField(default=64, validators=[django.core.validators.MaxValueValidator(128), django.core.validators.MinValueValidator(0)]), - ), + model_name="iptype", + name="prefix_v6_length", + field=models.IntegerField( + default=64, + validators=[ + django.core.validators.MaxValueValidator(128), + django.core.validators.MinValueValidator(0), + ], + ), + ) ] diff --git a/machines/migrations/0089_auto_20180805_1148.py b/machines/migrations/0089_auto_20180805_1148.py index 76962283..3ad70d15 100644 --- a/machines/migrations/0089_auto_20180805_1148.py +++ b/machines/migrations/0089_auto_20180805_1148.py @@ -8,14 +8,12 @@ import macaddress.fields class Migration(migrations.Migration): - dependencies = [ - ('machines', '0088_iptype_prefix_v6_length'), - ] + dependencies = [("machines", "0088_iptype_prefix_v6_length")] operations = [ migrations.AlterField( - model_name='interface', - name='mac_address', + model_name="interface", + name="mac_address", field=macaddress.fields.MACAddressField(integer=False, max_length=17), - ), + ) ] diff --git a/machines/migrations/0090_auto_20180805_1459.py b/machines/migrations/0090_auto_20180805_1459.py index 08af8587..cc5f3efd 100644 --- a/machines/migrations/0090_auto_20180805_1459.py +++ b/machines/migrations/0090_auto_20180805_1459.py @@ -7,14 +7,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0089_auto_20180805_1148'), - ] + dependencies = [("machines", "0089_auto_20180805_1148")] operations = [ migrations.AlterField( - model_name='ipv6list', - name='ipv6', - field=models.GenericIPAddressField(protocol='IPv6'), - ), + model_name="ipv6list", + name="ipv6", + field=models.GenericIPAddressField(protocol="IPv6"), + ) ] diff --git a/machines/migrations/0091_auto_20180806_2310.py b/machines/migrations/0091_auto_20180806_2310.py index cd756cad..918f2215 100644 --- a/machines/migrations/0091_auto_20180806_2310.py +++ b/machines/migrations/0091_auto_20180806_2310.py @@ -8,19 +8,29 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0090_auto_20180805_1459'), - ] + dependencies = [("machines", "0090_auto_20180805_1459")] operations = [ migrations.AddField( - model_name='iptype', - name='domaine_ip_netmask', - field=models.IntegerField(default=24, help_text='Netmask for the ipv4 range domain', validators=[django.core.validators.MaxValueValidator(31), django.core.validators.MinValueValidator(8)]), + model_name="iptype", + name="domaine_ip_netmask", + field=models.IntegerField( + default=24, + help_text="Netmask for the ipv4 range domain", + validators=[ + django.core.validators.MaxValueValidator(31), + django.core.validators.MinValueValidator(8), + ], + ), ), migrations.AddField( - model_name='iptype', - name='domaine_ip_network', - field=models.GenericIPAddressField(blank=True, help_text='Network containing the ipv4 range domain ip start/stop. Optional', null=True, protocol='IPv4'), + model_name="iptype", + name="domaine_ip_network", + field=models.GenericIPAddressField( + blank=True, + help_text="Network containing the ipv4 range domain ip start/stop. Optional", + null=True, + protocol="IPv4", + ), ), ] diff --git a/machines/migrations/0092_auto_20180807_0926.py b/machines/migrations/0092_auto_20180807_0926.py index f109a650..9ef5657c 100644 --- a/machines/migrations/0092_auto_20180807_0926.py +++ b/machines/migrations/0092_auto_20180807_0926.py @@ -7,19 +7,13 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('machines', '0091_auto_20180806_2310'), - ] + dependencies = [("machines", "0091_auto_20180806_2310")] operations = [ migrations.RenameField( - model_name='iptype', - old_name='dnssec_reverse_v4', - new_name='reverse_v4', + model_name="iptype", old_name="dnssec_reverse_v4", new_name="reverse_v4" ), migrations.RenameField( - model_name='iptype', - old_name='dnssec_reverse_v6', - new_name='reverse_v6', + model_name="iptype", old_name="dnssec_reverse_v6", new_name="reverse_v6" ), ] diff --git a/machines/migrations/0093_auto_20180807_1115.py b/machines/migrations/0093_auto_20180807_1115.py index 866cb87d..04112c0d 100644 --- a/machines/migrations/0093_auto_20180807_1115.py +++ b/machines/migrations/0093_auto_20180807_1115.py @@ -8,19 +8,17 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0092_auto_20180807_0926'), - ] + dependencies = [("machines", "0092_auto_20180807_0926")] operations = [ migrations.AlterField( - model_name='mx', - name='name', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='machines.Domain'), + model_name="mx", + name="name", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, to="machines.Domain" + ), ), migrations.AlterField( - model_name='mx', - name='priority', - field=models.PositiveIntegerField(), + model_name="mx", name="priority", field=models.PositiveIntegerField() ), ] diff --git a/machines/migrations/0094_auto_20180815_1918.py b/machines/migrations/0094_auto_20180815_1918.py index 775ac2c7..0465e489 100644 --- a/machines/migrations/0094_auto_20180815_1918.py +++ b/machines/migrations/0094_auto_20180815_1918.py @@ -10,212 +10,411 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0093_auto_20180807_1115'), - ] + dependencies = [("machines", "0093_auto_20180807_1115")] operations = [ migrations.AlterModelOptions( - name='dname', - options={'permissions': (('view_dname', 'Can view a DNAME record object'),), 'verbose_name': 'DNAME record', 'verbose_name_plural': 'DNAME records'}, + name="dname", + options={ + "permissions": (("view_dname", "Can view a DNAME record object"),), + "verbose_name": "DNAME record", + "verbose_name_plural": "DNAME records", + }, ), migrations.AlterModelOptions( - name='domain', - options={'permissions': (('view_domain', 'Can view a domain object'),), 'verbose_name': 'domain', 'verbose_name_plural': 'domains'}, + name="domain", + options={ + "permissions": (("view_domain", "Can view a domain object"),), + "verbose_name": "domain", + "verbose_name_plural": "domains", + }, ), migrations.AlterModelOptions( - name='extension', - options={'permissions': (('view_extension', 'Can view an extension object'), ('use_all_extension', 'Can use all extensions')), 'verbose_name': 'DNS extension', 'verbose_name_plural': 'DNS extensions'}, + name="extension", + options={ + "permissions": ( + ("view_extension", "Can view an extension object"), + ("use_all_extension", "Can use all extensions"), + ), + "verbose_name": "DNS extension", + "verbose_name_plural": "DNS extensions", + }, ), migrations.AlterModelOptions( - name='interface', - options={'permissions': (('view_interface', 'Can view an interface object'), ('change_interface_machine', 'Can change the owner of an interface')), 'verbose_name': 'interface', 'verbose_name_plural': 'interfaces'}, + name="interface", + options={ + "permissions": ( + ("view_interface", "Can view an interface object"), + ( + "change_interface_machine", + "Can change the owner of an interface", + ), + ), + "verbose_name": "interface", + "verbose_name_plural": "interfaces", + }, ), migrations.AlterModelOptions( - name='iplist', - options={'permissions': (('view_iplist', 'Can view an IPv4 addresses list object'),), 'verbose_name': 'IPv4 addresses list', 'verbose_name_plural': 'IPv4 addresses lists'}, + name="iplist", + options={ + "permissions": ( + ("view_iplist", "Can view an IPv4 addresses list object"), + ), + "verbose_name": "IPv4 addresses list", + "verbose_name_plural": "IPv4 addresses lists", + }, ), migrations.AlterModelOptions( - name='iptype', - options={'permissions': (('view_iptype', 'Can view an IP type object'), ('use_all_iptype', 'Can use all IP types')), 'verbose_name': 'IP type', 'verbose_name_plural': 'IP types'}, + name="iptype", + options={ + "permissions": ( + ("view_iptype", "Can view an IP type object"), + ("use_all_iptype", "Can use all IP types"), + ), + "verbose_name": "IP type", + "verbose_name_plural": "IP types", + }, ), migrations.AlterModelOptions( - name='ipv6list', - options={'permissions': (('view_ipv6list', 'Can view an IPv6 addresses list object'), ('change_ipv6list_slaac_ip', 'Can change the SLAAC value of an IPv6 addresses list')), 'verbose_name': 'IPv6 addresses list', 'verbose_name_plural': 'IPv6 addresses lists'}, + name="ipv6list", + options={ + "permissions": ( + ("view_ipv6list", "Can view an IPv6 addresses list object"), + ( + "change_ipv6list_slaac_ip", + "Can change the SLAAC value of an IPv6 addresses list", + ), + ), + "verbose_name": "IPv6 addresses list", + "verbose_name_plural": "IPv6 addresses lists", + }, ), migrations.AlterModelOptions( - name='machine', - options={'permissions': (('view_machine', 'Can view a machine object'), ('change_machine_user', 'Can change the user of a machine')), 'verbose_name': 'machine', 'verbose_name_plural': 'machines'}, + name="machine", + options={ + "permissions": ( + ("view_machine", "Can view a machine object"), + ("change_machine_user", "Can change the user of a machine"), + ), + "verbose_name": "machine", + "verbose_name_plural": "machines", + }, ), migrations.AlterModelOptions( - name='machinetype', - options={'permissions': (('view_machinetype', 'Can view a machine type object'), ('use_all_machinetype', 'Can use all machine types')), 'verbose_name': 'machine type', 'verbose_name_plural': 'machine types'}, + name="machinetype", + options={ + "permissions": ( + ("view_machinetype", "Can view a machine type object"), + ("use_all_machinetype", "Can use all machine types"), + ), + "verbose_name": "machine type", + "verbose_name_plural": "machine types", + }, ), migrations.AlterModelOptions( - name='mx', - options={'permissions': (('view_mx', 'Can view an MX record object'),), 'verbose_name': 'MX record', 'verbose_name_plural': 'MX records'}, + name="mx", + options={ + "permissions": (("view_mx", "Can view an MX record object"),), + "verbose_name": "MX record", + "verbose_name_plural": "MX records", + }, ), migrations.AlterModelOptions( - name='nas', - options={'permissions': (('view_nas', 'Can view a NAS device object'),), 'verbose_name': 'NAS device', 'verbose_name_plural': 'NAS devices'}, + name="nas", + options={ + "permissions": (("view_nas", "Can view a NAS device object"),), + "verbose_name": "NAS device", + "verbose_name_plural": "NAS devices", + }, ), migrations.AlterModelOptions( - name='ns', - options={'permissions': (('view_ns', 'Can view an NS record object'),), 'verbose_name': 'NS record', 'verbose_name_plural': 'NS records'}, + name="ns", + options={ + "permissions": (("view_ns", "Can view an NS record object"),), + "verbose_name": "NS record", + "verbose_name_plural": "NS records", + }, ), migrations.AlterModelOptions( - name='ouvertureport', - options={'verbose_name': 'ports openings'}, + name="ouvertureport", options={"verbose_name": "ports openings"} ), migrations.AlterModelOptions( - name='ouvertureportlist', - options={'permissions': (('view_ouvertureportlist', 'Can view a ports opening list object'),), 'verbose_name': 'ports opening list', 'verbose_name_plural': 'ports opening lists'}, + name="ouvertureportlist", + options={ + "permissions": ( + ("view_ouvertureportlist", "Can view a ports opening list object"), + ), + "verbose_name": "ports opening list", + "verbose_name_plural": "ports opening lists", + }, ), migrations.AlterModelOptions( - name='role', - options={'permissions': (('view_role', 'Can view a role object'),), 'verbose_name': 'server role', 'verbose_name_plural': 'server roles'}, + name="role", + options={ + "permissions": (("view_role", "Can view a role object"),), + "verbose_name": "server role", + "verbose_name_plural": "server roles", + }, ), migrations.AlterModelOptions( - name='service', - options={'permissions': (('view_service', 'Can view a service object'),), 'verbose_name': 'service to generate (DHCP, DNS, ...)', 'verbose_name_plural': 'services to generate (DHCP, DNS, ...)'}, + name="service", + options={ + "permissions": (("view_service", "Can view a service object"),), + "verbose_name": "service to generate (DHCP, DNS, ...)", + "verbose_name_plural": "services to generate (DHCP, DNS, ...)", + }, ), migrations.AlterModelOptions( - name='service_link', - options={'permissions': (('view_service_link', 'Can view a service server link object'),), 'verbose_name': 'link between service and server', 'verbose_name_plural': 'links between service and server'}, + name="service_link", + options={ + "permissions": ( + ("view_service_link", "Can view a service server link object"), + ), + "verbose_name": "link between service and server", + "verbose_name_plural": "links between service and server", + }, ), migrations.AlterModelOptions( - name='soa', - options={'permissions': (('view_soa', 'Can view an SOA record object'),), 'verbose_name': 'SOA record', 'verbose_name_plural': 'SOA records'}, + name="soa", + options={ + "permissions": (("view_soa", "Can view an SOA record object"),), + "verbose_name": "SOA record", + "verbose_name_plural": "SOA records", + }, ), migrations.AlterModelOptions( - name='srv', - options={'permissions': (('view_srv', 'Can view an SRV record object'),), 'verbose_name': 'SRV record', 'verbose_name_plural': 'SRV records'}, + name="srv", + options={ + "permissions": (("view_srv", "Can view an SRV record object"),), + "verbose_name": "SRV record", + "verbose_name_plural": "SRV records", + }, ), migrations.AlterModelOptions( - name='sshfp', - options={'permissions': (('view_sshfp', 'Can view an SSHFP record object'),), 'verbose_name': 'SSHFP record', 'verbose_name_plural': 'SSHFP records'}, + name="sshfp", + options={ + "permissions": (("view_sshfp", "Can view an SSHFP record object"),), + "verbose_name": "SSHFP record", + "verbose_name_plural": "SSHFP records", + }, ), migrations.AlterModelOptions( - name='txt', - options={'permissions': (('view_txt', 'Can view a TXT record object'),), 'verbose_name': 'TXT record', 'verbose_name_plural': 'TXT records'}, + name="txt", + options={ + "permissions": (("view_txt", "Can view a TXT record object"),), + "verbose_name": "TXT record", + "verbose_name_plural": "TXT records", + }, ), migrations.AlterModelOptions( - name='vlan', - options={'permissions': (('view_vlan', 'Can view a VLAN object'),), 'verbose_name': 'VLAN', 'verbose_name_plural': 'VLANs'}, + name="vlan", + options={ + "permissions": (("view_vlan", "Can view a VLAN object"),), + "verbose_name": "VLAN", + "verbose_name_plural": "VLANs", + }, ), migrations.AlterField( - model_name='domain', - name='name', - field=models.CharField(help_text='Mandatory and unique, must not contain dots.', max_length=255), + model_name="domain", + name="name", + field=models.CharField( + help_text="Mandatory and unique, must not contain dots.", max_length=255 + ), ), migrations.AlterField( - model_name='extension', - name='name', - field=models.CharField(help_text='Zone name, must begin with a dot (.example.org)', max_length=255, unique=True), + model_name="extension", + name="name", + field=models.CharField( + help_text="Zone name, must begin with a dot (.example.org)", + max_length=255, + unique=True, + ), ), migrations.AlterField( - model_name='extension', - name='origin', - field=models.ForeignKey(blank=True, help_text='A record associated with the zone', null=True, on_delete=django.db.models.deletion.PROTECT, to='machines.IpList'), + model_name="extension", + name="origin", + field=models.ForeignKey( + blank=True, + help_text="A record associated with the zone", + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="machines.IpList", + ), ), migrations.AlterField( - model_name='extension', - name='origin_v6', - field=models.GenericIPAddressField(blank=True, help_text='AAAA record associated with the zone', null=True, protocol='IPv6'), + model_name="extension", + name="origin_v6", + field=models.GenericIPAddressField( + blank=True, + help_text="AAAA record associated with the zone", + null=True, + protocol="IPv6", + ), ), migrations.AlterField( - model_name='iptype', - name='domaine_ip_netmask', - field=models.IntegerField(default=24, help_text="Netmask for the domain's IPv4 range", validators=[django.core.validators.MaxValueValidator(31), django.core.validators.MinValueValidator(8)]), + model_name="iptype", + name="domaine_ip_netmask", + field=models.IntegerField( + default=24, + help_text="Netmask for the domain's IPv4 range", + validators=[ + django.core.validators.MaxValueValidator(31), + django.core.validators.MinValueValidator(8), + ], + ), ), migrations.AlterField( - model_name='iptype', - name='domaine_ip_network', - field=models.GenericIPAddressField(blank=True, help_text="Network containing the domain's IPv4 range (optional)", null=True, protocol='IPv4'), + model_name="iptype", + name="domaine_ip_network", + field=models.GenericIPAddressField( + blank=True, + help_text="Network containing the domain's IPv4 range (optional)", + null=True, + protocol="IPv4", + ), ), migrations.AlterField( - model_name='iptype', - name='reverse_v4', - field=models.BooleanField(default=False, help_text='Enable reverse DNS for IPv4'), + model_name="iptype", + name="reverse_v4", + field=models.BooleanField( + default=False, help_text="Enable reverse DNS for IPv4" + ), ), migrations.AlterField( - model_name='iptype', - name='reverse_v6', - field=models.BooleanField(default=False, help_text='Enable reverse DNS for IPv6'), + model_name="iptype", + name="reverse_v6", + field=models.BooleanField( + default=False, help_text="Enable reverse DNS for IPv6" + ), ), migrations.AlterField( - model_name='machine', - name='name', - field=models.CharField(blank=True, help_text='Optional', max_length=255, null=True), + model_name="machine", + name="name", + field=models.CharField( + blank=True, help_text="Optional", max_length=255, null=True + ), ), migrations.AlterField( - model_name='ouvertureportlist', - name='name', - field=models.CharField(help_text='Name of the ports configuration', max_length=255), + model_name="ouvertureportlist", + name="name", + field=models.CharField( + help_text="Name of the ports configuration", max_length=255 + ), ), migrations.AlterField( - model_name='role', - name='specific_role', - field=models.CharField(blank=True, choices=[('dhcp-server', 'DHCP server'), ('switch-conf-server', 'Switches configuration server'), ('dns-recursif-server', 'Recursive DNS server'), ('ntp-server', 'NTP server'), ('radius-server', 'RADIUS server'), ('log-server', 'Log server'), ('ldap-master-server', 'LDAP master server'), ('ldap-backup-server', 'LDAP backup server'), ('smtp-server', 'SMTP server'), ('postgresql-server', 'postgreSQL server'), ('mysql-server', 'mySQL server'), ('sql-client', 'SQL client'), ('gateway', 'Gateway')], max_length=32, null=True), + model_name="role", + name="specific_role", + field=models.CharField( + blank=True, + choices=[ + ("dhcp-server", "DHCP server"), + ("switch-conf-server", "Switches configuration server"), + ("dns-recursif-server", "Recursive DNS server"), + ("ntp-server", "NTP server"), + ("radius-server", "RADIUS server"), + ("log-server", "Log server"), + ("ldap-master-server", "LDAP master server"), + ("ldap-backup-server", "LDAP backup server"), + ("smtp-server", "SMTP server"), + ("postgresql-server", "postgreSQL server"), + ("mysql-server", "mySQL server"), + ("sql-client", "SQL client"), + ("gateway", "Gateway"), + ], + max_length=32, + null=True, + ), ), migrations.AlterField( - model_name='service', - name='min_time_regen', - field=models.DurationField(default=datetime.timedelta(0, 60), help_text='Minimal time before regeneration of the service.'), + model_name="service", + name="min_time_regen", + field=models.DurationField( + default=datetime.timedelta(0, 60), + help_text="Minimal time before regeneration of the service.", + ), ), migrations.AlterField( - model_name='service', - name='regular_time_regen', - field=models.DurationField(default=datetime.timedelta(0, 3600), help_text='Maximal time before regeneration of the service.'), + model_name="service", + name="regular_time_regen", + field=models.DurationField( + default=datetime.timedelta(0, 3600), + help_text="Maximal time before regeneration of the service.", + ), ), migrations.AlterField( - model_name='soa', - name='expire', - field=models.PositiveIntegerField(default=3600000, help_text='Seconds before the secondary DNS stop answering requests in case of primary DNS timeout'), + model_name="soa", + name="expire", + field=models.PositiveIntegerField( + default=3600000, + help_text="Seconds before the secondary DNS stop answering requests in case of primary DNS timeout", + ), ), migrations.AlterField( - model_name='soa', - name='mail', - field=models.EmailField(help_text='Contact email address for the zone', max_length=254), + model_name="soa", + name="mail", + field=models.EmailField( + help_text="Contact email address for the zone", max_length=254 + ), ), migrations.AlterField( - model_name='soa', - name='refresh', - field=models.PositiveIntegerField(default=86400, help_text='Seconds before the secondary DNS have to ask the primary DNS serial to detect a modification'), + model_name="soa", + name="refresh", + field=models.PositiveIntegerField( + default=86400, + help_text="Seconds before the secondary DNS have to ask the primary DNS serial to detect a modification", + ), ), migrations.AlterField( - model_name='soa', - name='retry', - field=models.PositiveIntegerField(default=7200, help_text='Seconds before the secondary DNS ask the serial again in case of a primary DNS timeout'), + model_name="soa", + name="retry", + field=models.PositiveIntegerField( + default=7200, + help_text="Seconds before the secondary DNS ask the serial again in case of a primary DNS timeout", + ), ), migrations.AlterField( - model_name='soa', - name='ttl', - field=models.PositiveIntegerField(default=172800, help_text='Time to Live'), + model_name="soa", + name="ttl", + field=models.PositiveIntegerField(default=172800, help_text="Time to Live"), ), migrations.AlterField( - model_name='srv', - name='port', - field=models.PositiveIntegerField(help_text='TCP/UDP port', validators=[django.core.validators.MaxValueValidator(65535)]), + model_name="srv", + name="port", + field=models.PositiveIntegerField( + help_text="TCP/UDP port", + validators=[django.core.validators.MaxValueValidator(65535)], + ), ), migrations.AlterField( - model_name='srv', - name='priority', - field=models.PositiveIntegerField(default=0, help_text='Priority of the target server (positive integer value, the lower it is, the more the server will be used if available)', validators=[django.core.validators.MaxValueValidator(65535)]), + model_name="srv", + name="priority", + field=models.PositiveIntegerField( + default=0, + help_text="Priority of the target server (positive integer value, the lower it is, the more the server will be used if available)", + validators=[django.core.validators.MaxValueValidator(65535)], + ), ), migrations.AlterField( - model_name='srv', - name='target', - field=models.ForeignKey(help_text='Target server', on_delete=django.db.models.deletion.PROTECT, to='machines.Domain'), + model_name="srv", + name="target", + field=models.ForeignKey( + help_text="Target server", + on_delete=django.db.models.deletion.PROTECT, + to="machines.Domain", + ), ), migrations.AlterField( - model_name='srv', - name='ttl', - field=models.PositiveIntegerField(default=172800, help_text='Time to Live'), + model_name="srv", + name="ttl", + field=models.PositiveIntegerField(default=172800, help_text="Time to Live"), ), migrations.AlterField( - model_name='srv', - name='weight', - field=models.PositiveIntegerField(default=0, help_text='Relative weight for records with the same priority (integer value between 0 and 65535)', validators=[django.core.validators.MaxValueValidator(65535)]), + model_name="srv", + name="weight", + field=models.PositiveIntegerField( + default=0, + help_text="Relative weight for records with the same priority (integer value between 0 and 65535)", + validators=[django.core.validators.MaxValueValidator(65535)], + ), ), ] diff --git a/machines/migrations/0095_auto_20180919_2225.py b/machines/migrations/0095_auto_20180919_2225.py index 66c082ff..6a7536ee 100644 --- a/machines/migrations/0095_auto_20180919_2225.py +++ b/machines/migrations/0095_auto_20180919_2225.py @@ -7,34 +7,32 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0094_auto_20180815_1918'), - ] + dependencies = [("machines", "0094_auto_20180815_1918")] operations = [ migrations.AddField( - model_name='vlan', - name='arp_protect', + model_name="vlan", + name="arp_protect", field=models.BooleanField(default=False), ), migrations.AddField( - model_name='vlan', - name='dhcp_snooping', + model_name="vlan", + name="dhcp_snooping", field=models.BooleanField(default=False), ), migrations.AddField( - model_name='vlan', - name='dhcpv6_snooping', + model_name="vlan", + name="dhcpv6_snooping", field=models.BooleanField(default=False), ), migrations.AddField( - model_name='vlan', - name='igmp', - field=models.BooleanField(default=False, help_text='Gestion multicast v4'), + model_name="vlan", + name="igmp", + field=models.BooleanField(default=False, help_text="Gestion multicast v4"), ), migrations.AddField( - model_name='vlan', - name='mld', - field=models.BooleanField(default=False, help_text='Gestion multicast v6'), + model_name="vlan", + name="mld", + field=models.BooleanField(default=False, help_text="Gestion multicast v6"), ), ] diff --git a/machines/migrations/0096_auto_20181013_1417.py b/machines/migrations/0096_auto_20181013_1417.py index 745fb523..0017ead9 100644 --- a/machines/migrations/0096_auto_20181013_1417.py +++ b/machines/migrations/0096_auto_20181013_1417.py @@ -9,14 +9,14 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('machines', '0095_auto_20180919_2225'), - ] + dependencies = [("machines", "0095_auto_20180919_2225")] operations = [ migrations.AlterField( - model_name='machine', - name='user', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - ), + model_name="machine", + name="user", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL + ), + ) ] diff --git a/machines/migrations/0097_extension_dnssec.py b/machines/migrations/0097_extension_dnssec.py index 48e41f77..6da514ac 100644 --- a/machines/migrations/0097_extension_dnssec.py +++ b/machines/migrations/0097_extension_dnssec.py @@ -7,14 +7,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0096_auto_20181013_1417'), - ] + dependencies = [("machines", "0096_auto_20181013_1417")] operations = [ migrations.AddField( - model_name='extension', - name='dnssec', - field=models.BooleanField(default=False, help_text='Should the zone be signed with DNSSEC'), - ), + model_name="extension", + name="dnssec", + field=models.BooleanField( + default=False, help_text="Should the zone be signed with DNSSEC" + ), + ) ] diff --git a/machines/migrations/0098_auto_20190102_1745.py b/machines/migrations/0098_auto_20190102_1745.py index e886e8a1..55ec21ea 100644 --- a/machines/migrations/0098_auto_20190102_1745.py +++ b/machines/migrations/0098_auto_20190102_1745.py @@ -7,14 +7,32 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0097_extension_dnssec'), - ] + dependencies = [("machines", "0097_extension_dnssec")] operations = [ migrations.AlterField( - model_name='role', - name='specific_role', - field=models.CharField(blank=True, choices=[('dhcp-server', 'DHCP server'), ('switch-conf-server', 'Switches configuration server'), ('dns-recursif-server', 'Recursive DNS server'), ('dns-recursive-server', 'Recursive DNS server'), ('ntp-server', 'NTP server'), ('radius-server', 'RADIUS server'), ('log-server', 'Log server'), ('ldap-master-server', 'LDAP master server'), ('ldap-backup-server', 'LDAP backup server'), ('smtp-server', 'SMTP server'), ('postgresql-server', 'postgreSQL server'), ('mysql-server', 'mySQL server'), ('sql-client', 'SQL client'), ('gateway', 'Gateway')], max_length=32, null=True), - ), + model_name="role", + name="specific_role", + field=models.CharField( + blank=True, + choices=[ + ("dhcp-server", "DHCP server"), + ("switch-conf-server", "Switches configuration server"), + ("dns-recursif-server", "Recursive DNS server"), + ("dns-recursive-server", "Recursive DNS server"), + ("ntp-server", "NTP server"), + ("radius-server", "RADIUS server"), + ("log-server", "Log server"), + ("ldap-master-server", "LDAP master server"), + ("ldap-backup-server", "LDAP backup server"), + ("smtp-server", "SMTP server"), + ("postgresql-server", "postgreSQL server"), + ("mysql-server", "mySQL server"), + ("sql-client", "SQL client"), + ("gateway", "Gateway"), + ], + max_length=32, + null=True, + ), + ) ] diff --git a/machines/migrations/0099_role_recursive_dns.py b/machines/migrations/0099_role_recursive_dns.py index c1ce3965..bc1b8ab8 100644 --- a/machines/migrations/0099_role_recursive_dns.py +++ b/machines/migrations/0099_role_recursive_dns.py @@ -6,21 +6,15 @@ from django.db import migrations, models def migrate(apps, schema_editor): - Role = apps.get_model('machines', 'Role') + Role = apps.get_model("machines", "Role") - for role in Role.objects.filter(specific_role='dns-recursif-server'): - role.specific_role = 'dns-recursive-server' + for role in Role.objects.filter(specific_role="dns-recursif-server"): + role.specific_role = "dns-recursive-server" role.save() class Migration(migrations.Migration): - dependencies = [ - ('machines', '0098_auto_20190102_1745'), - ] - - operations = [ - migrations.RunPython(migrate), - ] - + dependencies = [("machines", "0098_auto_20190102_1745")] + operations = [migrations.RunPython(migrate)] diff --git a/machines/migrations/0100_auto_20190102_1753.py b/machines/migrations/0100_auto_20190102_1753.py index 35f7b78d..3d3e315f 100644 --- a/machines/migrations/0100_auto_20190102_1753.py +++ b/machines/migrations/0100_auto_20190102_1753.py @@ -7,14 +7,31 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0099_role_recursive_dns'), - ] + dependencies = [("machines", "0099_role_recursive_dns")] operations = [ migrations.AlterField( - model_name='role', - name='specific_role', - field=models.CharField(blank=True, choices=[('dhcp-server', 'DHCP server'), ('switch-conf-server', 'Switches configuration server'), ('dns-recursive-server', 'Recursive DNS server'), ('ntp-server', 'NTP server'), ('radius-server', 'RADIUS server'), ('log-server', 'Log server'), ('ldap-master-server', 'LDAP master server'), ('ldap-backup-server', 'LDAP backup server'), ('smtp-server', 'SMTP server'), ('postgresql-server', 'postgreSQL server'), ('mysql-server', 'mySQL server'), ('sql-client', 'SQL client'), ('gateway', 'Gateway')], max_length=32, null=True), - ), + model_name="role", + name="specific_role", + field=models.CharField( + blank=True, + choices=[ + ("dhcp-server", "DHCP server"), + ("switch-conf-server", "Switches configuration server"), + ("dns-recursive-server", "Recursive DNS server"), + ("ntp-server", "NTP server"), + ("radius-server", "RADIUS server"), + ("log-server", "Log server"), + ("ldap-master-server", "LDAP master server"), + ("ldap-backup-server", "LDAP backup server"), + ("smtp-server", "SMTP server"), + ("postgresql-server", "postgreSQL server"), + ("mysql-server", "mySQL server"), + ("sql-client", "SQL client"), + ("gateway", "Gateway"), + ], + max_length=32, + null=True, + ), + ) ] diff --git a/machines/migrations/0101_auto_20190108_1623.py b/machines/migrations/0101_auto_20190108_1623.py index 856721ac..009e11b4 100644 --- a/machines/migrations/0101_auto_20190108_1623.py +++ b/machines/migrations/0101_auto_20190108_1623.py @@ -7,28 +7,37 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('machines', '0100_auto_20190102_1753'), - ] + dependencies = [("machines", "0100_auto_20190102_1753")] operations = [ migrations.AlterModelOptions( - name='ouvertureport', - options={'verbose_name': 'ports opening', 'verbose_name_plural': 'ports openings'}, + name="ouvertureport", + options={ + "verbose_name": "ports opening", + "verbose_name_plural": "ports openings", + }, ), migrations.AlterField( - model_name='nas', - name='port_access_mode', - field=models.CharField(choices=[('802.1X', '802.1X'), ('Mac-address', 'MAC-address')], default='802.1X', max_length=32), + model_name="nas", + name="port_access_mode", + field=models.CharField( + choices=[("802.1X", "802.1X"), ("Mac-address", "MAC-address")], + default="802.1X", + max_length=32, + ), ), migrations.AlterField( - model_name='vlan', - name='igmp', - field=models.BooleanField(default=False, help_text='v4 multicast management'), + model_name="vlan", + name="igmp", + field=models.BooleanField( + default=False, help_text="v4 multicast management" + ), ), migrations.AlterField( - model_name='vlan', - name='mld', - field=models.BooleanField(default=False, help_text='v6 multicast management'), + model_name="vlan", + name="mld", + field=models.BooleanField( + default=False, help_text="v6 multicast management" + ), ), ] diff --git a/machines/migrations/0102_auto_20190303_1611.py b/machines/migrations/0102_auto_20190303_1611.py new file mode 100644 index 00000000..29f97700 --- /dev/null +++ b/machines/migrations/0102_auto_20190303_1611.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2019-03-03 15:11 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [("machines", "0101_auto_20190108_1623")] + + operations = [ + migrations.RenameField( + model_name="interface", old_name="type", new_name="machine_type" + ), + migrations.RenameField(model_name="iptype", old_name="type", new_name="name"), + migrations.RenameField( + model_name="machinetype", old_name="type", new_name="name" + ), + ] diff --git a/machines/migrations/0103_auto_20191002_2222.py b/machines/migrations/0103_auto_20191002_2222.py new file mode 100644 index 00000000..3c74721b --- /dev/null +++ b/machines/migrations/0103_auto_20191002_2222.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.23 on 2019-10-02 20:22 +from __future__ import unicode_literals + +from django.db import migrations, models +import machines.models + + +class Migration(migrations.Migration): + + dependencies = [ + ("machines", "0102_auto_20190303_1611"), + ("preferences", "0066_optionalmachine_default_dns_ttl"), + ] + + operations = [ + migrations.AddField( + model_name="domain", + name="ttl", + field=models.PositiveIntegerField( + default=0, verbose_name="Time To Live (TTL)" + ), + ), + migrations.AddField( + model_name="mx", + name="ttl", + field=models.PositiveIntegerField( + default=172800, verbose_name="Time To Live (TTL)" + ), + ), + migrations.AddField( + model_name="ns", + name="ttl", + field=models.PositiveIntegerField( + default=172800, verbose_name="Time To Live (TTL)" + ), + ), + migrations.AddField( + model_name="txt", + name="ttl", + field=models.PositiveIntegerField( + default=172800, verbose_name="Time To Live (TTL)" + ), + ), + ] diff --git a/machines/migrations/0104_auto_20191002_2231.py b/machines/migrations/0104_auto_20191002_2231.py new file mode 100644 index 00000000..3f2c22e2 --- /dev/null +++ b/machines/migrations/0104_auto_20191002_2231.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.23 on 2019-10-02 20:31 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [("machines", "0103_auto_20191002_2222")] + + operations = [ + migrations.AlterModelOptions( + name="domain", + options={ + "permissions": ( + ("view_domain", "Can view a domain object"), + ("change_ttl", "Can change TTL of a domain object"), + ), + "verbose_name": "domain", + "verbose_name_plural": "domains", + }, + ) + ] diff --git a/machines/migrations/0105_dname_ttl.py b/machines/migrations/0105_dname_ttl.py new file mode 100644 index 00000000..6cdca097 --- /dev/null +++ b/machines/migrations/0105_dname_ttl.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.23 on 2019-10-02 21:47 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [("machines", "0104_auto_20191002_2231")] + + operations = [ + migrations.AddField( + model_name="dname", + name="ttl", + field=models.PositiveIntegerField( + default=172800, verbose_name="Time To Live (TTL)" + ), + ) + ] diff --git a/machines/migrations/0106_auto_20191120_0159.py b/machines/migrations/0106_auto_20191120_0159.py new file mode 100644 index 00000000..5526a818 --- /dev/null +++ b/machines/migrations/0106_auto_20191120_0159.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.23 on 2019-11-20 00:59 +from __future__ import unicode_literals + +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0105_dname_ttl'), + ] + + operations = [ + migrations.AlterModelOptions( + name='domain', + options={'permissions': (('view_domain', 'Can view a domain object'), ('change_ttl', 'Can change the TTL of a domain object')), 'verbose_name': 'domain', 'verbose_name_plural': 'domains'}, + ), + migrations.AlterField( + model_name='extension', + name='dnssec', + field=models.BooleanField(default=False, help_text='Should the zone be signed with DNSSEC.'), + ), + migrations.AlterField( + model_name='extension', + name='name', + field=models.CharField(help_text='Zone name, must begin with a dot (.example.org).', max_length=255, unique=True), + ), + migrations.AlterField( + model_name='extension', + name='origin', + field=models.ForeignKey(blank=True, help_text='A record associated with the zone.', null=True, on_delete=django.db.models.deletion.PROTECT, to='machines.IpList'), + ), + migrations.AlterField( + model_name='extension', + name='origin_v6', + field=models.GenericIPAddressField(blank=True, help_text='AAAA record associated with the zone.', null=True, protocol='IPv6'), + ), + migrations.AlterField( + model_name='iptype', + name='domaine_ip_netmask', + field=models.IntegerField(default=24, help_text="Netmask for the domain's IPv4 range.", validators=[django.core.validators.MaxValueValidator(31), django.core.validators.MinValueValidator(8)]), + ), + migrations.AlterField( + model_name='iptype', + name='domaine_ip_network', + field=models.GenericIPAddressField(blank=True, help_text="Network containing the domain's IPv4 range (optional).", null=True, protocol='IPv4'), + ), + migrations.AlterField( + model_name='iptype', + name='reverse_v4', + field=models.BooleanField(default=False, help_text='Enable reverse DNS for IPv4.'), + ), + migrations.AlterField( + model_name='iptype', + name='reverse_v6', + field=models.BooleanField(default=False, help_text='Enable reverse DNS for IPv6.'), + ), + migrations.AlterField( + model_name='machine', + name='name', + field=models.CharField(blank=True, help_text='Optional.', max_length=255, null=True), + ), + migrations.AlterField( + model_name='soa', + name='expire', + field=models.PositiveIntegerField(default=3600000, help_text='Seconds before the secondary DNS stop answering requests in case of primary DNS timeout.'), + ), + migrations.AlterField( + model_name='soa', + name='mail', + field=models.EmailField(help_text='Contact email address for the zone.', max_length=254), + ), + migrations.AlterField( + model_name='soa', + name='refresh', + field=models.PositiveIntegerField(default=86400, help_text='Seconds before the secondary DNS have to ask the primary DNS serial to detect a modification.'), + ), + migrations.AlterField( + model_name='soa', + name='retry', + field=models.PositiveIntegerField(default=7200, help_text='Seconds before the secondary DNS ask the serial again in case of a primary DNS timeout.'), + ), + migrations.AlterField( + model_name='soa', + name='ttl', + field=models.PositiveIntegerField(default=172800, help_text='Time To Live.'), + ), + migrations.AlterField( + model_name='srv', + name='port', + field=models.PositiveIntegerField(help_text='TCP/UDP port.', validators=[django.core.validators.MaxValueValidator(65535)]), + ), + migrations.AlterField( + model_name='srv', + name='priority', + field=models.PositiveIntegerField(default=0, help_text='Priority of the target server (positive integer value, the lower it is, the more the server will be used if available).', validators=[django.core.validators.MaxValueValidator(65535)]), + ), + migrations.AlterField( + model_name='srv', + name='target', + field=models.ForeignKey(help_text='Target server.', on_delete=django.db.models.deletion.PROTECT, to='machines.Domain'), + ), + migrations.AlterField( + model_name='srv', + name='ttl', + field=models.PositiveIntegerField(default=172800, help_text='Time To Live.'), + ), + migrations.AlterField( + model_name='srv', + name='weight', + field=models.PositiveIntegerField(default=0, help_text='Relative weight for records with the same priority (integer value between 0 and 65535).', validators=[django.core.validators.MaxValueValidator(65535)]), + ), + migrations.AlterField( + model_name='sshfp', + name='comment', + field=models.CharField(blank=True, help_text='Comment.', max_length=255, null=True), + ), + migrations.AlterField( + model_name='sshfp', + name='pub_key_entry', + field=models.TextField(help_text='SSH public key.', max_length=2048), + ), + migrations.AlterField( + model_name='vlan', + name='igmp', + field=models.BooleanField(default=False, help_text='v4 multicast management.'), + ), + migrations.AlterField( + model_name='vlan', + name='mld', + field=models.BooleanField(default=False, help_text='v6 multicast management.'), + ), + ] diff --git a/machines/migrations/__init__.py b/machines/migrations/__init__.py index 20abb0d2..b409e525 100644 --- a/machines/migrations/__init__.py +++ b/machines/migrations/__init__.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -19,4 +19,3 @@ # 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. - diff --git a/machines/models.py b/machines/models.py index cdc6d830..c5a95863 100644 --- a/machines/models.py +++ b/machines/models.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2016-2018 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # Copyright © 2018 Charlie Jacomme # @@ -41,10 +41,20 @@ from django.db.models.signals import post_save, post_delete from django.dispatch import receiver from django.forms import ValidationError from django.utils import timezone +from django.db import transaction +from reversion import revisions as reversion from django.utils.functional import cached_property from django.utils.translation import ugettext_lazy as _ from macaddress.fields import MACAddressField, default_dialect -from netaddr import mac_bare, EUI, IPSet, IPRange, IPNetwork, IPAddress +from netaddr import ( + mac_bare, + EUI, + NotRegisteredError, + IPSet, + IPRange, + IPNetwork, + IPAddress, +) import preferences.models import users.models @@ -55,20 +65,17 @@ from re2o.mixins import AclMixin, RevMixin class Machine(RevMixin, FieldPermissionModelMixin, models.Model): """ Class définissant une machine, object parent user, objets fils interfaces""" - user = models.ForeignKey('users.User', on_delete=models.CASCADE) + + user = models.ForeignKey("users.User", on_delete=models.CASCADE) name = models.CharField( - max_length=255, - help_text=_("Optional"), - blank=True, - null=True + max_length=255, help_text=_("Optional."), blank=True, null=True ) active = models.BooleanField(default=True) class Meta: permissions = ( ("view_machine", _("Can view a machine object")), - ("change_machine_user", - _("Can change the user of a machine")), + ("change_machine_user", _("Can change the user of a machine")), ) verbose_name = _("machine") verbose_name_plural = _("machines") @@ -86,9 +93,7 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): Usefull in history display""" return chain( self.interface_set.all(), - Domain.objects.filter( - interface_parent__in=self.interface_set.all() - ) + Domain.objects.filter(interface_parent__in=self.interface_set.all()), ) @staticmethod @@ -103,8 +108,14 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): A tuple with a boolean stating if edition is allowed and an explanation message. """ - return (user_request.has_perm('machines.change_machine_user'), - _("You don't have the right to change the machine's user.")) + can = user_request.has_perm("machines.change_machine_user") + return ( + can, + _("You don't have the right to change the machine's user.") + if not can + else None, + ("machines.change_machine_user",), + ) @staticmethod def can_view_all(user_request, *_args, **_kwargs): @@ -112,10 +123,13 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): droit particulier correspondant :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" - if not user_request.has_perm('machines.view_machine'): - return False, _("You don't have the right to view all the" - " machines.") - return True, None + if not user_request.has_perm("machines.view_machine"): + return ( + False, + _("You don't have the right to view all the machines."), + ("machines.view_machine",), + ) + return True, None, None @staticmethod def can_create(user_request, userid, *_args, **_kwargs): @@ -127,23 +141,37 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): try: user = users.models.User.objects.get(pk=userid) except users.models.User.DoesNotExist: - return False, _("Nonexistent user.") - max_lambdauser_interfaces = (preferences.models.OptionalMachine - .get_cached_value( - 'max_lambdauser_interfaces' - )) - if not user_request.has_perm('machines.add_machine'): - if not (preferences.models.OptionalMachine - .get_cached_value('create_machine')): - return False, (_("You don't have the right to add a machine.")) + return False, _("Nonexistent user."), None + max_lambdauser_interfaces = preferences.models.OptionalMachine.get_cached_value( + "max_lambdauser_interfaces" + ) + if not user_request.has_perm("machines.add_machine"): + if not ( + preferences.models.OptionalMachine.get_cached_value("create_machine") + ): + return ( + False, + _("You don't have the right to add a machine."), + ("machines.add_machine",), + ) if user != user_request: - return False, (_("You don't have the right to add a machine" - " to another user.")) + return ( + False, + _("You don't have the right to add a machine to another" + " user."), + ("machines.add_machine",), + ) if user.user_interfaces().count() >= max_lambdauser_interfaces: - return False, (_("You reached the maximum number of interfaces" - " that you are allowed to create yourself" - " (%s)." % max_lambdauser_interfaces)) - return True, None + return ( + False, + _( + "You reached the maximum number of interfaces" + " that you are allowed to create yourself" + " (%s)." % max_lambdauser_interfaces + ), + None, + ) + return True, None, None def can_edit(self, user_request, *args, **kwargs): """Vérifie qu'on peut bien éditer cette instance particulière (soit @@ -152,16 +180,17 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): :param user_request: instance user qui fait l'edition :return: True ou False avec la raison le cas échéant""" if self.user != user_request: - if (not user_request.has_perm('machines.change_interface') or - not self.user.can_edit( - self.user, - user_request, - *args, - **kwargs - )[0]): - return False, (_("You don't have the right to edit a machine" - " of another user.")) - return True, None + can_user, _message, permissions = self.user.can_edit( + self.user, user_request, *args, **kwargs + ) + if not (user_request.has_perm("machines.change_interface") and can_user): + return ( + False, + _("You don't have the right to edit a machine of another" + " user."), + ("machines.change_interface",) + permissions, + ) + return True, None, None def can_delete(self, user_request, *args, **kwargs): """Vérifie qu'on peut bien supprimer cette instance particulière (soit @@ -170,16 +199,19 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" if self.user != user_request: - if (not user_request.has_perm('machines.change_interface') or - not self.user.can_edit( - self.user, - user_request, - *args, - **kwargs - )[0]): - return False, _("You don't have the right to delete a machine" - " of another user.") - return True, None + can_user, _message, permissions = self.user.can_edit( + self.user, user_request, *args, **kwargs + ) + if not (user_request.has_perm("machines.change_interface") and can_user): + return ( + False, + _( + "You don't have the right to delete a machine" + " of another user." + ), + ("machines.change_interface",) + permissions, + ) + return True, None, None def can_view(self, user_request, *_args, **_kwargs): """Vérifie qu'on peut bien voir cette instance particulière (soit @@ -187,11 +219,17 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): :param self: instance machine à éditer :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" - if (not user_request.has_perm('machines.view_machine') and - self.user != user_request): - return False, _("You don't have the right to view other machines" - " than yours.") - return True, None + if ( + not user_request.has_perm("machines.view_machine") + and self.user != user_request + ): + return ( + False, + _("You don't have the right to view other machines than" + " yours."), + ("machines.view_machine",), + ) + return True, None, None @cached_property def short_name(self): @@ -213,40 +251,67 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): def all_short_names(self): """Renvoie de manière unique, le nom des interfaces de cette machine""" - return Domain.objects.filter( - interface_parent__machine=self - ).values_list('name', flat=True).distinct() + return ( + Domain.objects.filter(interface_parent__machine=self) + .values_list("name", flat=True) + .distinct() + ) @cached_property def get_name(self): """Return a name : user provided name or first interface name""" return self.name or self.short_name + @classmethod + def mass_delete(cls, machine_queryset): + """Mass delete for machine queryset""" + from topologie.models import AccessPoint + + Domain.objects.filter( + cname__interface_parent__machine__in=machine_queryset + )._raw_delete(machine_queryset.db) + Domain.objects.filter( + interface_parent__machine__in=machine_queryset + )._raw_delete(machine_queryset.db) + Ipv6List.objects.filter(interface__machine__in=machine_queryset)._raw_delete( + machine_queryset.db + ) + Interface.objects.filter(machine__in=machine_queryset).filter( + port_lists__isnull=False + ).delete() + Interface.objects.filter(machine__in=machine_queryset)._raw_delete( + machine_queryset.db + ) + AccessPoint.objects.filter(machine_ptr__in=machine_queryset)._raw_delete( + machine_queryset.db + ) + machine_queryset._raw_delete(machine_queryset.db) + @cached_property def all_complete_names(self): """Renvoie tous les tls complets de la machine""" - return [str(domain) for domain in Domain.objects.filter( - Q(cname__interface_parent__machine=self) | Q(interface_parent__machine=self) - )] + return [ + str(domain) + for domain in Domain.objects.filter( + Q(cname__interface_parent__machine=self) + | Q(interface_parent__machine=self) + ) + ] def __init__(self, *args, **kwargs): super(Machine, self).__init__(*args, **kwargs) - self.field_permissions = { - 'user': self.can_change_user, - } + self.field_permissions = {"user": self.can_change_user} def __str__(self): - return str(self.user) + ' - ' + str(self.id) + ' - ' + str(self.name) + return str(self.user) + " - " + str(self.id) + " - " + str(self.name) class MachineType(RevMixin, AclMixin, models.Model): """ Type de machine, relié à un type d'ip, affecté aux interfaces""" - type = models.CharField(max_length=255) + + name = models.CharField(max_length=255) ip_type = models.ForeignKey( - 'IpType', - on_delete=models.PROTECT, - blank=True, - null=True + "IpType", on_delete=models.PROTECT, blank=True, null=True ) class Meta: @@ -260,7 +325,7 @@ class MachineType(RevMixin, AclMixin, models.Model): def all_interfaces(self): """ Renvoie toutes les interfaces (cartes réseaux) de type machinetype""" - return Interface.objects.filter(type=self) + return Interface.objects.filter(machine_type=self) @staticmethod def can_use_all(user_request, *_args, **_kwargs): @@ -272,67 +337,49 @@ class MachineType(RevMixin, AclMixin, models.Model): A tuple with a boolean stating if user can acces and an explanation message is acces is not allowed. """ - if not user_request.has_perm('machines.use_all_machinetype'): - return False, (_("You don't have the right to use all machine" - " types.")) - return True, None + if not user_request.has_perm("machines.use_all_machinetype"): + return ( + False, + _("You don't have the right to use all machine types."), + ("machines.use_all_machinetype",), + ) + return True, None, None def __str__(self): - return self.type + return self.name class IpType(RevMixin, AclMixin, models.Model): """ Type d'ip, définissant un range d'ip, affecté aux machine types""" - type = models.CharField(max_length=255) - extension = models.ForeignKey('Extension', on_delete=models.PROTECT) + + name = models.CharField(max_length=255) + extension = models.ForeignKey("Extension", on_delete=models.PROTECT) need_infra = models.BooleanField(default=False) - domaine_ip_start = models.GenericIPAddressField(protocol='IPv4') - domaine_ip_stop = models.GenericIPAddressField(protocol='IPv4') + domaine_ip_start = models.GenericIPAddressField(protocol="IPv4") + domaine_ip_stop = models.GenericIPAddressField(protocol="IPv4") domaine_ip_network = models.GenericIPAddressField( - protocol='IPv4', + protocol="IPv4", null=True, blank=True, - help_text=_("Network containing the domain's IPv4 range (optional)") + help_text=_("Network containing the domain's IPv4 range (optional)."), ) domaine_ip_netmask = models.IntegerField( default=24, - validators=[ - MaxValueValidator(31), - MinValueValidator(8) - ], - help_text=_("Netmask for the domain's IPv4 range") + validators=[MaxValueValidator(31), MinValueValidator(8)], + help_text=_("Netmask for the domain's IPv4 range."), ) reverse_v4 = models.BooleanField( - default=False, - help_text=_("Enable reverse DNS for IPv4"), - ) - prefix_v6 = models.GenericIPAddressField( - protocol='IPv6', - null=True, - blank=True + default=False, help_text=_("Enable reverse DNS for IPv4.") ) + prefix_v6 = models.GenericIPAddressField(protocol="IPv6", null=True, blank=True) prefix_v6_length = models.IntegerField( - default=64, - validators=[ - MaxValueValidator(128), - MinValueValidator(0) - ] + default=64, validators=[MaxValueValidator(128), MinValueValidator(0)] ) reverse_v6 = models.BooleanField( - default=False, - help_text=_("Enable reverse DNS for IPv6"), - ) - vlan = models.ForeignKey( - 'Vlan', - on_delete=models.PROTECT, - blank=True, - null=True - ) - ouverture_ports = models.ForeignKey( - 'OuverturePortList', - blank=True, - null=True + default=False, help_text=_("Enable reverse DNS for IPv6.") ) + vlan = models.ForeignKey("Vlan", on_delete=models.PROTECT, blank=True, null=True) + ouverture_ports = models.ForeignKey("OuverturePortList", blank=True, null=True) class Meta: permissions = ( @@ -367,24 +414,25 @@ class IpType(RevMixin, AclMixin, models.Model): """Iter sur les range cidr, et renvoie network, broacast , etc""" return [ { - 'network': str(ip_set.network), - 'netmask': str(ip_set.netmask), - 'netmask_cidr': str(ip_set.prefixlen), - 'broadcast': str(ip_set.broadcast), - 'vlan': str(self.vlan), - 'vlan_id': self.vlan.vlan_id - } for ip_set in self.ip_set.iter_cidrs() + "network": str(ip_set.network), + "netmask": str(ip_set.netmask), + "netmask_cidr": str(ip_set.prefixlen), + "broadcast": str(ip_set.broadcast), + "vlan": str(self.vlan), + "vlan_id": self.vlan.vlan_id, + } + for ip_set in self.ip_set.iter_cidrs() ] @cached_property def ip6_set_full_info(self): if self.prefix_v6: return { - 'network': str(self.prefix_v6), - 'netmask': 'ffff:ffff:ffff:ffff::', - 'netmask_cidr': str(self.prefix_v6_length), - 'vlan': str(self.vlan), - 'vlan_id': self.vlan.vlan_id + "network": str(self.prefix_v6), + "netmask": "ffff:ffff:ffff:ffff::", + "netmask_cidr": str(self.prefix_v6_length), + "vlan": str(self.vlan), + "vlan_id": self.vlan.vlan_id, } else: return None @@ -394,18 +442,25 @@ class IpType(RevMixin, AclMixin, models.Model): """Renvoie le network parent du range start-stop, si spécifié Différent de ip_set_cidrs ou iP_set, car lui est supérieur ou égal""" if self.domaine_ip_network: - return IPNetwork(str(self.domaine_ip_network) + '/' + str(self.domaine_ip_netmask)) + return IPNetwork( + str(self.domaine_ip_network) + "/" + str(self.domaine_ip_netmask) + ) return None @cached_property def ip_net_full_info(self): """Renvoie les infos du network contenant du range""" - return { - 'network': str(self.ip_network.network), - 'netmask': str(self.ip_network.netmask), - 'broadcast': str(self.ip_network.broadcast), - 'netmask_cidr': str(self.ip_network.prefixlen), - } + if self.ip_network: + return { + "network": str(self.ip_network.network), + "netmask": str(self.ip_network.netmask), + "broadcast": str(self.ip_network.broadcast), + "netmask_cidr": str(self.ip_network.prefixlen), + "vlan": str(self.vlan), + "vlan_id": self.vlan.vlan_id, + } + else: + return None @cached_property def complete_prefixv6(self): @@ -418,9 +473,7 @@ class IpType(RevMixin, AclMixin, models.Model): def free_ip(self): """ Renvoie toutes les ip libres associées au type donné (self)""" - return IpList.objects.filter( - interface__isnull=True - ).filter(ip_type=self) + return IpList.objects.filter(interface__isnull=True).filter(ip_type=self) def gen_ip_range(self): """ Cree les IpList associées au type self. Parcours pédestrement et @@ -428,9 +481,7 @@ class IpType(RevMixin, AclMixin, models.Model): associé à l'ip""" # Creation du range d'ip dans les objets iplist ip_obj = [IpList(ip_type=self, ipv4=str(ip)) for ip in self.ip_range] - listes_ip = IpList.objects.filter( - ipv4__in=[str(ip) for ip in self.ip_range] - ) + listes_ip = IpList.objects.filter(ipv4__in=[str(ip) for ip in self.ip_range]) # Si il n'y a pas d'ip, on les crée if not listes_ip: IpList.objects.bulk_create(ip_obj) @@ -443,9 +494,13 @@ class IpType(RevMixin, AclMixin, models.Model): """ Methode dépréciée, IpList est en mode cascade et supprimé automatiquement""" if Interface.objects.filter(ipv4__in=self.ip_objects()): - raise ValidationError(_("One or several IP addresses from the" - " range are affected, impossible to delete" - " the range.")) + raise ValidationError( + _( + "One or several IP addresses from the" + " range are affected, impossible to delete" + " the range." + ) + ) for ip in self.ip_objects(): ip.delete() @@ -455,26 +510,29 @@ class IpType(RevMixin, AclMixin, models.Model): return else: for ipv6 in Ipv6List.objects.filter( - interface__in=Interface.objects.filter( - type__in=MachineType.objects.filter(ip_type=self) - ) + interface__in=Interface.objects.filter( + machine_type__in=MachineType.objects.filter(ip_type=self) + ) ): ipv6.check_and_replace_prefix(prefix=self.prefix_v6) def get_associated_ptr_records(self): from re2o.utils import all_active_assigned_interfaces + if self.reverse_v4: - return (all_active_assigned_interfaces() - .filter(type__ip_type=self) - .filter(ipv4__isnull=False)) + return ( + all_active_assigned_interfaces() + .filter(machine_type__ip_type=self) + .filter(ipv4__isnull=False) + ) else: return None def get_associated_ptr_v6_records(self): from re2o.utils import all_active_interfaces + if self.reverse_v6: - return (all_active_interfaces(full=True) - .filter(type__ip_type=self)) + return all_active_interfaces(full=True).filter(machine_type__ip_type=self) else: return None @@ -488,22 +546,35 @@ class IpType(RevMixin, AclMixin, models.Model): raise ValidationError(_("Range end must be after range start...")) # On ne crée pas plus grand qu'un /16 if self.ip_range.size > 65536: - raise ValidationError(_("The range is too large, you can't create" - " a larger one than a /16.")) + raise ValidationError( + _( + "The range is too large, you can't create" + " a larger one than a /16." + ) + ) # On check que les / ne se recoupent pas for element in IpType.objects.all().exclude(pk=self.pk): if not self.ip_set.isdisjoint(element.ip_set): - raise ValidationError(_("The specified range is not disjoint" - " from existing ranges.")) + raise ValidationError( + _("The specified range is not disjoint from existing" + " ranges.") + ) # On formate le prefix v6 if self.prefix_v6: - self.prefix_v6 = str(IPNetwork(self.prefix_v6 + '/64').network) + self.prefix_v6 = str(IPNetwork(self.prefix_v6 + "/64").network) # On vérifie qu'un domaine network/netmask contiens bien le domaine ip start-stop if self.domaine_ip_network: - if not self.domaine_ip_start in self.ip_network or not self.domaine_ip_stop in self.ip_network: - raise ValidationError(_("If you specify a domain network or" - " netmask, it must contain the" - " domain's IP range.")) + if ( + not self.domaine_ip_start in self.ip_network + or not self.domaine_ip_stop in self.ip_network + ): + raise ValidationError( + _( + "If you specify a domain network or" + " netmask, it must contain the" + " domain's IP range." + ) + ) return def save(self, *args, **kwargs): @@ -516,35 +587,32 @@ class IpType(RevMixin, AclMixin, models.Model): restrictions :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" - return user_request.has_perm('machines.use_all_iptype'), None + return ( + user_request.has_perm("machines.use_all_iptype"), + None, + ("machines.use_all_iptype",), + ) def __str__(self): - return self.type + return self.name class Vlan(RevMixin, AclMixin, models.Model): """ Un vlan : vlan_id et nom On limite le vlan id entre 0 et 4096, comme défini par la norme""" + vlan_id = models.PositiveIntegerField(validators=[MaxValueValidator(4095)]) name = models.CharField(max_length=256) comment = models.CharField(max_length=256, blank=True) - #Réglages supplémentaires + # Réglages supplémentaires arp_protect = models.BooleanField(default=False) dhcp_snooping = models.BooleanField(default=False) dhcpv6_snooping = models.BooleanField(default=False) - igmp = models.BooleanField( - default=False, - help_text=_("v4 multicast management") - ) - mld = models.BooleanField( - default=False, - help_text=_("v6 multicast management") - ) + igmp = models.BooleanField(default=False, help_text=_("v4 multicast management.")) + mld = models.BooleanField(default=False, help_text=_("v6 multicast management.")) class Meta: - permissions = ( - ("view_vlan", _("Can view a VLAN object")), - ) + permissions = (("view_vlan", _("Can view a VLAN object")),) verbose_name = _("VLAN") verbose_name_plural = _("VLANs") @@ -556,34 +624,24 @@ class Nas(RevMixin, AclMixin, models.Model): """ Les nas. Associé à un machine_type. Permet aussi de régler le port_access_mode (802.1X ou mac-address) pour le radius. Champ autocapture de la mac à true ou false""" - default_mode = '802.1X' - AUTH = ( - ('802.1X', '802.1X'), - ('Mac-address', _("MAC-address")), - ) + + default_mode = "802.1X" + AUTH = (("802.1X", "802.1X"), ("Mac-address", _("MAC-address"))) name = models.CharField(max_length=255, unique=True) nas_type = models.ForeignKey( - 'MachineType', - on_delete=models.PROTECT, - related_name='nas_type' + "MachineType", on_delete=models.PROTECT, related_name="nas_type" ) machine_type = models.ForeignKey( - 'MachineType', - on_delete=models.PROTECT, - related_name='machinetype_on_nas' + "MachineType", on_delete=models.PROTECT, related_name="machinetype_on_nas" ) port_access_mode = models.CharField( - choices=AUTH, - default=default_mode, - max_length=32 + choices=AUTH, default=default_mode, max_length=32 ) autocapture_mac = models.BooleanField(default=False) class Meta: - permissions = ( - ("view_nas", _("Can view a NAS device object")), - ) + permissions = (("view_nas", _("Can view a NAS device object")),) verbose_name = _("NAS device") verbose_name_plural = _("NAS devices") @@ -597,34 +655,36 @@ class SOA(RevMixin, AclMixin, models.Model): Les valeurs par défault viennent des recommandations RIPE : https://www.ripe.net/publications/docs/ripe-203 """ + name = models.CharField(max_length=255) - mail = models.EmailField( - help_text=_("Contact email address for the zone") - ) + mail = models.EmailField(help_text=_("Contact email address for the zone.")) refresh = models.PositiveIntegerField( default=86400, # 24 hours - help_text=_("Seconds before the secondary DNS have to ask the primary" - " DNS serial to detect a modification") + help_text=_( + "Seconds before the secondary DNS have to ask the primary" + " DNS serial to detect a modification." + ), ) retry = models.PositiveIntegerField( default=7200, # 2 hours - help_text=_("Seconds before the secondary DNS ask the serial again in" - " case of a primary DNS timeout") + help_text=_( + "Seconds before the secondary DNS ask the serial again in" + " case of a primary DNS timeout." + ), ) expire = models.PositiveIntegerField( default=3600000, # 1000 hours - help_text=_("Seconds before the secondary DNS stop answering requests" - " in case of primary DNS timeout") + help_text=_( + "Seconds before the secondary DNS stop answering requests" + " in case of primary DNS timeout." + ), ) ttl = models.PositiveIntegerField( - default=172800, # 2 days - help_text=_("Time to Live") + default=172800, help_text=_("Time To Live.") # 2 days ) class Meta: - permissions = ( - ("view_soa", _("Can view an SOA record object")), - ) + permissions = (("view_soa", _("Can view an SOA record object")),) verbose_name = _("SOA record") verbose_name_plural = _("SOA records") @@ -641,22 +701,22 @@ class SOA(RevMixin, AclMixin, models.Model): ; TTL """ return ( - ' {refresh}; refresh\n' - ' {retry}; retry\n' - ' {expire}; expire\n' - ' {ttl}; TTL' + " {refresh}; refresh\n" + " {retry}; retry\n" + " {expire}; expire\n" + " {ttl}; TTL" ).format( refresh=str(self.refresh).ljust(12), retry=str(self.retry).ljust(12), expire=str(self.expire).ljust(12), - ttl=str(self.ttl).ljust(12) + ttl=str(self.ttl).ljust(12), ) @cached_property def dns_soa_mail(self): """ Renvoie le mail dans l'enregistrement SOA """ - mail_fields = str(self.mail).split('@') - return mail_fields[0].replace('.', '\\.') + '.' + mail_fields[1] + '.' + mail_fields = str(self.mail).split("@") + return mail_fields[0].replace(".", "\\.") + "." + mail_fields[1] + "." @classmethod def new_default_soa(cls): @@ -665,40 +725,36 @@ class SOA(RevMixin, AclMixin, models.Model): /!\ Ne jamais supprimer ou renommer cette fonction car elle est utilisée dans les migrations de la BDD. """ return cls.objects.get_or_create( - name=_("SOA to edit"), - mail="postmaster@example.com" + name=_("SOA to edit"), mail="postmaster@example.com" )[0].pk class Extension(RevMixin, AclMixin, models.Model): """ Extension dns type example.org. Précise si tout le monde peut l'utiliser, associé à un origin (ip d'origine)""" + name = models.CharField( max_length=255, unique=True, - help_text=_("Zone name, must begin with a dot (.example.org)") + help_text=_("Zone name, must begin with a dot (.example.org)."), ) need_infra = models.BooleanField(default=False) origin = models.ForeignKey( - 'IpList', + "IpList", on_delete=models.PROTECT, blank=True, null=True, - help_text=_("A record associated with the zone") + help_text=_("A record associated with the zone."), ) origin_v6 = models.GenericIPAddressField( - protocol='IPv6', + protocol="IPv6", null=True, blank=True, - help_text=_("AAAA record associated with the zone") - ) - soa = models.ForeignKey( - 'SOA', - on_delete=models.CASCADE + help_text=_("AAAA record associated with the zone."), ) + soa = models.ForeignKey("SOA", on_delete=models.CASCADE) dnssec = models.BooleanField( - default=False, - help_text=_("Should the zone be signed with DNSSEC") + default=False, help_text=_("Should the zone be signed with DNSSEC.") ) class Meta: @@ -723,30 +779,40 @@ class Extension(RevMixin, AclMixin, models.Model): def get_associated_sshfp_records(self): from re2o.utils import all_active_assigned_interfaces - return (all_active_assigned_interfaces() - .filter(type__ip_type__extension=self) - .filter(machine__id__in=SshFp.objects.values('machine'))) + + return ( + all_active_assigned_interfaces() + .filter(machine_type__ip_type__extension=self) + .filter(machine__id__in=SshFp.objects.values("machine")) + ) def get_associated_a_records(self): from re2o.utils import all_active_assigned_interfaces - return (all_active_assigned_interfaces() - .filter(type__ip_type__extension=self) - .filter(ipv4__isnull=False)) + + return ( + all_active_assigned_interfaces() + .filter(machine_type__ip_type__extension=self) + .filter(ipv4__isnull=False) + ) def get_associated_aaaa_records(self): from re2o.utils import all_active_interfaces - return (all_active_interfaces(full=True) - .filter(type__ip_type__extension=self)) + + return all_active_interfaces(full=True).filter( + machine_type__ip_type__extension=self + ) def get_associated_cname_records(self): from re2o.utils import all_active_assigned_interfaces - return (Domain.objects - .filter(extension=self) - .filter(cname__interface_parent__in=all_active_assigned_interfaces()) - .prefetch_related('cname')) + + return ( + Domain.objects.filter(extension=self) + .filter(cname__interface_parent__in=all_active_assigned_interfaces()) + .prefetch_related("cname") + ) def get_associated_dname_records(self): - return (DName.objects.filter(alias=self)) + return DName.objects.filter(alias=self) @staticmethod def can_use_all(user_request, *_args, **_kwargs): @@ -754,13 +820,18 @@ class Extension(RevMixin, AclMixin, models.Model): restrictions :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" - return user_request.has_perm('machines.use_all_extension'), None + can = user_request.has_perm("machines.use_all_extension") + return ( + can, + _("You don't have the right to use all extensions.") if not can else None, + ("machines.use_all_extension",), + ) def __str__(self): return self.name def clean(self, *args, **kwargs): - if self.name and self.name[0] != '.': + if self.name and self.name[0] != ".": raise ValidationError(_("An extension must begin with a dot.")) super(Extension, self).clean(*args, **kwargs) @@ -769,14 +840,16 @@ class Mx(RevMixin, AclMixin, models.Model): """ Entrées des MX. Enregistre la zone (extension) associée et la priorité Todo : pouvoir associer un MX à une interface """ - zone = models.ForeignKey('Extension', on_delete=models.PROTECT) + + zone = models.ForeignKey("Extension", on_delete=models.PROTECT) priority = models.PositiveIntegerField() - name = models.ForeignKey('Domain', on_delete=models.PROTECT) + name = models.ForeignKey("Domain", on_delete=models.PROTECT) + ttl = models.PositiveIntegerField( + verbose_name=_("Time To Live (TTL)"), default=172800 # 2 days + ) class Meta: - permissions = ( - ("view_mx", _("Can view an MX record object")), - ) + permissions = (("view_mx", _("Can view an MX record object")),) verbose_name = _("MX record") verbose_name_plural = _("MX records") @@ -785,23 +858,24 @@ class Mx(RevMixin, AclMixin, models.Model): """Renvoie l'entrée DNS complète pour un MX à mettre dans les fichiers de zones""" return "@ IN MX {prior} {name}".format( - prior=str(self.priority).ljust(3), - name=str(self.name) + prior=str(self.priority).ljust(3), name=str(self.name) ) def __str__(self): - return str(self.zone) + ' ' + str(self.priority) + ' ' + str(self.name) + return str(self.zone) + " " + str(self.priority) + " " + str(self.name) class Ns(RevMixin, AclMixin, models.Model): """Liste des enregistrements name servers par zone considéérée""" - zone = models.ForeignKey('Extension', on_delete=models.PROTECT) - ns = models.ForeignKey('Domain', on_delete=models.PROTECT) + + zone = models.ForeignKey("Extension", on_delete=models.PROTECT) + ns = models.ForeignKey("Domain", on_delete=models.PROTECT) + ttl = models.PositiveIntegerField( + verbose_name=_("Time To Live (TTL)"), default=172800 # 2 days + ) class Meta: - permissions = ( - ("view_ns", _("Can view an NS record object")), - ) + permissions = (("view_ns", _("Can view an NS record object")),) verbose_name = _("NS record") verbose_name_plural = _("NS records") @@ -811,25 +885,26 @@ class Ns(RevMixin, AclMixin, models.Model): return "@ IN NS " + str(self.ns) def __str__(self): - return str(self.zone) + ' ' + str(self.ns) + return str(self.zone) + " " + str(self.ns) class Txt(RevMixin, AclMixin, models.Model): """ Un enregistrement TXT associé à une extension""" - zone = models.ForeignKey('Extension', on_delete=models.PROTECT) + + zone = models.ForeignKey("Extension", on_delete=models.PROTECT) field1 = models.CharField(max_length=255) field2 = models.TextField(max_length=2047) + ttl = models.PositiveIntegerField( + verbose_name=_("Time To Live (TTL)"), default=172800 # 2 days + ) class Meta: - permissions = ( - ("view_txt", _("Can view a TXT record object")), - ) + permissions = (("view_txt", _("Can view a TXT record object")),) verbose_name = _("TXT record") verbose_name_plural = _("TXT records") def __str__(self): - return str(self.zone) + " : " + str(self.field1) + " " + \ - str(self.field2) + return str(self.zone) + " : " + str(self.field1) + " " + str(self.field2) @cached_property def dns_entry(self): @@ -839,13 +914,15 @@ class Txt(RevMixin, AclMixin, models.Model): class DName(RevMixin, AclMixin, models.Model): """A DNAME entry for the DNS.""" - zone = models.ForeignKey('Extension', on_delete=models.PROTECT) + + zone = models.ForeignKey("Extension", on_delete=models.PROTECT) alias = models.CharField(max_length=255) + ttl = models.PositiveIntegerField( + verbose_name=_("Time To Live (TTL)"), default=172800 # 2 days + ) class Meta: - permissions = ( - ("view_dname", _("Can view a DNAME record object")), - ) + permissions = (("view_dname", _("Can view a DNAME record object")),) verbose_name = _("DNAME record") verbose_name_plural = _("DNAME records") @@ -860,65 +937,82 @@ class DName(RevMixin, AclMixin, models.Model): class Srv(RevMixin, AclMixin, models.Model): """ A SRV record """ - TCP = 'TCP' - UDP = 'UDP' + + TCP = "TCP" + UDP = "UDP" service = models.CharField(max_length=31) protocole = models.CharField( - max_length=3, - choices=( - (TCP, 'TCP'), - (UDP, 'UDP'), - ), - default=TCP, + max_length=3, choices=((TCP, "TCP"), (UDP, "UDP")), default=TCP ) - extension = models.ForeignKey('Extension', on_delete=models.PROTECT) + extension = models.ForeignKey("Extension", on_delete=models.PROTECT) ttl = models.PositiveIntegerField( - default=172800, # 2 days - help_text=_("Time to Live") + default=172800, help_text=_("Time To Live.") # 2 days ) priority = models.PositiveIntegerField( default=0, validators=[MaxValueValidator(65535)], - help_text=_("Priority of the target server (positive integer value," - " the lower it is, the more the server will be used if" - " available)") + help_text=_( + "Priority of the target server (positive integer value," + " the lower it is, the more the server will be used if" + " available)." + ), ) weight = models.PositiveIntegerField( default=0, validators=[MaxValueValidator(65535)], - help_text=_("Relative weight for records with the same priority" - " (integer value between 0 and 65535)") + help_text=_( + "Relative weight for records with the same priority" + " (integer value between 0 and 65535)." + ), ) port = models.PositiveIntegerField( - validators=[MaxValueValidator(65535)], - help_text=_("TCP/UDP port") + validators=[MaxValueValidator(65535)], help_text=_("TCP/UDP port.") ) target = models.ForeignKey( - 'Domain', - on_delete=models.PROTECT, - help_text=_("Target server") + "Domain", on_delete=models.PROTECT, help_text=_("Target server.") ) class Meta: - permissions = ( - ("view_srv", _("Can view an SRV record object")), - ) + permissions = (("view_srv", _("Can view an SRV record object")),) verbose_name = _("SRV record") verbose_name_plural = _("SRV records") def __str__(self): - return str(self.service) + ' ' + str(self.protocole) + ' ' + \ - str(self.extension) + ' ' + str(self.priority) + \ - ' ' + str(self.weight) + str(self.port) + str(self.target) + return ( + str(self.service) + + " " + + str(self.protocole) + + " " + + str(self.extension) + + " " + + str(self.priority) + + " " + + str(self.weight) + + str(self.port) + + str(self.target) + ) @cached_property def dns_entry(self): """Renvoie l'enregistrement SRV complet pour le fichier de zone""" - return str(self.service) + '._' + str(self.protocole).lower() + \ - str(self.extension) + '. ' + str(self.ttl) + ' IN SRV ' + \ - str(self.priority) + ' ' + str(self.weight) + ' ' + \ - str(self.port) + ' ' + str(self.target) + '.' + return ( + str(self.service) + + "._" + + str(self.protocole).lower() + + str(self.extension) + + ". " + + str(self.ttl) + + " IN SRV " + + str(self.priority) + + " " + + str(self.weight) + + " " + + str(self.port) + + " " + + str(self.target) + + "." + ) class SshFp(RevMixin, AclMixin, models.Model): @@ -932,20 +1026,11 @@ class SshFp(RevMixin, AclMixin, models.Model): ("ecdsa-sha2-nistp521", "ecdsa-sha2-nistp521"), ) - machine = models.ForeignKey('Machine', on_delete=models.CASCADE) - pub_key_entry = models.TextField( - help_text=_("SSH public key"), - max_length=2048 - ) - algo = models.CharField( - choices=ALGO, - max_length=32 - ) + machine = models.ForeignKey("Machine", on_delete=models.CASCADE) + pub_key_entry = models.TextField(help_text=_("SSH public key."), max_length=2048) + algo = models.CharField(choices=ALGO, max_length=32) comment = models.CharField( - help_text=_("Comment"), - max_length=255, - null=True, - blank=True + help_text=_("Comment."), max_length=255, null=True, blank=True ) @cached_property @@ -968,9 +1053,7 @@ class SshFp(RevMixin, AclMixin, models.Model): } class Meta: - permissions = ( - ("view_sshfp", _("Can view an SSHFP record object")), - ) + permissions = (("view_sshfp", _("Can view an SSHFP record object")),) verbose_name = _("SSHFP record") verbose_name_plural = _("SSHFP records") @@ -984,7 +1067,7 @@ class SshFp(RevMixin, AclMixin, models.Model): return self.machine.can_delete(user_request, *args, **kwargs) def __str__(self): - return str(self.algo) + ' ' + str(self.comment) + return str(self.algo) + " " + str(self.comment) class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): @@ -995,23 +1078,20 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): - le type parent associé au range ip et à l'extension - un objet domain associé contenant son nom - la liste des ports oiuvert""" + ipv4 = models.OneToOneField( - 'IpList', - on_delete=models.PROTECT, - blank=True, - null=True + "IpList", on_delete=models.PROTECT, blank=True, null=True ) mac_address = MACAddressField(integer=False) - machine = models.ForeignKey('Machine', on_delete=models.CASCADE) - type = models.ForeignKey('MachineType', on_delete=models.PROTECT) + machine = models.ForeignKey("Machine", on_delete=models.CASCADE) + machine_type = models.ForeignKey("MachineType", on_delete=models.PROTECT) details = models.CharField(max_length=255, blank=True) - port_lists = models.ManyToManyField('OuverturePortList', blank=True) + port_lists = models.ManyToManyField("OuverturePortList", blank=True) class Meta: permissions = ( ("view_interface", _("Can view an interface object")), - ("change_interface_machine", - _("Can change the owner of an interface")), + ("change_interface_machine", _("Can change the owner of an interface")), ) verbose_name = _("interface") verbose_name_plural = _("interfaces") @@ -1027,9 +1107,9 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): def ipv6_slaac(self): """ Renvoie un objet type ipv6 à partir du prefix associé à l'iptype parent""" - if self.type.ip_type.prefix_v6: + if self.machine_type.ip_type.prefix_v6: return EUI(self.mac_address).ipv6( - IPNetwork(self.type.ip_type.prefix_v6).network + IPNetwork(self.machine_type.ip_type.prefix_v6).network ) else: return None @@ -1037,14 +1117,24 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): @cached_property def gen_ipv6_dhcpv6(self): """Cree une ip, à assigner avec dhcpv6 sur une machine""" - prefix_v6 = self.type.ip_type.prefix_v6.encode().decode('utf-8') + prefix_v6 = self.machine_type.ip_type.prefix_v6.encode().decode("utf-8") if not prefix_v6: return None return IPv6Address( - IPv6Address(prefix_v6).exploded[:20] + - IPv6Address(self.id).exploded[20:] + IPv6Address(prefix_v6).exploded[:20] + IPv6Address(self.id).exploded[20:] ) + @cached_property + def get_vendor(self): + """Retourne le vendeur associé à la mac de l'interface""" + mac = EUI(self.mac_address) + try: + oui = mac.oui + vendor = oui.registration().org + except NotRegisteredError: + vendor = _("Unknown vendor.") + return vendor + def sync_ipv6_dhcpv6(self): """Affecte une ipv6 dhcpv6 calculée à partir de l'id de la machine""" ipv6_dhcpv6 = self.gen_ipv6_dhcpv6 @@ -1065,9 +1155,7 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): ipv6_slaac = self.ipv6_slaac if not ipv6_slaac: return - ipv6_object = (Ipv6List.objects - .filter(interface=self, slaac_ip=True) - .first()) + ipv6_object = Ipv6List.objects.filter(interface=self, slaac_ip=True).first() if not ipv6_object: ipv6_object = Ipv6List(interface=self, slaac_ip=True) if ipv6_object.ipv6 != str(ipv6_slaac): @@ -1076,11 +1164,11 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): def sync_ipv6(self): """Cree et met à jour l'ensemble des ipv6 en fonction du mode choisi""" - if (preferences.models.OptionalMachine - .get_cached_value('ipv6_mode') == 'SLAAC'): + if preferences.models.OptionalMachine.get_cached_value("ipv6_mode") == "SLAAC": self.sync_ipv6_slaac() - elif (preferences.models.OptionalMachine - .get_cached_value('ipv6_mode') == 'DHCPV6'): + elif ( + preferences.models.OptionalMachine.get_cached_value("ipv6_mode") == "DHCPV6" + ): self.sync_ipv6_dhcpv6() else: return @@ -1089,11 +1177,11 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): """ Renvoie le queryset de la liste des ipv6 On renvoie l'ipv6 slaac que si le mode slaac est activé (et non dhcpv6)""" - if (preferences.models.OptionalMachine - .get_cached_value('ipv6_mode') == 'SLAAC'): + if preferences.models.OptionalMachine.get_cached_value("ipv6_mode") == "SLAAC": return self.ipv6list.all() - elif (preferences.models.OptionalMachine - .get_cached_value('ipv6_mode') == 'DHCPV6'): + elif ( + preferences.models.OptionalMachine.get_cached_value("ipv6_mode") == "DHCPV6" + ): return self.ipv6list.filter(slaac_ip=False) else: return [] @@ -1112,18 +1200,34 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): def assign_ipv4(self): """ Assigne une ip à l'interface """ - free_ips = self.type.ip_type.free_ip() + free_ips = self.machine_type.ip_type.free_ip() if free_ips: self.ipv4 = free_ips[0] else: - raise ValidationError(_("There is no IP address available in the" - " slash.")) + raise ValidationError( + _("There are no IP addresses available in the slash.") + ) return def unassign_ipv4(self): """ Sans commentaire, désassigne une ipv4""" self.ipv4 = None + @classmethod + def mass_unassign_ipv4(cls, interface_list): + """Unassign ipv4 to multiple interfaces""" + with transaction.atomic(), reversion.create_revision(): + interface_list.update(ipv4=None) + reversion.set_comment("IPv4 unassignment") + + @classmethod + def mass_assign_ipv4(cls, interface_list): + for interface in interface_list: + with transaction.atomic(), reversion.create_revision(): + interface.assign_ipv4() + interface.save() + reversion.set_comment("IPv4 assignment") + def update_type(self): """ Lorsque le machinetype est changé de type d'ip, on réassigne""" self.clean() @@ -1152,26 +1256,32 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): # instance. # But in our case, it's impossible to create a type value so we raise # the error. - if not hasattr(self, 'type'): + if not hasattr(self, "machine_type"): raise ValidationError(_("The selected IP type is invalid.")) self.filter_macaddress() - if not self.ipv4 or self.type.ip_type != self.ipv4.ip_type: + if not self.ipv4 or self.machine_type.ip_type != self.ipv4.ip_type: self.assign_ipv4() super(Interface, self).clean(*args, **kwargs) def validate_unique(self, *args, **kwargs): super(Interface, self).validate_unique(*args, **kwargs) - interfaces_similar = Interface.objects.filter(mac_address=self.mac_address, type__ip_type=self.type.ip_type) + interfaces_similar = Interface.objects.filter( + mac_address=self.mac_address, + machine_type__ip_type=self.machine_type.ip_type, + ) if interfaces_similar and interfaces_similar.first() != self: - raise ValidationError(_("Mac address already registered in this Machine Type/Subnet")) + raise ValidationError( + _("MAC address already registered in this machine type/subnet.") + ) def save(self, *args, **kwargs): self.filter_macaddress() # On verifie la cohérence en forçant l'extension par la méthode if self.ipv4: - if self.type.ip_type != self.ipv4.ip_type: - raise ValidationError(_("The IPv4 address and the machine type" - " don't match.")) + if self.machine_type.ip_type != self.ipv4.ip_type: + raise ValidationError( + _("The IPv4 address and the machine type don't match.") + ) self.validate_unique() super(Interface, self).save(*args, **kwargs) @@ -1185,31 +1295,46 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): try: machine = Machine.objects.get(pk=machineid) except Machine.DoesNotExist: - return False, _("Nonexistent machine.") - if not user_request.has_perm('machines.add_interface'): - if not (preferences.models.OptionalMachine - .get_cached_value('create_machine')): - return False, _("You can't add a machine.") - max_lambdauser_interfaces = (preferences.models.OptionalMachine - .get_cached_value( - 'max_lambdauser_interfaces' - )) + return False, _("Nonexistent machine."), None + if not user_request.has_perm("machines.add_interface"): + if not ( + preferences.models.OptionalMachine.get_cached_value("create_machine") + ): + return False, _("You don't have the right to add a machine."), ("machines.add_interface",) + max_lambdauser_interfaces = preferences.models.OptionalMachine.get_cached_value( + "max_lambdauser_interfaces" + ) if machine.user != user_request: - return False, _("You don't have the right to add an interface" - " to a machine of another user.") - if (machine.user.user_interfaces().count() >= - max_lambdauser_interfaces): - return False, (_("You reached the maximum number of interfaces" - " that you are allowed to create yourself" - " (%s)." % max_lambdauser_interfaces)) - return True, None + return ( + False, + _( + "You don't have the right to add an interface" + " to a machine of another user." + ), + ("machines.add_interface",), + ) + if machine.user.user_interfaces().count() >= max_lambdauser_interfaces: + return ( + False, + _( + "You reached the maximum number of interfaces" + " that you are allowed to create yourself" + " (%s)." % max_lambdauser_interfaces + ), + ("machines.add_interface",), + ) + return True, None, None @staticmethod def can_change_machine(user_request, *_args, **_kwargs): """Check if a user can change the machine associated with an Interface object """ - return (user_request.has_perm('machines.change_interface_machine'), - _("Permission required to edit the machine.")) + can = user_request.has_perm("machines.change_interface_machine") + return ( + can, + _("You don't have the right to edit the machine.") if not can else None, + ("machines.change_interface_machine",), + ) def can_edit(self, user_request, *args, **kwargs): """Verifie que l'user a les bons droits infra pour editer @@ -1218,15 +1343,17 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): :param user_request: Utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" if self.machine.user != user_request: - if (not user_request.has_perm('machines.change_interface') or - not self.machine.user.can_edit( - user_request, - *args, - **kwargs - )[0]): - return False, _("You don't have the right to edit a machine of" - " another user.") - return True, None + can_user, _message, permissions = self.machine.user.can_edit( + user_request, *args, **kwargs + ) + if not (user_request.has_perm("machines.change_interface") and can_user): + return ( + False, + _("You don't have the right to edit a machine of another" + " user."), + ("machines.change_interface",) + permissions, + ) + return True, None, None def can_delete(self, user_request, *args, **kwargs): """Verifie que l'user a les bons droits delete object pour del @@ -1235,15 +1362,17 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): :param user_request: Utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" if self.machine.user != user_request: - if (not user_request.has_perm('machines.change_interface') or - not self.machine.user.can_edit( - user_request, - *args, - **kwargs - )[0]): - return False, _("You don't have the right to edit a machine of" - " another user.") - return True, None + can_user, _message, permissions = self.machine.user.can_edit( + user_request, *args, **kwargs + ) + if not (user_request.has_perm("machines.change_interface") and can_user): + return ( + False, + _("You don't have the right to edit a machine of another" + " user."), + ("machines.change_interface",) + permissions, + ) + return True, None, None def can_view(self, user_request, *_args, **_kwargs): """Vérifie qu'on peut bien voir cette instance particulière avec @@ -1251,17 +1380,20 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): :param self: instance interface à voir :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" - if (not user_request.has_perm('machines.view_interface') and - self.machine.user != user_request): - return False, _("You don't have the right to view machines other" - " than yours.") - return True, None + if ( + not user_request.has_perm("machines.view_interface") + and self.machine.user != user_request + ): + return ( + False, + _("You don't have the right to view machines other than yours."), + ("machines.view_interface",), + ) + return True, None, None def __init__(self, *args, **kwargs): super(Interface, self).__init__(*args, **kwargs) - self.field_permissions = { - 'machine': self.can_change_machine, - } + self.field_permissions = {"machine": self.can_change_machine} def __str__(self): try: @@ -1274,21 +1406,19 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): """ A list of IPv6 """ - ipv6 = models.GenericIPAddressField( - protocol='IPv6', - ) + ipv6 = models.GenericIPAddressField(protocol="IPv6") interface = models.ForeignKey( - 'Interface', - on_delete=models.CASCADE, - related_name='ipv6list' + "Interface", on_delete=models.CASCADE, related_name="ipv6list" ) slaac_ip = models.BooleanField(default=False) class Meta: permissions = ( ("view_ipv6list", _("Can view an IPv6 addresses list object")), - ("change_ipv6list_slaac_ip", _("Can change the SLAAC value of an" - " IPv6 addresses list")), + ( + "change_ipv6list_slaac_ip", + _("Can change the SLAAC value of an IPv6 addresses list"), + ), ) verbose_name = _("IPv6 addresses list") verbose_name_plural = _("IPv6 addresses lists") @@ -1303,19 +1433,30 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): try: interface = Interface.objects.get(pk=interfaceid) except Interface.DoesNotExist: - return False, _("Nonexistent interface.") - if not user_request.has_perm('machines.add_ipv6list'): + return False, _("Nonexistent interface."), None + if not user_request.has_perm("machines.add_ipv6list"): if interface.machine.user != user_request: - return False, _("You don't have the right to add an alias to a" - " machine of another user.") - return True, None + return ( + False, + _( + "You don't have the right to add an alias to a" + " machine of another user." + ), + ("machines.add_ipv6list",), + ) + return True, None, None @staticmethod def can_change_slaac_ip(user_request, *_args, **_kwargs): """ Check if a user can change the slaac value """ - return (user_request.has_perm('machines.change_ipv6list_slaac_ip'), - _("Permission required to change the SLAAC value of an IPv6" - " address")) + can = user_request.has_perm("machines.change_ipv6list_slaac_ip") + return ( + can, + _("You don't have the right to change the SLAAC value of an IPv6 address.") + if not can + else None, + ("machines.change_ipv6list_slaac_ip",), + ) def can_edit(self, user_request, *args, **kwargs): """Verifie que l'user a les bons droits infra pour editer @@ -1324,15 +1465,16 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): :param user_request: Utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" if self.interface.machine.user != user_request: - if (not user_request.has_perm('machines.change_ipv6list') or - not self.interface.machine.user.can_edit( - user_request, - *args, - **kwargs - )[0]): - return False, _("You don't have the right to edit a machine of" - " another user.") - return True, None + can_user, _message, permissions = self.interface.machine.user.can_edit( + user_request, *args, **kwargs + ) + if not (user_request.has_perm("machines.change_ipv6list") and can_user): + return ( + False, + _("You don't have the right to edit a machine of another user."), + ("machines.change_ipv6list",), + ) + return True, None, None def can_delete(self, user_request, *args, **kwargs): """Verifie que l'user a les bons droits delete object pour del @@ -1341,15 +1483,16 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): :param user_request: Utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" if self.interface.machine.user != user_request: - if (not user_request.has_perm('machines.change_ipv6list') or - not self.interface.machine.user.can_edit( - user_request, - *args, - **kwargs - )[0]): - return False, _("You don't have the right to edit a machine of" - " another user.") - return True, None + can_user, _message, permissions = self.interface.machine.user.can_edit( + user_request, *args, **kwargs + ) + if not (user_request.has_perm("machines.change_ipv6list") and can_user): + return ( + False, + _("You don't have the right to edit a machine of another user."), + ("machines.change_ipv6list",) + permissions, + ) + return True, None, None def can_view(self, user_request, *_args, **_kwargs): """Vérifie qu'on peut bien voir cette instance particulière avec @@ -1357,46 +1500,63 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): :param self: instance interface à voir :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" - if (not user_request.has_perm('machines.view_ipv6list') and - self.interface.machine.user != user_request): - return False, _("You don't have the right to view machines other" - " than yours.") - return True, None + if ( + not user_request.has_perm("machines.view_ipv6list") + and self.interface.machine.user != user_request + ): + return ( + False, + _("You don't have the right to view machines other than yours."), + ("machines.view_ipv6list",), + ) + return True, None, None def __init__(self, *args, **kwargs): super(Ipv6List, self).__init__(*args, **kwargs) - self.field_permissions = { - 'slaac_ip': self.can_change_slaac_ip, - } + self.field_permissions = {"slaac_ip": self.can_change_slaac_ip} def check_and_replace_prefix(self, prefix=None): """Si le prefixe v6 est incorrect, on maj l'ipv6""" - prefix_v6 = prefix or self.interface.type.ip_type.prefix_v6.encode().decode('utf-8') + prefix_v6 = prefix or self.interface.machine_type.ip_type.prefix_v6.encode().decode( + "utf-8" + ) if not prefix_v6: return - if (IPv6Address(self.ipv6.encode().decode('utf-8')).exploded[:20] != - IPv6Address(prefix_v6).exploded[:20]): + if ( + IPv6Address(self.ipv6.encode().decode("utf-8")).exploded[:20] + != IPv6Address(prefix_v6).exploded[:20] + ): self.ipv6 = IPv6Address( - IPv6Address(prefix_v6).exploded[:20] + - IPv6Address(self.ipv6.encode().decode('utf-8')).exploded[20:] + IPv6Address(prefix_v6).exploded[:20] + + IPv6Address(self.ipv6.encode().decode("utf-8")).exploded[20:] ) self.save() def clean(self, *args, **kwargs): - if self.slaac_ip and (Ipv6List.objects - .filter(interface=self.interface, slaac_ip=True) - .exclude(id=self.id)): + if self.slaac_ip and ( + Ipv6List.objects.filter(interface=self.interface, slaac_ip=True).exclude( + id=self.id + ) + ): raise ValidationError(_("A SLAAC IP address is already registered.")) try: - prefix_v6 = self.interface.type.ip_type.prefix_v6.encode().decode('utf-8') - except AttributeError: # Prevents from crashing when there is no defined prefix_v6 + prefix_v6 = self.interface.machine_type.ip_type.prefix_v6.encode().decode( + "utf-8" + ) + except AttributeError: # Prevents from crashing when there is no defined prefix_v6 prefix_v6 = None if prefix_v6: - if (IPv6Address(self.ipv6.encode().decode('utf-8')).exploded[:20] != - IPv6Address(prefix_v6).exploded[:20]): - raise ValidationError(_("The v6 prefix is incorrect and" - " doesn't match the type associated" - " with the machine.")) + if ( + IPv6Address(self.ipv6.encode().decode("utf-8")).exploded[:20] + != IPv6Address(prefix_v6).exploded[:20] + ): + raise ValidationError( + _( + "The v6 prefix is incorrect and" + " doesn't match the type associated" + " with the machine." + ) + ) super(Ipv6List, self).clean(*args, **kwargs) def save(self, *args, **kwargs): @@ -1408,33 +1568,32 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): return str(self.ipv6) -class Domain(RevMixin, AclMixin, models.Model): +class Domain(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): """ Objet domain. Enregistrement A et CNAME en même temps : permet de stocker les alias et les nom de machines, suivant si interface_parent ou cname sont remplis""" interface_parent = models.OneToOneField( - 'Interface', - on_delete=models.CASCADE, - blank=True, - null=True + "Interface", on_delete=models.CASCADE, blank=True, null=True ) name = models.CharField( - help_text=_("Mandatory and unique, must not contain dots."), - max_length=255 + help_text=_("Mandatory and unique, must not contain dots."), max_length=255 ) - extension = models.ForeignKey('Extension', on_delete=models.PROTECT) + extension = models.ForeignKey("Extension", on_delete=models.PROTECT) cname = models.ForeignKey( - 'self', - null=True, - blank=True, - related_name='related_domain' + "self", null=True, blank=True, related_name="related_domain" + ) + ttl = models.PositiveIntegerField( + verbose_name=_("Time To Live (TTL)"), + default=0 # 0 means that the re2o-service for DNS should retrieve the + # default TTL ) class Meta: unique_together = (("name", "extension"),) permissions = ( ("view_domain", _("Can view a domain object")), + ("change_ttl", _("Can change the TTL of a domain object")), ) verbose_name = _("domain") verbose_name_plural = _("domains") @@ -1443,8 +1602,8 @@ class Domain(RevMixin, AclMixin, models.Model): """ Retourne l'extension de l'interface parente si c'est un A Retourne l'extension propre si c'est un cname, renvoie None sinon""" if self.interface_parent: - return self.interface_parent.type.ip_type.extension - elif hasattr(self, 'extension'): + return self.interface_parent.machine_type.ip_type.extension + elif hasattr(self, "extension"): return self.extension else: return None @@ -1459,22 +1618,21 @@ class Domain(RevMixin, AclMixin, models.Model): if self.get_extension(): self.extension = self.get_extension() if self.interface_parent and self.cname: - raise ValidationError(_("You can't create a both A and CNAME" - " record.")) + raise ValidationError(_("You can't create a both A and CNAME record.")) if self.cname == self: - raise ValidationError(_("You can't create a CNAME record pointing" - " to itself.")) - HOSTNAME_LABEL_PATTERN = re.compile( - r"(?!-)[A-Z\d-]+(? 63: - raise ValidationError(_("The domain name %s is too long (over 63" - " characters).") % dns) + raise ValidationError( + _("The domain name %s is too long (over 63 characters).") % dns + ) if not HOSTNAME_LABEL_PATTERN.match(dns): - raise ValidationError(_("The domain name %s contains forbidden" - " characters.") % dns) + raise ValidationError( + _("The domain name %s contains forbidden characters.") % dns + ) self.validate_unique() super(Domain, self).clean() @@ -1483,8 +1641,7 @@ class Domain(RevMixin, AclMixin, models.Model): """ Une entrée DNS""" if self.cname: return "{name} IN CNAME {cname}.".format( - name=str(self.name).ljust(15), - cname=str(self.cname) + name=str(self.name).ljust(15), cname=str(self.cname) ) def save(self, *args, **kwargs): @@ -1505,7 +1662,7 @@ class Domain(RevMixin, AclMixin, models.Model): if self.interface_parent: return self.interface_parent else: - return self.cname.get_parent_interface() + return self.cname.get_source_interface @staticmethod def can_create(user_request, interfaceid, *_args, **_kwargs): @@ -1517,25 +1674,38 @@ class Domain(RevMixin, AclMixin, models.Model): try: interface = Interface.objects.get(pk=interfaceid) except Interface.DoesNotExist: - return False, _("Nonexistent interface.") - if not user_request.has_perm('machines.add_domain'): - max_lambdauser_aliases = (preferences.models.OptionalMachine - .get_cached_value( - 'max_lambdauser_aliases' - )) + return False, _("Nonexistent interface."), None + if not user_request.has_perm("machines.add_domain"): + max_lambdauser_aliases = preferences.models.OptionalMachine.get_cached_value( + "max_lambdauser_aliases" + ) if interface.machine.user != user_request: - return False, _("You don't have the right to add an alias to a" - " machine of another user.") - if Domain.objects.filter( + return ( + False, + _( + "You don't have the right to add an alias to a" + " machine of another user." + ), + ("machines.add_domain",), + ) + if ( + Domain.objects.filter( cname__in=Domain.objects.filter( - interface_parent__in=(interface.machine.user - .user_interfaces()) + interface_parent__in=(interface.machine.user.user_interfaces()) ) - ).count() >= max_lambdauser_aliases: - return False, _("You reached the maximum number of alias that" - " you are allowed to create yourself (%s). " - % max_lambdauser_aliases) - return True, None + ).count() + >= max_lambdauser_aliases + ): + return ( + False, + _( + "You reached the maximum number of alias that" + " you are allowed to create yourself (%s). " + % max_lambdauser_aliases + ), + ("machines.add_domain",), + ) + return True, None, None def can_edit(self, user_request, *_args, **_kwargs): """Verifie que l'user a les bons droits pour editer @@ -1543,11 +1713,19 @@ class Domain(RevMixin, AclMixin, models.Model): :param self: Instance domain à editer :param user_request: Utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" - if (not user_request.has_perm('machines.change_domain') and - self.get_source_interface.machine.user != user_request): - return False, _("You don't have the right to edit an alias of a" - " machine of another user.") - return True, None + if ( + not user_request.has_perm("machines.change_domain") + and self.get_source_interface.machine.user != user_request + ): + return ( + False, + _( + "You don't have the right to edit an alias of a" + " machine of another user." + ), + ("machines.change_domain",), + ) + return True, None, None def can_delete(self, user_request, *_args, **_kwargs): """Verifie que l'user a les bons droits delete object pour del @@ -1555,11 +1733,19 @@ class Domain(RevMixin, AclMixin, models.Model): :param self: Instance domain à del :param user_request: Utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" - if (not user_request.has_perm('machines.delete_domain') and - self.get_source_interface.machine.user != user_request): - return False, _("You don't have the right to delete an alias of a" - " machine of another user.") - return True, None + if ( + not user_request.has_perm("machines.delete_domain") + and self.get_source_interface.machine.user != user_request + ): + return ( + False, + _( + "You don't have the right to delete an alias of a" + " machine of another user." + ), + ("machines.delete_domain",), + ) + return True, None, None def can_view(self, user_request, *_args, **_kwargs): """Vérifie qu'on peut bien voir cette instance particulière avec @@ -1567,11 +1753,28 @@ class Domain(RevMixin, AclMixin, models.Model): :param self: instance domain à voir :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" - if (not user_request.has_perm('machines.view_domain') and - self.get_source_interface.machine.user != user_request): - return False, _("You don't have the right to view machines other" - " than yours.") - return True, None + if ( + not user_request.has_perm("machines.view_domain") + and self.get_source_interface.machine.user != user_request + ): + return ( + False, + _("You don't have the right to view other machines than" + " yours."), + ("machines.view_domain",), + ) + return True, None, None + + @staticmethod + def can_change_ttl(user_request, *_args, **_kwargs): + can = user_request.has_perm("machines.change_ttl") + return ( + can, + _("You don't have the right to change the domain's TTL.") + if not can + else None, + ("machines.change_ttl",), + ) def __str__(self): return str(self.name) + str(self.extension) @@ -1580,13 +1783,11 @@ class Domain(RevMixin, AclMixin, models.Model): class IpList(RevMixin, AclMixin, models.Model): """ A list of IPv4 """ - ipv4 = models.GenericIPAddressField(protocol='IPv4', unique=True) - ip_type = models.ForeignKey('IpType', on_delete=models.CASCADE) + ipv4 = models.GenericIPAddressField(protocol="IPv4", unique=True) + ip_type = models.ForeignKey("IpType", on_delete=models.CASCADE) class Meta: - permissions = ( - ("view_iplist", _("Can view an IPv4 addresses list object")), - ) + permissions = (("view_iplist", _("Can view an IPv4 addresses list object")),) verbose_name = _("IPv4 addresses list") verbose_name_plural = _("IPv4 addresses lists") @@ -1599,8 +1800,9 @@ class IpList(RevMixin, AclMixin, models.Model): def clean(self): """ Erreur si l'ip_type est incorrect""" if not str(self.ipv4) in self.ip_type.ip_set_as_str: - raise ValidationError(_("The IPv4 address and the range of the IP" - " type don't match.")) + raise ValidationError( + _("The IPv4 address and the range of the IP type don't match.") + ) return def save(self, *args, **kwargs): @@ -1617,43 +1819,34 @@ class Role(RevMixin, AclMixin, models.Model): """ ROLE = ( - ('dhcp-server', _("DHCP server")), - ('switch-conf-server', _("Switches configuration server")), - ('dns-recursive-server', _("Recursive DNS server")), - ('ntp-server', _("NTP server")), - ('radius-server', _("RADIUS server")), - ('log-server', _("Log server")), - ('ldap-master-server', _("LDAP master server")), - ('ldap-backup-server', _("LDAP backup server")), - ('smtp-server', _("SMTP server")), - ('postgresql-server', _("postgreSQL server")), - ('mysql-server', _("mySQL server")), - ('sql-client', _("SQL client")), - ('gateway', _("Gateway")), + ("dhcp-server", _("DHCP server")), + ("switch-conf-server", _("Switches configuration server")), + ("dns-recursive-server", _("Recursive DNS server")), + ("ntp-server", _("NTP server")), + ("radius-server", _("RADIUS server")), + ("log-server", _("Log server")), + ("ldap-master-server", _("LDAP master server")), + ("ldap-backup-server", _("LDAP backup server")), + ("smtp-server", _("SMTP server")), + ("postgresql-server", _("postgreSQL server")), + ("mysql-server", _("mySQL server")), + ("sql-client", _("SQL client")), + ("gateway", _("Gateway")), ) role_type = models.CharField(max_length=255, unique=True) - servers = models.ManyToManyField('Interface') - specific_role = models.CharField( - choices=ROLE, - null=True, - blank=True, - max_length=32, - ) + servers = models.ManyToManyField("Interface") + specific_role = models.CharField(choices=ROLE, null=True, blank=True, max_length=32) class Meta: - permissions = ( - ("view_role", _("Can view a role object")), - ) + permissions = (("view_role", _("Can view a role object")),) verbose_name = _("server role") verbose_name_plural = _("server roles") @classmethod def interface_for_roletype(cls, roletype): """Return interfaces for a roletype""" - return Interface.objects.filter( - role=cls.objects.filter(specific_role=roletype) - ) + return Interface.objects.filter(role=cls.objects.filter(specific_role=roletype)) @classmethod def all_interfaces_for_roletype(cls, roletype): @@ -1680,37 +1873,33 @@ class Service(RevMixin, AclMixin, models.Model): service_type = models.CharField(max_length=255, blank=True, unique=True) min_time_regen = models.DurationField( default=timedelta(minutes=1), - help_text=_("Minimal time before regeneration of the service.") + help_text=_("Minimal time before regeneration of the service."), ) regular_time_regen = models.DurationField( default=timedelta(hours=1), - help_text=_("Maximal time before regeneration of the service.") + help_text=_("Maximal time before regeneration of the service."), ) - servers = models.ManyToManyField('Interface', through='Service_link') + servers = models.ManyToManyField("Interface", through="Service_link") class Meta: - permissions = ( - ("view_service", _("Can view a service object")), - ) + permissions = (("view_service", _("Can view a service object")),) verbose_name = _("service to generate (DHCP, DNS, ...)") verbose_name_plural = _("services to generate (DHCP, DNS, ...)") def ask_regen(self): """ Marque à True la demande de régénération pour un service x """ - Service_link.objects.filter(service=self).exclude(asked_regen=True) \ - .update(asked_regen=True) + Service_link.objects.filter(service=self).exclude(asked_regen=True).update( + asked_regen=True + ) return def process_link(self, servers): """ Django ne peut créer lui meme les relations manytomany avec table intermediaire explicite""" - for serv in servers.exclude( - pk__in=Interface.objects.filter(service=self) - ): + for serv in servers.exclude(pk__in=Interface.objects.filter(service=self)): link = Service_link(service=self, server=serv) link.save() - Service_link.objects.filter(service=self).exclude(server__in=servers) \ - .delete() + Service_link.objects.filter(service=self).exclude(server__in=servers).delete() return def save(self, *args, **kwargs): @@ -1732,8 +1921,8 @@ def regen(service): class Service_link(RevMixin, AclMixin, models.Model): """ Definition du lien entre serveurs et services""" - service = models.ForeignKey('Service', on_delete=models.CASCADE) - server = models.ForeignKey('Interface', on_delete=models.CASCADE) + service = models.ForeignKey("Service", on_delete=models.CASCADE) + server = models.ForeignKey("Interface", on_delete=models.CASCADE) last_regen = models.DateTimeField(auto_now_add=True) asked_regen = models.BooleanField(default=False) @@ -1755,12 +1944,11 @@ class Service_link(RevMixin, AclMixin, models.Model): """ Décide si le temps minimal écoulé est suffisant pour provoquer une régénération de service""" return bool( - (self.asked_regen and ( - self.last_regen + self.service.min_time_regen - ) < timezone.now() - ) or ( - self.last_regen + self.service.regular_time_regen - ) < timezone.now() + ( + self.asked_regen + and (self.last_regen + self.service.min_time_regen) < timezone.now() + ) + or (self.last_regen + self.service.regular_time_regen) < timezone.now() ) @need_regen.setter @@ -1784,14 +1972,13 @@ class OuverturePortList(RevMixin, AclMixin, models.Model): """Liste des ports ouverts sur une interface.""" name = models.CharField( - help_text=_("Name of the ports configuration"), - max_length=255 + help_text=_("Name of the ports configuration"), max_length=255 ) class Meta: permissions = ( ("view_ouvertureportlist", _("Can view a ports opening list" - " object")), + " object")), ) verbose_name = _("ports opening list") verbose_name_plural = _("ports opening lists") @@ -1802,12 +1989,15 @@ class OuverturePortList(RevMixin, AclMixin, models.Model): :param self: Instance ouvertureportlist à delete :param user_request: Utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" - if not user_request.has_perm('machines.delete_ouvertureportlist'): - return False, _("You don't have the right to delete a ports" - " opening list.") + if not user_request.has_perm("machines.delete_ouvertureportlist"): + return ( + False, + _("You don't have the right to delete a ports opening list."), + ("machines.delete_ouvertureportlist",), + ) if self.interface_set.all(): - return False, _("This ports opening list is used.") - return True, None + return False, _("This ports opening list is used."), None + return True, None, None def __str__(self): return self.name @@ -1815,29 +2005,25 @@ class OuverturePortList(RevMixin, AclMixin, models.Model): def tcp_ports_in(self): """Renvoie la liste des ports ouverts en TCP IN pour ce profil""" return self.ouvertureport_set.filter( - protocole=OuverturePort.TCP, - io=OuverturePort.IN + protocole=OuverturePort.TCP, io=OuverturePort.IN ) def udp_ports_in(self): """Renvoie la liste des ports ouverts en UDP IN pour ce profil""" return self.ouvertureport_set.filter( - protocole=OuverturePort.UDP, - io=OuverturePort.IN + protocole=OuverturePort.UDP, io=OuverturePort.IN ) def tcp_ports_out(self): """Renvoie la liste des ports ouverts en TCP OUT pour ce profil""" return self.ouvertureport_set.filter( - protocole=OuverturePort.TCP, - io=OuverturePort.OUT + protocole=OuverturePort.TCP, io=OuverturePort.OUT ) def udp_ports_out(self): """Renvoie la liste des ports ouverts en UDP OUT pour ce profil""" return self.ouvertureport_set.filter( - protocole=OuverturePort.UDP, - io=OuverturePort.OUT + protocole=OuverturePort.UDP, io=OuverturePort.OUT ) @@ -1851,32 +2037,17 @@ class OuverturePort(RevMixin, AclMixin, models.Model): On limite les ports entre 0 et 65535, tels que défini par la RFC """ - TCP = 'T' - UDP = 'U' - IN = 'I' - OUT = 'O' + TCP = "T" + UDP = "U" + IN = "I" + OUT = "O" begin = models.PositiveIntegerField(validators=[MaxValueValidator(65535)]) end = models.PositiveIntegerField(validators=[MaxValueValidator(65535)]) - port_list = models.ForeignKey( - 'OuverturePortList', - on_delete=models.CASCADE - ) + port_list = models.ForeignKey("OuverturePortList", on_delete=models.CASCADE) protocole = models.CharField( - max_length=1, - choices=( - (TCP, 'TCP'), - (UDP, 'UDP'), - ), - default=TCP, - ) - io = models.CharField( - max_length=1, - choices=( - (IN, 'IN'), - (OUT, 'OUT'), - ), - default=OUT, + max_length=1, choices=((TCP, "TCP"), (UDP, "UDP")), default=TCP ) + io = models.CharField(max_length=1, choices=((IN, "IN"), (OUT, "OUT")), default=OUT) class Meta: verbose_name = _("ports opening") @@ -1885,7 +2056,7 @@ class OuverturePort(RevMixin, AclMixin, models.Model): def __str__(self): if self.begin == self.end: return str(self.begin) - return ':'.join([str(self.begin), str(self.end)]) + return ":".join([str(self.begin), str(self.end)]) def show_port(self): """Formatage plus joli, alias pour str""" @@ -1896,41 +2067,41 @@ class OuverturePort(RevMixin, AclMixin, models.Model): def machine_post_save(**kwargs): """Synchronisation ldap et régen parefeu/dhcp lors de la modification d'une machine""" - user = kwargs['instance'].user + user = kwargs["instance"].user user.ldap_sync(base=False, access_refresh=False, mac_refresh=True) - regen('dhcp') - regen('mac_ip_list') + regen("dhcp") + regen("mac_ip_list") @receiver(post_delete, sender=Machine) def machine_post_delete(**kwargs): """Synchronisation ldap et régen parefeu/dhcp lors de la suppression d'une machine""" - machine = kwargs['instance'] + machine = kwargs["instance"] user = machine.user user.ldap_sync(base=False, access_refresh=False, mac_refresh=True) - regen('dhcp') - regen('mac_ip_list') + regen("dhcp") + regen("mac_ip_list") @receiver(post_save, sender=Interface) def interface_post_save(**kwargs): """Synchronisation ldap et régen parefeu/dhcp lors de la modification d'une interface""" - interface = kwargs['instance'] + interface = kwargs["instance"] interface.sync_ipv6() user = interface.machine.user user.ldap_sync(base=False, access_refresh=False, mac_refresh=True) # Regen services - regen('dhcp') - regen('mac_ip_list') + regen("dhcp") + regen("mac_ip_list") @receiver(post_delete, sender=Interface) def interface_post_delete(**kwargs): """Synchronisation ldap et régen parefeu/dhcp lors de la suppression d'une interface""" - interface = kwargs['instance'] + interface = kwargs["instance"] user = interface.machine.user user.ldap_sync(base=False, access_refresh=False, mac_refresh=True) @@ -1938,7 +2109,7 @@ def interface_post_delete(**kwargs): @receiver(post_save, sender=IpType) def iptype_post_save(**kwargs): """Generation des objets ip après modification d'un range ip""" - iptype = kwargs['instance'] + iptype = kwargs["instance"] iptype.gen_ip_range() iptype.check_replace_prefixv6() @@ -1947,7 +2118,7 @@ def iptype_post_save(**kwargs): def machinetype_post_save(**kwargs): """Mise à jour des interfaces lorsque changement d'attribution d'une machinetype (changement iptype parent)""" - machinetype = kwargs['instance'] + machinetype = kwargs["instance"] for interface in machinetype.all_interfaces(): interface.update_type() @@ -1955,94 +2126,94 @@ def machinetype_post_save(**kwargs): @receiver(post_save, sender=Domain) def domain_post_save(**_kwargs): """Regeneration dns après modification d'un domain object""" - regen('dns') + regen("dns") @receiver(post_delete, sender=Domain) def domain_post_delete(**_kwargs): """Regeneration dns après suppression d'un domain object""" - regen('dns') + regen("dns") @receiver(post_save, sender=Extension) def extension_post_save(**_kwargs): """Regeneration dns après modification d'une extension""" - regen('dns') + regen("dns") @receiver(post_delete, sender=Extension) def extension_post_selete(**_kwargs): """Regeneration dns après suppression d'une extension""" - regen('dns') + regen("dns") @receiver(post_save, sender=SOA) def soa_post_save(**_kwargs): """Regeneration dns après modification d'un SOA""" - regen('dns') + regen("dns") @receiver(post_delete, sender=SOA) def soa_post_delete(**_kwargs): """Regeneration dns après suppresson d'un SOA""" - regen('dns') + regen("dns") @receiver(post_save, sender=Mx) def mx_post_save(**_kwargs): """Regeneration dns après modification d'un MX""" - regen('dns') + regen("dns") @receiver(post_delete, sender=Mx) def mx_post_delete(**_kwargs): """Regeneration dns après suppresson d'un MX""" - regen('dns') + regen("dns") @receiver(post_save, sender=Ns) def ns_post_save(**_kwargs): """Regeneration dns après modification d'un NS""" - regen('dns') + regen("dns") @receiver(post_delete, sender=Ns) def ns_post_delete(**_kwargs): """Regeneration dns après modification d'un NS""" - regen('dns') + regen("dns") @receiver(post_save, sender=Txt) def text_post_save(**_kwargs): """Regeneration dns après modification d'un TXT""" - regen('dns') + regen("dns") @receiver(post_delete, sender=Txt) def text_post_delete(**_kwargs): """Regeneration dns après modification d'un TX""" - regen('dns') + regen("dns") @receiver(post_save, sender=DName) def dname_post_save(**_kwargs): """Updates the DNS regen after modification of a DName object.""" - regen('dns') + regen("dns") @receiver(post_delete, sender=DName) def dname_post_delete(**_kwargs): """Updates the DNS regen after deletion of a DName object.""" - regen('dns') + regen("dns") @receiver(post_save, sender=Srv) def srv_post_save(**_kwargs): """Regeneration dns après modification d'un SRV""" - regen('dns') + regen("dns") @receiver(post_delete, sender=Srv) def srv_post_delete(**_kwargs): """Regeneration dns après modification d'un SRV""" - regen('dns') + regen("dns") diff --git a/machines/serializers.py b/machines/serializers.py index 3f5fb966..a6c523ab 100644 --- a/machines/serializers.py +++ b/machines/serializers.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -40,7 +40,7 @@ from machines.models import ( Service_link, Ns, OuverturePort, - Ipv6List + Ipv6List, ) @@ -61,7 +61,7 @@ class IpListSerializer(serializers.ModelSerializer): class Meta: model = IpList - fields = ('ipv4', 'ip_type') + fields = ("ipv4", "ip_type") class Ipv6ListSerializer(serializers.ModelSerializer): @@ -69,7 +69,7 @@ class Ipv6ListSerializer(serializers.ModelSerializer): class Meta: model = Ipv6List - fields = ('ipv6', 'slaac_ip') + fields = ("ipv6", "slaac_ip") class InterfaceSerializer(serializers.ModelSerializer): @@ -78,13 +78,13 @@ class InterfaceSerializer(serializers.ModelSerializer): ipv4 = IpListSerializer(read_only=True) # TODO : use serializer.RelatedField to avoid duplicate code - mac_address = serializers.SerializerMethodField('get_macaddress') - domain = serializers.SerializerMethodField('get_dns') - extension = serializers.SerializerMethodField('get_interface_extension') + 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') + fields = ("ipv4", "mac_address", "domain", "extension") @staticmethod def get_dns(obj): @@ -109,13 +109,13 @@ class FullInterfaceSerializer(serializers.ModelSerializer): ipv4 = IpListSerializer(read_only=True) ipv6 = Ipv6ListSerializer(read_only=True, many=True) # TODO : use serializer.RelatedField to avoid duplicate code - mac_address = serializers.SerializerMethodField('get_macaddress') - domain = serializers.SerializerMethodField('get_dns') - extension = serializers.SerializerMethodField('get_interface_extension') + 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') + fields = ("ipv4", "ipv6", "mac_address", "domain", "extension") @staticmethod def get_dns(obj): @@ -148,21 +148,32 @@ class TypeSerializer(serializers.ModelSerializer): get ForeignKey values. Infos about the general port policy is added """ 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') + 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',) + 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", + ) @staticmethod def get_port_policy(obj, protocole, io): @@ -172,9 +183,9 @@ class TypeSerializer(serializers.ModelSerializer): return [] return map( str, - obj.ouverture_ports.ouvertureport_set.filter( - protocole=protocole - ).filter(io=io) + obj.ouverture_ports.ouvertureport_set.filter(protocole=protocole).filter( + io=io + ), ) def get_port_policy_input_tcp(self, obj): @@ -197,13 +208,14 @@ class TypeSerializer(serializers.ModelSerializer): 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') + + 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') + fields = ("name", "origin", "origin_v6", "zone_entry", "soa") @staticmethod def get_origin_ip(obj): @@ -218,19 +230,20 @@ class ExtensionSerializer(serializers.ModelSerializer): @staticmethod def get_soa_data(obj): """ The representation of the associated SOA """ - return {'mail': obj.soa.dns_soa_mail, 'param': obj.soa.dns_soa_param} + 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') + + 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') + fields = ("zone", "priority", "name", "mx_entry") @staticmethod def get_entry_name(obj): @@ -251,12 +264,13 @@ class MxSerializer(serializers.ModelSerializer): 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') + + zone = serializers.SerializerMethodField("get_zone_name") + txt_entry = serializers.SerializerMethodField("get_txt_name") class Meta: model = Txt - fields = ('zone', 'txt_entry', 'field1', 'field2') + fields = ("zone", "txt_entry", "field1", "field2") @staticmethod def get_zone_name(obj): @@ -271,21 +285,22 @@ class TxtSerializer(serializers.ModelSerializer): 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') + + 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' + "service", + "protocole", + "extension", + "ttl", + "priority", + "weight", + "port", + "target", + "srv_entry", ) @staticmethod @@ -302,13 +317,14 @@ class SrvSerializer(serializers.ModelSerializer): 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') + + 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') + fields = ("zone", "ns", "ns_entry") @staticmethod def get_zone_name(obj): @@ -329,13 +345,14 @@ class NsSerializer(serializers.ModelSerializer): 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') + + 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') + fields = ("name", "extension", "cname", "cname_entry") @staticmethod def get_zone_name(obj): @@ -355,13 +372,14 @@ class DomainSerializer(serializers.ModelSerializer): class ServiceServersSerializer(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') + + 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') + fields = ("server", "service", "need_regen") @staticmethod def get_server_name(obj): @@ -381,6 +399,7 @@ class ServiceServersSerializer(serializers.ModelSerializer): class OuverturePortsSerializer(serializers.Serializer): """Serialisation de l'ouverture des ports""" + ipv4 = serializers.SerializerMethodField() ipv6 = serializers.SerializerMethodField() @@ -404,7 +423,8 @@ class OuverturePortsSerializer(serializers.Serializer): "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 + for i in Interface.objects.all() + if i.ipv4 } @staticmethod @@ -417,5 +437,6 @@ class OuverturePortsSerializer(serializers.Serializer): "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 + for i in Interface.objects.all() + if i.ipv6 } diff --git a/machines/templates/machines/aff_alias.html b/machines/templates/machines/aff_alias.html index ee8580b0..8fe7260c 100644 --- a/machines/templates/machines/aff_alias.html +++ b/machines/templates/machines/aff_alias.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -30,12 +30,14 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Aliases" %} + {% trans "TTL" %} {% for alias in alias_list %} {{ alias }} + {{ alias.ttl }} {% can_edit alias %} {% include 'buttons/edit.html' with href='machines:edit-alias' id=alias.id %} diff --git a/machines/templates/machines/aff_dname.html b/machines/templates/machines/aff_dname.html index 6d07a7bc..4073a388 100644 --- a/machines/templates/machines/aff_dname.html +++ b/machines/templates/machines/aff_dname.html @@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Target zone" %} {% trans "Record" %} + {% trans "TTL" %} @@ -36,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {{ dname.zone }} {{ dname.dns_entry }} + {{ dname.ttl }} {% can_edit dname %} {% include 'buttons/edit.html' with href='machines:edit-dname' id=dname.id %} diff --git a/machines/templates/machines/aff_extension.html b/machines/templates/machines/aff_extension.html index 1083b1b1..358fc33e 100644 --- a/machines/templates/machines/aff_extension.html +++ b/machines/templates/machines/aff_extension.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Extension" %} - {% trans "'infra' right required" %} + {% blocktrans %}"infra" right required{% endblocktrans %} {% trans "SOA record" %} {% trans "A record origin" %} {% if ipv6_enabled %} diff --git a/machines/templates/machines/aff_iptype.html b/machines/templates/machines/aff_iptype.html index c30c0c73..7cf710c2 100644 --- a/machines/templates/machines/aff_iptype.html +++ b/machines/templates/machines/aff_iptype.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "IP type" %} {% trans "Extension" %} - {% trans "'infra' right required" %} + {% blocktrans %}"infra" right required{% endblocktrans %} {% trans "IPv4 range" %} {% trans "v6 prefix" %} {% trans "DNSSEC reverse v4/v6" %} @@ -45,7 +45,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% for type in iptype_list %} - {{ type.type }} + {{ type.name }} {{ type.extension }} {{ type.need_infra|tick }} {{ type.domaine_ip_start }}-{{ type.domaine_ip_stop }}{% if type.ip_network %} on diff --git a/machines/templates/machines/aff_ipv6.html b/machines/templates/machines/aff_ipv6.html index f67bf4c3..e27ba3b8 100644 --- a/machines/templates/machines/aff_ipv6.html +++ b/machines/templates/machines/aff_ipv6.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/machines/templates/machines/aff_machines.html b/machines/templates/machines/aff_machines.html index 4363cd6e..77b65546 100644 --- a/machines/templates/machines/aff_machines.html +++ b/machines/templates/machines/aff_machines.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -52,7 +52,12 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "No name" as tr_no_name %} {% trans "View the profile" as tr_view_the_profile %} - {{ machine.get_name|default:tr_no_name }} + {% if machine.active %} + + {% else %} + {% trans "Deactivated" %}: + {% endif %} + {{ machine.get_name|default:tr_no_name }} {{ machine.user }} @@ -83,10 +88,15 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endif %} - {{ interface.type }} + {{ interface.machine_type }} {{ interface.mac_address }} + IPv4 {{ interface.ipv4 }} @@ -113,7 +123,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
  • - {% trans " Edit" %} + {% trans "Edit" %}
  • {% acl_end %} @@ -121,7 +131,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
  • - {% trans " Manage the aliases" %} + {% trans "Manage the aliases" %}
  • {% acl_end %} @@ -129,7 +139,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
  • - {% trans " Manage the IPv6 addresses" %} + {% trans "Manage the IPv6 addresses" %}
  • {% acl_end %} @@ -137,7 +147,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
  • - {% trans " Manage the SSH fingerprints" %} + {% trans "Manage the SSH fingerprints" %}
  • {% acl_end %} @@ -145,7 +155,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
  • - {% trans " Manage the ports configuration" %} + {% trans "Manage the ports configuration" %}
  • {% acl_end %} @@ -158,6 +168,17 @@ with this program; if not, write to the Free Software Foundation, Inc., + + +
    +
      +
    • + {{ interface.get_vendor }} +
    • +
    +
    + + {% if ipv6_enabled and interface.ipv6 != 'None' %} @@ -212,6 +233,12 @@ with this program; if not, write to the Free Software Foundation, Inc., ipv6_div[i].collapse('hide'); } }); + $("#machines_table").ready(function () { + var vendor_div = [{% for machine in machines_list %}{% for interface in machine.interface_set.all %}{% if interface.get_vendor %}$("#collapseVendor_{{ interface.id }}"), {% endif %}{% endfor %}{% endfor %}]; + for (var i = 0; i < vendor_div.length; i++) { + vendor_div[i].collapse('hide'); + } + }); {% if machines_list.paginator %} diff --git a/machines/templates/machines/aff_machinetype.html b/machines/templates/machines/aff_machinetype.html index ebd77b4f..087b27d2 100644 --- a/machines/templates/machines/aff_machinetype.html +++ b/machines/templates/machines/aff_machinetype.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -36,7 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% for type in machinetype_list %} - {{ type.type }} + {{ type.name }} {{ type.ip_type }} {% can_edit type %} diff --git a/machines/templates/machines/aff_mx.html b/machines/templates/machines/aff_mx.html index b10d4eff..f6fe5fd8 100644 --- a/machines/templates/machines/aff_mx.html +++ b/machines/templates/machines/aff_mx.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Concerned zone" %} {% trans "Priority" %} {% trans "Record" %} + {% trans "TTL" %} @@ -40,6 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {{ mx.zone }} {{ mx.priority }} {{ mx.name }} + {{ mx.ttl }} {% can_edit mx %} {% include 'buttons/edit.html' with href='machines:edit-mx' id=mx.id %} diff --git a/machines/templates/machines/aff_nas.html b/machines/templates/machines/aff_nas.html index 7a98a7f4..339ff608 100644 --- a/machines/templates/machines/aff_nas.html +++ b/machines/templates/machines/aff_nas.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/machines/templates/machines/aff_ns.html b/machines/templates/machines/aff_ns.html index dda578f3..96534f8a 100644 --- a/machines/templates/machines/aff_ns.html +++ b/machines/templates/machines/aff_ns.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Concerned zone" %} {% trans "Authoritarian interface for the concerned zone" %} + {% trans "TTL" %} @@ -38,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {{ ns.zone }} {{ ns.ns }} + {{ ns.ttl }} {% can_edit ns %} {% include 'buttons/edit.html' with href='machines:edit-ns' id=ns.id %} diff --git a/machines/templates/machines/aff_role.html b/machines/templates/machines/aff_role.html index 6f285c89..11b923d6 100644 --- a/machines/templates/machines/aff_role.html +++ b/machines/templates/machines/aff_role.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/machines/templates/machines/aff_servers.html b/machines/templates/machines/aff_servers.html index 4134c269..3829c6c1 100644 --- a/machines/templates/machines/aff_servers.html +++ b/machines/templates/machines/aff_servers.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -31,8 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Service name" %} {% trans "Server" %} {% trans "Last regeneration" %} - {% trans "Regeneration required" %} - {% trans "Regeneration activated" %} + {% trans "Regeneration asked" %} + {% trans "Regeneration needed" %} {% for server in servers_list %} diff --git a/machines/templates/machines/aff_service.html b/machines/templates/machines/aff_service.html index d3eb16ba..535ddf63 100644 --- a/machines/templates/machines/aff_service.html +++ b/machines/templates/machines/aff_service.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/machines/templates/machines/aff_soa.html b/machines/templates/machines/aff_soa.html index 50a4a5c3..31905b31 100644 --- a/machines/templates/machines/aff_soa.html +++ b/machines/templates/machines/aff_soa.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/machines/templates/machines/aff_srv.html b/machines/templates/machines/aff_srv.html index 1a699b49..42f66fe5 100644 --- a/machines/templates/machines/aff_srv.html +++ b/machines/templates/machines/aff_srv.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/machines/templates/machines/aff_txt.html b/machines/templates/machines/aff_txt.html index 3da268ca..bb140ce8 100644 --- a/machines/templates/machines/aff_txt.html +++ b/machines/templates/machines/aff_txt.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Concerned zone" %} {% trans "Record" %} + {% trans "TTL" %} @@ -38,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {{ txt.zone }} {{ txt.dns_entry }} + {{ txt.ttl }} {% can_edit txt %} {% include 'buttons/edit.html' with href='machines:edit-txt' id=txt.id %} diff --git a/machines/templates/machines/aff_vlan.html b/machines/templates/machines/aff_vlan.html index 0b10262b..f60424a2 100644 --- a/machines/templates/machines/aff_vlan.html +++ b/machines/templates/machines/aff_vlan.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/machines/templates/machines/delete.html b/machines/templates/machines/delete.html index 59ba2102..3c890c67 100644 --- a/machines/templates/machines/delete.html +++ b/machines/templates/machines/delete.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/machines/templates/machines/edit_portlist.html b/machines/templates/machines/edit_portlist.html index fa9f771a..a2aded23 100644 --- a/machines/templates/machines/edit_portlist.html +++ b/machines/templates/machines/edit_portlist.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -47,11 +47,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    - {% trans "Add a port" as value %} - +

    - {% trans "Create or edit" as tr_create_or_edit %} - {% bootstrap_button tr_create_or_edit icon='ok' button_class='btn-success' %} + {% trans "Confirm" as tr_confirm %} + {% bootstrap_button tr_confirm icon='ok' button_class='btn-success' %} + {% if request.user.shortcuts_enabled %} - + {% endif %} {# Read the documentation for more information #} diff --git a/templates/buttons/add.html b/templates/buttons/add.html index efbbe142..b04381ca 100644 --- a/templates/buttons/add.html +++ b/templates/buttons/add.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/templates/buttons/edit.html b/templates/buttons/edit.html index a169b6c6..c7bbaac4 100644 --- a/templates/buttons/edit.html +++ b/templates/buttons/edit.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/templates/buttons/history.html b/templates/buttons/history.html index cabcd6b2..730023e8 100644 --- a/templates/buttons/history.html +++ b/templates/buttons/history.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load i18n %} - - {% if text %}{% trans 'History' %}{% endif %} + + {% if text %}{% trans "History" %}{% endif %} diff --git a/templates/buttons/multiple_checkbox_alt.html b/templates/buttons/multiple_checkbox_alt.html index 2809d3a2..2e59845f 100644 --- a/templates/buttons/multiple_checkbox_alt.html +++ b/templates/buttons/multiple_checkbox_alt.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/templates/buttons/sort.html b/templates/buttons/sort.html index 3de5b6cd..2f34a2c6 100644 --- a/templates/buttons/sort.html +++ b/templates/buttons/sort.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/templates/buttons/suppr.html b/templates/buttons/suppr.html index 4910db03..1cb6cbe4 100644 --- a/templates/buttons/suppr.html +++ b/templates/buttons/suppr.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/templates/errors/404.html b/templates/errors/404.html index 6edaee64..2f743532 100644 --- a/templates/errors/404.html +++ b/templates/errors/404.html @@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., - + diff --git a/templates/errors/500.html b/templates/errors/500.html index e7148f7b..b15341ee 100644 --- a/templates/errors/500.html +++ b/templates/errors/500.html @@ -38,7 +38,7 @@

    {% blocktrans trimmed %}An email has been automatically sent to the site administrators. Please avoid - spamming them by trigerring the same issue multiple times.{% endblocktrans %}{% blocktrans trimmed %} The mail should + spamming them by trigerring the same issue multiple times.{% endblocktrans %}{% blocktrans trimmed %} The email should contains all the details necessary to understand what went wrong but if your help were needed, you will probably be contacted by them.{% endblocktrans %}

    diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index 2adc5854..80131b1d 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-01-08 23:59+0100\n" +"POT-Creation-Date: 2019-11-19 23:43+0100\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -30,160 +30,11 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: base.html:42 errors/404.html:35 -msgid "Networking managing website endorsed by FedeRez." -msgstr "Site de gestion de réseau soutenu par FedeRez." - -#: base.html:59 -msgid "Home" -msgstr "Accueil" - -#: base.html:80 -msgid "Users" -msgstr "Utilisateurs" - -#: base.html:83 -msgid "Manage the users" -msgstr "Gérer les utilisateurs" - -#: base.html:84 -msgid "Manage the clubs" -msgstr "Gérer les clubs" - -#: base.html:87 -msgid "Manage the machines" -msgstr "Gérer les machines" - -#: base.html:90 -msgid "Manage the subscriptions" -msgstr "Gérer les cotisations" - -#: base.html:97 -msgid "Topology" -msgstr "Topologie" - -#: base.html:99 -msgid "Switches" -msgstr "Commutateurs réseau" - -#: base.html:100 -msgid "Access points" -msgstr "Points d'accès sans fil" - -#: base.html:101 -msgid "Rooms" -msgstr "Chambres" - -#: base.html:106 -msgid "Statistics" -msgstr "Statistiques" - -#: base.html:111 -msgid "Administration" -msgstr "Administration" - -#: base.html:118 -msgid "More information" -msgstr "Plus d'informations" - -#: base.html:120 -msgid "About" -msgstr "À propos" - -#: base.html:121 -msgid "Contact" -msgstr "Contact" - -#: base.html:128 -msgid "Sign up" -msgstr "S'inscrire" - -#: base.html:134 registration/login.html:29 registration/login.html:36 -msgid "Log in" -msgstr "Se connecter" - -#: base.html:142 -msgid "Search" -msgstr "Rechercher" - -#: base.html:156 -msgid "My profile" -msgstr "Mon profil" - -#: base.html:157 -msgid "Log out" -msgstr "Se déconnecter" - -#: base.html:192 -msgid "Username" -msgstr "Pseudo" - -#: base.html:196 -msgid "Room" -msgstr "Chambre" - -#: base.html:200 -msgid "Internet access" -msgstr "Accès Internet" - -#: base.html:203 -#, python-format -msgid "Until %(end_access_date)s" -msgstr "Jusqu'au %(end_access_date)s" - -#: base.html:205 -msgid "Disabled" -msgstr "Désactivé" - -#: base.html:210 -msgid "Membership" -msgstr "Adhésion" - -#: base.html:213 -#, python-format -msgid "Until %(end_adhesion_date)s" -msgstr "Jusqu'au %(end_adhesion_date)s" - -#: base.html:215 -msgid "Non member" -msgstr "Non adhérent" - -#: base.html:223 -msgid "View my profile" -msgstr "Voir mon profil" - -#: base.html:228 -msgid "You are not logged in." -msgstr "Vous n'êtes pas connecté." - -#: base.html:235 -#, python-format -msgid "%(nb)s active machine" -msgid_plural "%(nb)s active machines" -msgstr[0] "%(nb)s machine active" -msgstr[1] "%(nb)s machines actives" - -#: base.html:244 -msgid "View my machines" -msgstr "Voir mes machines" - -#: base.html:257 -msgid "Back to top" -msgstr "Retour en haut" - -#: base.html:259 +#: templates/admin/base_site.html:65 templates/base.html:288 msgid "powered by" msgstr "propulsé par" -#: base.html:261 -msgid "Brought to you with ." -msgstr "Codé avec ." - -#: base.html:264 -msgid "About this website" -msgstr "À propos de ce site" - -#: base.html:267 +#: templates/admin/base_site.html:69 templates/base.html:296 msgid "" "This software is under the terms of the GPLv2 License." @@ -191,63 +42,253 @@ msgstr "" "Ce logiciel est sous les termes de la licence GPLv2." -#: buttons/add.html:27 -msgid "Add" -msgstr "Ajouter" +#: templates/admin/custom_index.html:11 +#, python-format +msgid "Welcome to %(name_website)s" +msgstr "Bienvenue sur %(name_website)s" -#: buttons/edit.html:27 +#: templates/admin/custom_index.html:17 +msgid "" +"You are on the operator interface. Here you will be able to manage the " +"network and users from the top left menu. You can also go read the developer " +"documentation." +msgstr "" +"Vous êtes dans l'interface opérateur. Ici vous pourrez gérer le réseau et " +"les utilisateurs depuis le menu en haut à gauche. Vous pouvez aussi lire la " +"documentation du développeur." + +#: templates/admin/custom_index.html:20 +msgid "" +"To go back to the main site, click \"View site\" button in top right menu." +msgstr "" +"Pour revenir au site principal, cliquez sur le bouton « Voir le site » dans " +"le menu en haut à droite." + +#: templates/admin/custom_index.html:28 +msgid "My account" +msgstr "Mon compte" + +#: templates/admin/custom_index.html:29 +msgid "Recent actions" +msgstr "" + +#: templates/admin/custom_index.html:33 +msgid "None available" +msgstr "" + +#: templates/admin/custom_index.html:48 +msgid "Unknown content" +msgstr "" + +#: templates/base.html:70 templates/registration/logged_out.html:11 +#: templates/registration/password_change_done.html:11 +#: templates/registration/password_change_form.html:11 +#: templates/registration/password_reset_complete.html:11 +#: templates/registration/password_reset_confirm.html:11 +#: templates/registration/password_reset_done.html:11 +#: templates/registration/password_reset_form.html:11 +msgid "Home" +msgstr "" + +#: templates/base.html:91 +msgid "Users" +msgstr "Utilisateurs" + +#: templates/base.html:94 +msgid "Manage the users" +msgstr "Gérer les utilisateurs" + +#: templates/base.html:95 +msgid "Manage the clubs" +msgstr "Gérer les clubs" + +#: templates/base.html:98 +msgid "Manage the machines" +msgstr "Gérer les machines" + +#: templates/base.html:101 +msgid "Manage the subscriptions" +msgstr "Gérer les cotisations" + +#: templates/base.html:114 +msgid "Topology" +msgstr "Topologie" + +#: templates/base.html:116 +msgid "Switches" +msgstr "Commutateurs réseau" + +#: templates/base.html:117 +msgid "Access points" +msgstr "Points d'accès sans fil" + +#: templates/base.html:118 +msgid "Rooms" +msgstr "Chambres" + +#: templates/base.html:128 +msgid "Statistics" +msgstr "Statistiques" + +#: templates/base.html:133 +msgid "Administration" +msgstr "" + +#: templates/base.html:140 +msgid "Information and contact" +msgstr "Informations et contact" + +#: templates/base.html:142 +msgid "About" +msgstr "À propos" + +#: templates/base.html:143 +msgid "Contact" +msgstr "Contact" + +#: templates/base.html:157 +msgid "Sign up" +msgstr "S'inscrire" + +#: templates/base.html:163 templates/registration/login.html:29 +#: templates/registration/login.html:36 +msgid "Log in" +msgstr "" + +#: templates/base.html:171 +msgid "Search" +msgstr "" + +#: templates/base.html:185 +msgid "My profile" +msgstr "Mon profil" + +#: templates/base.html:186 +msgid "Log out" +msgstr "" + +#: templates/base.html:221 +msgid "Username" +msgstr "Pseudo" + +#: templates/base.html:225 +msgid "Room" +msgstr "Chambre" + +#: templates/base.html:229 +msgid "Internet access" +msgstr "Accès Internet" + +#: templates/base.html:232 +#, python-format +msgid "Until %(end_access_date)s" +msgstr "Jusqu'au %(end_access_date)s" + +#: templates/base.html:234 +msgid "Disabled" +msgstr "Désactivé" + +#: templates/base.html:239 +msgid "Membership" +msgstr "Adhésion" + +#: templates/base.html:242 +#, python-format +msgid "Until %(end_adhesion_date)s" +msgstr "Jusqu'au %(end_adhesion_date)s" + +#: templates/base.html:244 +msgid "Non member" +msgstr "Non adhérent" + +#: templates/base.html:252 +msgid "View my profile" +msgstr "Voir mon profil" + +#: templates/base.html:257 +msgid "You are not logged in." +msgstr "Vous n'êtes pas connecté." + +#: templates/base.html:264 +#, python-format +msgid "%(nb)s active machine" +msgid_plural "%(nb)s active machines" +msgstr[0] "%(nb)s machine active" +msgstr[1] "%(nb)s machines actives" + +#: templates/base.html:273 +msgid "View my machines" +msgstr "Voir mes machines" + +#: templates/base.html:286 +msgid "Back to top" +msgstr "Retour en haut" + +#: templates/base.html:290 +msgid "Brought to you with ." +msgstr "Codé avec ." + +#: templates/base.html:293 +msgid "About this website" +msgstr "À propos de ce site" + +#: templates/buttons/add.html:27 +msgid "Add" +msgstr "" + +#: templates/buttons/edit.html:27 msgid "Edit" msgstr "Modifier" -#: buttons/history.html:26 buttons/history.html:27 +#: templates/buttons/history.html:26 templates/buttons/history.html:27 msgid "History" -msgstr "Historique" +msgstr "" -#: buttons/setlang.html:34 +#: templates/buttons/setlang.html:34 msgid "Translation in development" msgstr "Traduction en développement" -#: buttons/sort.html:35 +#: templates/buttons/sort.html:35 msgid "Ascending sort" msgstr "Tri croissant" -#: buttons/sort.html:36 +#: templates/buttons/sort.html:36 msgid "Descending sort" msgstr "Tri décroissant" -#: buttons/suppr.html:27 +#: templates/buttons/suppr.html:27 msgid "Delete" -msgstr "Supprimer" +msgstr "" -#: errors/404.html:39 +#: templates/errors/404.html:39 msgid "404 error: page not found" msgstr "Erreur 404 : page non trouvée" -#: errors/404.html:125 +#: templates/errors/404.html:125 msgid "Score: " msgstr "Score : " -#: errors/404.html:133 +#: templates/errors/404.html:133 msgid "YOU LOST" msgstr "VOUS AVEZ PERDU" -#: errors/404.html:220 +#: templates/errors/404.html:220 msgid "Yup, that's a 404 error." msgstr "Yep, c'est une erreur 404." -#: errors/404.html:220 +#: templates/errors/404.html:220 msgid "(Go back to a safe page)" msgstr "(Retourner à une page sécurisée)" -#: errors/404.html:222 +#: templates/errors/404.html:222 msgid "Your browser does not support the HTML5 canvas tag." msgstr "Votre navigateur ne supporte pas la balise HTML5 canvas." -#: errors/500.html:6 errors/500.html:31 +#: templates/errors/500.html:6 templates/errors/500.html:31 msgid "500 error: Re2o internal server error" msgstr "Erreur 500 : Erreur interne du serveur Re2o" -#: errors/500.html:34 +#: templates/errors/500.html:34 msgid "" "Congratulations! You have discovered a bug on Re2o and you've reached a page " "we try to hide, you can be proud of youself. We try to track those bugs down " @@ -260,7 +301,7 @@ msgstr "" "en avons raté un. Nous vous remercions sincèrement pour votre aide: ce n'est " "pas si facile de tous les attraper." -#: errors/500.html:40 +#: templates/errors/500.html:40 msgid "" "An email has been automatically sent to the site administrators. Please " "avoid spamming them by trigerring the same issue multiple times." @@ -269,16 +310,16 @@ msgstr "" "site. Veuillez éviter de déclencher la même erreur de multiples fois pour ne " "pas renvoyer de courrier électronique." -#: errors/500.html:41 +#: templates/errors/500.html:41 msgid "" -"The mail should contains all the details necessary to understand what went " +"The email should contains all the details necessary to understand what went " "wrong but if your help were needed, you will probably be contacted by them." msgstr "" " Le courrier électronique devrait contenir tous les détails nécessaires à la " "compréhension de ce qui s'est mal passé mais si votre aide était requise, " "vous serez probablement contacté par eux." -#: errors/500.html:46 +#: templates/errors/500.html:46 msgid "" "This issue will be fixed as soon as possible but please take into " "consideration the administrators may not be always available. If your " @@ -290,30 +331,73 @@ msgstr "" "temps disponibles. Si votre requête est vraiment urgente, informez votre " "association locale, elle vous aidra à corriger temporairement l'erreur." -#: errors/500.html:54 +#: templates/errors/500.html:54 msgid "If you have no idea what you've done:" msgstr "Si vous n'avez aucune idée de ce que vous avez fait :" -#: errors/500.html:55 +#: templates/errors/500.html:55 msgid "Go back to a safe page" msgstr "Retourner à une page sécurisée" -#: pagination.html:34 +#: templates/pagination.html:34 msgid "First" msgstr "Première page" -#: pagination.html:40 +#: templates/pagination.html:40 msgid "Previous" msgstr "Précédent" -#: pagination.html:60 +#: templates/pagination.html:60 msgid "Next" msgstr "Suivant" -#: pagination.html:66 +#: templates/pagination.html:66 msgid "Last" msgstr "Dernière page" -#: registration/login.html:40 +#: templates/registration/logged_out.html:16 +msgid "Thanks for spending some quality time with the Web site today." +msgstr "" + +#: templates/registration/logged_out.html:17 +msgid "Log in again" +msgstr "" + +#: templates/registration/login.html:40 msgid "Forgotten password?" msgstr "Mot de passe oublié ?" + +#: templates/registration/password_change_done.html:11 +#: templates/registration/password_change_form.html:11 +msgid "Password change" +msgstr "" + +#: templates/registration/password_reset_complete.html:11 +#: templates/registration/password_reset_done.html:11 +#: templates/registration/password_reset_form.html:11 +msgid "Password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:11 +msgid "Password reset confirmation" +msgstr "" + +#: templates/registration/password_reset_email.html:2 +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" + +#: templates/registration/password_reset_email.html:4 +msgid "Please go to the following page and choose a new password:" +msgstr "" + +#: templates/registration/password_reset_email.html:9 +msgid "Thanks for using our site!" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +#, python-format +msgid "The %(site_name)s team" +msgstr "" diff --git a/templates/pagination.html b/templates/pagination.html index 5ecced6d..402f61be 100644 --- a/templates/pagination.html +++ b/templates/pagination.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -23,19 +23,20 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load url_insert_param %} +{% load pagination_extra %} {% load i18n %} {% if list.paginator.num_pages > 1 %} + {% endif %} diff --git a/templates/registration/logged_out.html b/templates/registration/logged_out.html new file mode 100644 index 00000000..dc4845bf --- /dev/null +++ b/templates/registration/logged_out.html @@ -0,0 +1,18 @@ +{% extends "registration/logged_out.html" %} +{% comment %} +SPDX-License-Identifier: GPL-2.0-or-later + +Copyright © 2019 Alexandre Iooss +{% endcomment %} +{% load i18n %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block content %} +

    {% trans "Thanks for spending some quality time with the Web site today." %}

    +

    {% trans "Log in again" %}

    +{% endblock %} diff --git a/templates/registration/login.html b/templates/registration/login.html index f4226d7d..47e8b25f 100644 --- a/templates/registration/login.html +++ b/templates/registration/login.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/templates/registration/password_change_done.html b/templates/registration/password_change_done.html new file mode 100644 index 00000000..a0f46597 --- /dev/null +++ b/templates/registration/password_change_done.html @@ -0,0 +1,13 @@ +{% extends "registration/password_change_done.html" %} +{% comment %} +SPDX-License-Identifier: GPL-2.0-or-later + +Copyright © 2019 Alexandre Iooss +{% endcomment %} +{% load i18n %} + +{% block breadcrumbs %} + +{% endblock %} diff --git a/templates/registration/password_change_form.html b/templates/registration/password_change_form.html new file mode 100644 index 00000000..56dcb6c1 --- /dev/null +++ b/templates/registration/password_change_form.html @@ -0,0 +1,13 @@ +{% extends "registration/password_change_form.html" %} +{% comment %} +SPDX-License-Identifier: GPL-2.0-or-later + +Copyright © 2019 Alexandre Iooss +{% endcomment %} +{% load i18n %} + +{% block breadcrumbs %} + +{% endblock %} diff --git a/templates/registration/password_reset_complete.html b/templates/registration/password_reset_complete.html new file mode 100644 index 00000000..4182b519 --- /dev/null +++ b/templates/registration/password_reset_complete.html @@ -0,0 +1,13 @@ +{% extends "registration/password_reset_complete.html" %} +{% comment %} +SPDX-License-Identifier: GPL-2.0-or-later + +Copyright © 2019 Alexandre Iooss +{% endcomment %} +{% load i18n %} + +{% block breadcrumbs %} + +{% endblock %} diff --git a/templates/registration/password_reset_confirm.html b/templates/registration/password_reset_confirm.html new file mode 100644 index 00000000..b25773a8 --- /dev/null +++ b/templates/registration/password_reset_confirm.html @@ -0,0 +1,13 @@ +{% extends "registration/password_reset_confirm.html" %} +{% comment %} +SPDX-License-Identifier: GPL-2.0-or-later + +Copyright © 2019 Alexandre Iooss +{% endcomment %} +{% load i18n %} + +{% block breadcrumbs %} + +{% endblock %} diff --git a/templates/registration/password_reset_done.html b/templates/registration/password_reset_done.html new file mode 100644 index 00000000..f1609de6 --- /dev/null +++ b/templates/registration/password_reset_done.html @@ -0,0 +1,13 @@ +{% extends "registration/password_reset_done.html" %} +{% comment %} +SPDX-License-Identifier: GPL-2.0-or-later + +Copyright © 2019 Alexandre Iooss +{% endcomment %} +{% load i18n %} + +{% block breadcrumbs %} + +{% endblock %} diff --git a/templates/registration/password_reset_email.html b/templates/registration/password_reset_email.html new file mode 100644 index 00000000..f43d80c3 --- /dev/null +++ b/templates/registration/password_reset_email.html @@ -0,0 +1,13 @@ +{% load i18n %}{% autoescape off %} +{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %} + +{% trans "Please go to the following page and choose a new password:" %} +{% block reset_link %} +{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %} +{% endblock %} + +{% trans "Thanks for using our site!" %} + +{% blocktrans %}The {{ site_name }} team{% endblocktrans %} + +{% endautoescape %} diff --git a/templates/registration/password_reset_form.html b/templates/registration/password_reset_form.html new file mode 100644 index 00000000..d02d98e5 --- /dev/null +++ b/templates/registration/password_reset_form.html @@ -0,0 +1,13 @@ +{% extends "registration/password_reset_form.html" %} +{% comment %} +SPDX-License-Identifier: GPL-2.0-or-later + +Copyright © 2019 Alexandre Iooss +{% endcomment %} +{% load i18n %} + +{% block breadcrumbs %} + +{% endblock %} diff --git a/test_utils/runner.py b/test_utils/runner.py index 54ddd82f..6cdd4cae 100644 --- a/test_utils/runner.py +++ b/test_utils/runner.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -34,87 +34,137 @@ from users.models import LdapUser, LdapUserGroup, LdapServiceUser, LdapServiceUs # The path of this file __here = os.path.dirname(os.path.realpath(__file__)) # The absolute path where to find the schemas for the LDAP -schema_path = os.path.abspath(os.path.join(__here, 'ldap', 'schema')) +schema_path = os.path.abspath(os.path.join(__here, "ldap", "schema")) # The absolute path of the "radius.schema" file -radius_schema_path = os.path.join(schema_path, 'radius.schema') +radius_schema_path = os.path.join(schema_path, "radius.schema") # The absolute path of the "samba.schema" file -samba_schema_path = os.path.join(schema_path, 'samba.schema') +samba_schema_path = os.path.join(schema_path, "samba.schema") # The suffix for the LDAP -suffix = 'dc=example,dc=net' +suffix = "dc=example,dc=net" # The admin CN of the LDAP -rootdn = 'cn=admin,'+suffix +rootdn = "cn=admin," + suffix # Defines all ldap_entry mandatory for Re2o under a key-value list format # that can be used directly by volatildap. For more on how to generate this # data, see https://gitlab.federez.net/re2o/scripts/blob/master/print_ldap_entries.py -ldapentry_Utilisateurs = ('cn=Utilisateurs,'+suffix, { - 'cn': ['Utilisateurs'], - 'sambaSID': ['500'], - 'uid': ['Users'], - 'objectClass': ['posixGroup', 'top', 'sambaSamAccount', 'radiusprofile'], - 'gidNumber': ['500'], -}) -ldapentry_groups = ('ou=groups,'+suffix, { - 'ou': ['groups'], - 'objectClass': ['organizationalUnit'], - 'description': ["Groupes d'utilisateurs"], -}) -ldapentry_services = ('ou=services,ou=groups,'+suffix, { - 'ou': ['services'], - 'objectClass': ['organizationalUnit'], - 'description': ['Groupes de comptes techniques'], -}) -ldapentry_service_users = ('ou=service-users,'+suffix, { - 'ou': ['service-users'], - 'objectClass': ['organizationalUnit'], - 'description': ["Utilisateurs techniques de l'annuaire"], -}) -ldapentry_freeradius = ('cn=freeradius,ou=service-users,'+suffix, { - 'cn': ['freeradius'], - 'objectClass': ['applicationProcess', 'simpleSecurityObject'], - 'userPassword': ['FILL_IT'], -}) -ldapentry_nssauth = ('cn=nssauth,ou=service-users,'+suffix, { - 'cn': ['nssauth'], - 'objectClass': ['applicationProcess', 'simpleSecurityObject'], - 'userPassword': ['FILL_IT'], -}) -ldapentry_auth = ('cn=auth,ou=services,ou=groups,'+suffix, { - 'cn': ['auth'], - 'objectClass': ['groupOfNames'], - 'member': ['cn=nssauth,ou=service-users,'+suffix], -}) -ldapentry_posix = ('ou=posix,ou=groups,'+suffix, { - 'ou': ['posix'], - 'objectClass': ['organizationalUnit'], - 'description': ['Groupes de comptes POSIX'], -}) -ldapentry_wifi = ('cn=wifi,ou=service-users,'+suffix, { - 'cn': ['wifi'], - 'objectClass': ['applicationProcess', 'simpleSecurityObject'], - 'userPassword': ['FILL_IT'], -}) -ldapentry_usermgmt = ('cn=usermgmt,ou=services,ou=groups,'+suffix, { - 'cn': ['usermgmt'], - 'objectClass': ['groupOfNames'], - 'member': ['cn=wifi,ou=service-users,'+suffix], -}) -ldapentry_replica = ('cn=replica,ou=service-users,'+suffix, { - 'cn': ['replica'], - 'objectClass': ['applicationProcess', 'simpleSecurityObject'], - 'userPassword': ['FILL_IT'], -}) -ldapentry_readonly = ('cn=readonly,ou=services,ou=groups,'+suffix, { - 'cn': ['readonly'], - 'objectClass': ['groupOfNames'], - 'member': ['cn=replica,ou=service-users,'+suffix, 'cn=freeradius,ou=service-users,'+suffix], -}) -ldapbasic = dict([ldapentry_Utilisateurs, ldapentry_groups, - ldapentry_services, ldapentry_service_users, - ldapentry_freeradius, ldapentry_nssauth, ldapentry_auth, - ldapentry_posix, ldapentry_wifi, ldapentry_usermgmt, - ldapentry_replica, ldapentry_readonly]) +ldapentry_Utilisateurs = ( + "cn=Utilisateurs," + suffix, + { + "cn": ["Utilisateurs"], + "sambaSID": ["500"], + "uid": ["Users"], + "objectClass": ["posixGroup", "top", "sambaSamAccount", "radiusprofile"], + "gidNumber": ["500"], + }, +) +ldapentry_groups = ( + "ou=groups," + suffix, + { + "ou": ["groups"], + "objectClass": ["organizationalUnit"], + "description": ["Groupes d'utilisateurs"], + }, +) +ldapentry_services = ( + "ou=services,ou=groups," + suffix, + { + "ou": ["services"], + "objectClass": ["organizationalUnit"], + "description": ["Groupes de comptes techniques"], + }, +) +ldapentry_service_users = ( + "ou=service-users," + suffix, + { + "ou": ["service-users"], + "objectClass": ["organizationalUnit"], + "description": ["Utilisateurs techniques de l'annuaire"], + }, +) +ldapentry_freeradius = ( + "cn=freeradius,ou=service-users," + suffix, + { + "cn": ["freeradius"], + "objectClass": ["applicationProcess", "simpleSecurityObject"], + "userPassword": ["FILL_IT"], + }, +) +ldapentry_nssauth = ( + "cn=nssauth,ou=service-users," + suffix, + { + "cn": ["nssauth"], + "objectClass": ["applicationProcess", "simpleSecurityObject"], + "userPassword": ["FILL_IT"], + }, +) +ldapentry_auth = ( + "cn=auth,ou=services,ou=groups," + suffix, + { + "cn": ["auth"], + "objectClass": ["groupOfNames"], + "member": ["cn=nssauth,ou=service-users," + suffix], + }, +) +ldapentry_posix = ( + "ou=posix,ou=groups," + suffix, + { + "ou": ["posix"], + "objectClass": ["organizationalUnit"], + "description": ["Groupes de comptes POSIX"], + }, +) +ldapentry_wifi = ( + "cn=wifi,ou=service-users," + suffix, + { + "cn": ["wifi"], + "objectClass": ["applicationProcess", "simpleSecurityObject"], + "userPassword": ["FILL_IT"], + }, +) +ldapentry_usermgmt = ( + "cn=usermgmt,ou=services,ou=groups," + suffix, + { + "cn": ["usermgmt"], + "objectClass": ["groupOfNames"], + "member": ["cn=wifi,ou=service-users," + suffix], + }, +) +ldapentry_replica = ( + "cn=replica,ou=service-users," + suffix, + { + "cn": ["replica"], + "objectClass": ["applicationProcess", "simpleSecurityObject"], + "userPassword": ["FILL_IT"], + }, +) +ldapentry_readonly = ( + "cn=readonly,ou=services,ou=groups," + suffix, + { + "cn": ["readonly"], + "objectClass": ["groupOfNames"], + "member": [ + "cn=replica,ou=service-users," + suffix, + "cn=freeradius,ou=service-users," + suffix, + ], + }, +) +ldapbasic = dict( + [ + ldapentry_Utilisateurs, + ldapentry_groups, + ldapentry_services, + ldapentry_service_users, + ldapentry_freeradius, + ldapentry_nssauth, + ldapentry_auth, + ldapentry_posix, + ldapentry_wifi, + ldapentry_usermgmt, + ldapentry_replica, + ldapentry_readonly, + ] +) class DiscoverLdapRunner(DiscoverRunner): @@ -133,28 +183,35 @@ class DiscoverLdapRunner(DiscoverRunner): suffix=suffix, rootdn=rootdn, initial_data=ldapbasic, - schemas=['core.schema', 'cosine.schema', 'inetorgperson.schema', - 'nis.schema', radius_schema_path, samba_schema_path] + schemas=[ + "core.schema", + "cosine.schema", + "inetorgperson.schema", + "nis.schema", + radius_schema_path, + samba_schema_path, + ], ) def __init__(self, *args, **kwargs): - settings.DATABASES['ldap']['USER'] = self.ldap_server.rootdn - settings.DATABASES['ldap']['PASSWORD'] = self.ldap_server.rootpw - settings.DATABASES['ldap']['NAME'] = self.ldap_server.uri - settings.LDAP['base_user_dn'] = ldapentry_Utilisateurs[0] - settings.LDAP['base_userservice_dn'] = ldapentry_service_users[0] - settings.LDAP['base_usergroup_dn'] = ldapentry_posix[0] - settings.LDAP['base_userservicegroup_dn'] = ldapentry_services[0] - settings.LDAP['user_gid'] = ldapentry_Utilisateurs[1].get('gidNumber', ["500"])[0] - LdapUser.base_dn = settings.LDAP['base_user_dn'] - LdapUserGroup.base_dn = settings.LDAP['base_usergroup_dn'] - LdapServiceUser.base_dn = settings.LDAP['base_userservice_dn'] - LdapServiceUserGroup.base_dn = settings.LDAP['base_userservicegroup_dn'] + settings.DATABASES["ldap"]["USER"] = self.ldap_server.rootdn + settings.DATABASES["ldap"]["PASSWORD"] = self.ldap_server.rootpw + settings.DATABASES["ldap"]["NAME"] = self.ldap_server.uri + settings.LDAP["base_user_dn"] = ldapentry_Utilisateurs[0] + settings.LDAP["base_userservice_dn"] = ldapentry_service_users[0] + settings.LDAP["base_usergroup_dn"] = ldapentry_posix[0] + settings.LDAP["base_userservicegroup_dn"] = ldapentry_services[0] + settings.LDAP["user_gid"] = ldapentry_Utilisateurs[1].get("gidNumber", ["500"])[ + 0 + ] + LdapUser.base_dn = settings.LDAP["base_user_dn"] + LdapUserGroup.base_dn = settings.LDAP["base_usergroup_dn"] + LdapServiceUser.base_dn = settings.LDAP["base_userservice_dn"] + LdapServiceUserGroup.base_dn = settings.LDAP["base_userservicegroup_dn"] super(DiscoverLdapRunner, self).__init__(*args, **kwargs) - def setup_databases(self, *args, **kwargs): - ret = super(DiscoverLdapRunner, self).setup_databases(*args, **kwargs) + ret = super(DiscoverLdapRunner, self).setup_databases(*args, **kwargs) print("Creating test LDAP with volatildap...") self.ldap_server.start() return ret @@ -163,4 +220,3 @@ class DiscoverLdapRunner(DiscoverRunner): self.ldap_server.stop() print("Destroying test LDAP...") super(DiscoverLdapRunner, self).teardown_databases(*args, **kwargs) - diff --git a/tickets/__init__.py b/tickets/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tickets/admin.py b/tickets/admin.py new file mode 100644 index 00000000..6a20b775 --- /dev/null +++ b/tickets/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin +from .models import Ticket + +admin.site.register(Ticket) +# Register your models here. diff --git a/tickets/apps.py b/tickets/apps.py new file mode 100644 index 00000000..295e4a1f --- /dev/null +++ b/tickets/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class TicketsConfig(AppConfig): + name = "tickets" diff --git a/tickets/forms.py b/tickets/forms.py new file mode 100644 index 00000000..00edb0ec --- /dev/null +++ b/tickets/forms.py @@ -0,0 +1,25 @@ +from django import forms +from django.forms import ModelForm, Form +from re2o.field_permissions import FieldPermissionFormMixin +from re2o.mixins import FormRevMixin +from django.utils.translation import ugettext_lazy as _ + +from .models import Ticket + + +class NewTicketForm(ModelForm): + """ Creation of a ticket""" + + email = forms.EmailField(required=False) + + class Meta: + model = Ticket + fields = ["title", "description", "email"] + + +class ChangeStatusTicketForm(ModelForm): + """ Change ticket status""" + + class Meta: + model = Ticket + fields = [] diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 00000000..1ccfe87f --- /dev/null +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,300 @@ +# 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. +msgid "" +msgstr "" +"Project-Id-Version: 2.5\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-11-20 01:24+0100\n" +"PO-Revision-Date: 2019-11-16 00:35+0100\n" +"Last-Translator: Laouen Fernet \n" +"Language-Team: \n" +"Language: fr_FR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: tickets/models.py:28 +msgid "Title of the ticket." +msgstr "Titre du ticket." + +#: tickets/models.py:32 +msgid "Description of the ticket." +msgstr "Description du ticket." + +#: tickets/models.py:38 +msgid "An email address to get back to you." +msgstr "Une adresse mail pour vous recontacter." + +#: tickets/models.py:43 +msgid "Can view a ticket object" +msgstr "Peut voir un objet ticket" + +#: tickets/models.py:44 +msgid "ticket" +msgstr "ticket" + +#: tickets/models.py:45 +msgid "tickets" +msgstr "tickets" + +#: tickets/models.py:49 +#, python-format +msgid "Ticket from %(name)s. Date: %(date)s." +msgstr "Ticket de %(name)s. Date : %(date)s." + +#: tickets/models.py:51 +#, python-format +msgid "Anonymous ticket. Date: %s." +msgstr "Ticket anonyme. Date : %s." + +#: tickets/models.py:82 +msgid "You don't have the right to view other tickets than yours." +msgstr "Vous n'avez pas le droit de voir d'autres tickets que les vôtres." + +#: tickets/models.py:94 +msgid "You don't have the right to view the list of tickets." +msgstr "Vous n'avez pas le droit de voir la liste des tickets." + +#: tickets/preferences/models.py:10 +msgid "" +"Email address to publish the new tickets (leave empty for no publication)." +msgstr "" +"Adresse mail où publier les nouveaux tickets (laissez vide pour ne pas " +"publier)." + +#: tickets/preferences/models.py:17 +msgid "French" +msgstr "Français" + +#: tickets/preferences/models.py:17 +msgid "English" +msgstr "Anglais" + +#: tickets/preferences/models.py:21 +msgid "tickets preferences" +msgstr "préférences de tickets" + +#: tickets/templates/tickets/aff_ticket.html:30 +#: tickets/templates/tickets/contact.html:4 +#: tickets/templates/tickets/index.html:29 +#: tickets/templates/tickets/preferences.html:6 +#: tickets/templates/tickets/profil.html:6 +msgid "Tickets" +msgstr "Tickets" + +#: tickets/templates/tickets/aff_ticket.html:34 +#, python-format +msgid "Ticket #%(id)s" +msgstr "Ticket #%(id)s" + +#: tickets/templates/tickets/aff_ticket.html:36 +#: tickets/templates/tickets/aff_tickets.html:58 +msgid "Solved" +msgstr "Résolu" + +#: tickets/templates/tickets/aff_ticket.html:38 +msgid "Not solved" +msgstr "Non résolu" + +#: tickets/templates/tickets/aff_ticket.html:44 +msgid "Opened by" +msgstr "Ouvert par" + +#: tickets/templates/tickets/aff_ticket.html:50 +msgid "Anonymous user" +msgstr "Utilisateur anonyme" + +#: tickets/templates/tickets/aff_ticket.html:54 +msgid "Response address: " +msgstr "Adresse de réponse : " + +#: tickets/templates/tickets/aff_ticket.html:54 +msgid "Response to your ticket" +msgstr "Réponse à votre ticket" + +#: tickets/templates/tickets/aff_ticket.html:59 +msgid "Title:" +msgstr "Titre :" + +#: tickets/templates/tickets/aff_ticket.html:60 +msgid "Description:" +msgstr "Description :" + +#: tickets/templates/tickets/aff_ticket.html:68 +msgid "Mark as solved" +msgstr "Marquer comme résolu" + +#: tickets/templates/tickets/aff_ticket.html:71 +msgid "Mark as not solved" +msgstr "Marquer comme non résolu" + +#: tickets/templates/tickets/aff_ticket.html:81 +msgid "All tickets" +msgstr "Tous les tickets" + +#: tickets/templates/tickets/aff_tickets.html:35 +#: tickets/templates/tickets/form_preferences.html:30 +#: tickets/templates/tickets/form_ticket.html:31 +msgid "Ticket" +msgid_plural "Tickets" +msgstr[0] "Ticket" +msgstr[1] "Tickets" + +#: tickets/templates/tickets/aff_tickets.html:38 +msgid "Ticket not solved" +msgid_plural "Tickets not solved" +msgstr[0] "Ticket non résolu" +msgstr[1] "Tickets non résolus" + +#: tickets/templates/tickets/aff_tickets.html:41 +msgid "Last ticket:" +msgstr "Dernier ticket :" + +#: tickets/templates/tickets/aff_tickets.html:55 +msgid "User" +msgstr "Utilisateur" + +#: tickets/templates/tickets/aff_tickets.html:56 +msgid "Title" +msgstr "Titre" + +#: tickets/templates/tickets/aff_tickets.html:57 +msgid "Date" +msgstr "Date" + +#: tickets/templates/tickets/aff_tickets.html:70 +msgid "Anonymous" +msgstr "Anonyme" + +#: tickets/templates/tickets/contact.html:8 +#, python-format +msgid "" +"If you are experiencing issues with the services offered by %(asso_name)s, " +"you can open a ticket that will be taken care of. If you want to contact us " +"on any other topic, please choose one address below." +msgstr "" +"Si vous rencontrez des problèmes avec les services proposés par " +"%(asso_name)s, vous pouvez ouvrir un ticket qui sera pris en compte. Si vous " +"voulez nous contacter pour n'importe quel autre sujet, veuillez choisir une " +"adresse ci-dessous." + +#: tickets/templates/tickets/contact.html:10 +#: tickets/templates/tickets/navbar_logout.html:4 +#: tickets/templates/tickets/profil.html:12 +msgid "Open a ticket" +msgstr "Ouvrir un ticket" + +#: tickets/templates/tickets/form_preferences.html:33 +msgid "Editing of tickets preferences" +msgstr "Modification des préférences de tickets" + +#: tickets/templates/tickets/form_preferences.html:46 +#: tickets/templates/tickets/preferences.html:14 +msgid "Edit" +msgstr "Modifier" + +#: tickets/templates/tickets/form_ticket.html:34 +msgid "Ticket opening" +msgstr "Ouverture de ticket" + +#: tickets/templates/tickets/form_ticket.html:39 tickets/views.py:88 +msgid "" +"You are not authenticated. Please log in or provide an email address so we " +"can get back to you." +msgstr "" +"Vous n'êtes pas authentifié. Veuillez vous connecter ou fournir une adresse " +"mail pour que nous puissions vous recontacter." + +#: tickets/templates/tickets/form_ticket.html:44 +msgid "" +"Description of your problem. Please give as much information as possible to " +"help us searching for a solution. Here is some information we might need:" +msgstr "" +"Description de votre problème. Veuillez donner le plus d'informations " +"possible pour nous aider à chercher une solution. Voici quelques " +"informations dont nous pourrions avoir besoin :" + +#: tickets/templates/tickets/form_ticket.html:47 +msgid "The type of your problem (membership, connection, payment etc.)." +msgstr "Le type de votre problème (adhésion, connexion, paiement etc.)." + +#: tickets/templates/tickets/form_ticket.html:50 +msgid "" +"The conditions in which you encounter the problem (Wi-Fi/wired connection, " +"on every machines or only one, on a new machine etc.)." +msgstr "" +"Les conditions dans lesquelles vous rencontrez le problème (connexion Wi-Fi/" +"filaire, sur toutes les machines ou une seule, sur une nouvelle machine " +"etc.)." + +#: tickets/templates/tickets/form_ticket.html:53 +msgid "" +"The locations where you encounter the problem (in your room, in a common " +"space, in a specific building etc.)." +msgstr "" +"Les lieux où vous rencontrez le problème (dans votre chambre, dans un espace " +"commun, dans un bâtiment en particulier etc.)." + +#: tickets/templates/tickets/form_ticket.html:56 +msgid "Open the ticket" +msgstr "Ouvrir le ticket" + +#: tickets/templates/tickets/index.html:32 +msgid "List of tickets" +msgstr "Liste des tickets" + +#: tickets/templates/tickets/navbar.html:2 +msgid "Manage the tickets" +msgstr "Gérer les tickets" + +#: tickets/templates/tickets/preferences.html:21 +msgid "Publication email address" +msgstr "Adresse mail de publication" + +#: tickets/templates/tickets/preferences.html:25 +msgid "No email address, the tickets will not be published." +msgstr "Pas d'adresse mail, les tickets ne seront pas publiés." + +#: tickets/templates/tickets/preferences.html:29 +msgid "Email language" +msgstr "Langue du mail" + +#: tickets/templates/tickets/profil.html:19 +msgid "No tickets" +msgstr "Pas de tickets" + +#: tickets/views.py:69 tickets/views.py:80 +msgid "" +"Your ticket has been succesfully opened. We will take care of it as soon as " +"possible." +msgstr "" +"Votre ticket a bien été ouvert. Nous nous en occuperons dès que possible." + +#: tickets/views.py:125 tickets/views.py:175 +msgid "Never" +msgstr "Jamais" + +#: tickets/views.py:152 +msgid "The tickets preferences were edited." +msgstr "Les préférences de tickets ont été modifiées." + +#: tickets/views.py:155 +msgid "Invalid form." +msgstr "Formulaire invalide." diff --git a/tickets/migrations/0001_initial.py b/tickets/migrations/0001_initial.py new file mode 100644 index 00000000..4140c955 --- /dev/null +++ b/tickets/migrations/0001_initial.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2019-08-19 08:19 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import re2o.mixins + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)] + + operations = [ + migrations.CreateModel( + name="Preferences", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "publish_address", + models.EmailField( + help_text="Email address to publish the new tickets (leave empty for no publications)", + max_length=1000, + null=True, + ), + ), + ( + "mail_language", + models.IntegerField( + choices=[(0, "Français"), (1, "English")], default=0 + ), + ), + ], + options={"verbose_name": "Ticket's settings"}, + ), + migrations.CreateModel( + name="Ticket", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "title", + models.CharField(help_text="Title of the ticket", max_length=255), + ), + ( + "description", + models.TextField( + help_text="Description of the ticket", max_length=3000 + ), + ), + ("date", models.DateTimeField(auto_now_add=True)), + ( + "email", + models.EmailField( + help_text="An email address to get back to you", + max_length=100, + null=True, + ), + ), + ("solved", models.BooleanField(default=False)), + ( + "user", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="tickets", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "verbose_name": "Ticket", + "verbose_name_plural": "Tickets", + "permissions": (("view_tickets", "Can view a ticket object"),), + "verbose_name": "Ticket", + "verbose_name_plural": "Tickets", + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + ] diff --git a/tickets/migrations/0002_auto_20191120_0159.py b/tickets/migrations/0002_auto_20191120_0159.py new file mode 100644 index 00000000..05ae5a68 --- /dev/null +++ b/tickets/migrations/0002_auto_20191120_0159.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.23 on 2019-11-20 00:59 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tickets', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='preferences', + options={'verbose_name': 'tickets preferences'}, + ), + migrations.AlterModelOptions( + name='ticket', + options={'permissions': (('view_tickets', 'Can view a ticket object'),), 'verbose_name': 'ticket', 'verbose_name_plural': 'tickets'}, + ), + migrations.AlterField( + model_name='preferences', + name='mail_language', + field=models.IntegerField(choices=[(0, 'French'), (1, 'English')], default=0), + ), + migrations.AlterField( + model_name='preferences', + name='publish_address', + field=models.EmailField(help_text='Email address to publish the new tickets (leave empty for no publication).', max_length=1000, null=True), + ), + migrations.AlterField( + model_name='ticket', + name='description', + field=models.TextField(help_text='Description of the ticket.', max_length=3000), + ), + migrations.AlterField( + model_name='ticket', + name='email', + field=models.EmailField(help_text='An email address to get back to you.', max_length=100, null=True), + ), + migrations.AlterField( + model_name='ticket', + name='title', + field=models.CharField(help_text='Title of the ticket.', max_length=255), + ), + ] diff --git a/tickets/migrations/__init__.py b/tickets/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tickets/models.py b/tickets/models.py new file mode 100644 index 00000000..3308c6db --- /dev/null +++ b/tickets/models.py @@ -0,0 +1,111 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ +from django.core.mail import send_mail +from django.template import loader +from django.db.models.signals import post_save +from django.dispatch import receiver + +from re2o.mixins import AclMixin + +from preferences.models import GeneralOption + +import users.models + +from .preferences.models import Preferences + + +class Ticket(AclMixin, models.Model): + """Model of a ticket""" + + user = models.ForeignKey( + "users.User", + on_delete=models.CASCADE, + related_name="tickets", + blank=True, + null=True, + ) + title = models.CharField( + max_length=255, help_text=_("Title of the ticket."), blank=False, null=False + ) + description = models.TextField( + max_length=3000, + help_text=_("Description of the ticket."), + blank=False, + null=False, + ) + date = models.DateTimeField(auto_now_add=True) + email = models.EmailField( + help_text=_("An email address to get back to you."), max_length=100, null=True + ) + solved = models.BooleanField(default=False) + + class Meta: + permissions = (("view_tickets", _("Can view a ticket object")),) + verbose_name = _("ticket") + verbose_name_plural = _("tickets") + + def __str__(self): + if self.user: + return _("Ticket from %(name)s. Date: %(date)s.").format(name=self.user.surname, date=self.date) + else: + return _("Anonymous ticket. Date: %s.") % (self.date) + + def publish_mail(self): + site_url = GeneralOption.objects.first().main_site_url + to_addr = Preferences.objects.first().publish_address + context = {"ticket": self, "site_url": site_url} + + lang = Preferences.objects.first().mail_language + if lang == 0: + obj = "Nouveau ticket ouvert" + template = loader.get_template("tickets/publication_mail_fr") + else: + obj = "New ticket opened" + template = loader.get_template("tickets/publication_mail_en") + send_mail( + obj, + template.render(context), + GeneralOption.get_cached_value("email_from"), + [to_addr], + fail_silently=False, + ) + + def can_view(self, user_request, *_args, **_kwargs): + """ Check that the user has the right to view the ticket + or that it is the author""" + if ( + not user_request.has_perm("tickets.view_ticket") + and self.user != user_request + ): + return ( + False, + _("You don't have the right to view other tickets than yours."), + ("tickets.view_ticket",), + ) + else: + return True, None, None + + @staticmethod + def can_view_all(user_request, *_args, **_kwargs): + """ Check that the user has access to the list of all tickets""" + can = user_request.has_perm("tickets.view_tickets") + return ( + can, + _("You don't have the right to view the list of tickets.") + if not can + else None, + ("tickets.view_tickets",), + ) + + def can_create(user_request, *_args, **_kwargs): + """ Authorise all users to open tickets """ + return True, None, None + + +@receiver(post_save, sender=Ticket) +def ticket_post_save(**kwargs): + """ Send the mail to publish the new ticket """ + if kwargs["created"]: + if Preferences.objects.first().publish_address: + ticket = kwargs["instance"] + ticket.publish_mail() diff --git a/tickets/preferences/forms.py b/tickets/preferences/forms.py new file mode 100644 index 00000000..b12bde67 --- /dev/null +++ b/tickets/preferences/forms.py @@ -0,0 +1,13 @@ +from django import forms +from django.forms import ModelForm, Form +from django.utils.translation import ugettext_lazy as _ + +from .models import Preferences + + +class EditPreferencesForm(ModelForm): + """ Edit the ticket's settings""" + + class Meta: + model = Preferences + fields = "__all__" diff --git a/tickets/preferences/models.py b/tickets/preferences/models.py new file mode 100644 index 00000000..27922303 --- /dev/null +++ b/tickets/preferences/models.py @@ -0,0 +1,21 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ + + +class Preferences(models.Model): + """ Definition of the ticket's settings""" + + publish_address = models.EmailField( + help_text=_( + "Email address to publish the new tickets (leave empty for no publication)." + ), + max_length=1000, + null=True, + ) + LANG_FR = 0 + LANG_EN = 1 + LANGUES = ((0, _("French")), (1, _("English"))) + mail_language = models.IntegerField(choices=LANGUES, default=LANG_FR) + + class Meta: + verbose_name = _("tickets preferences") diff --git a/tickets/templates/tickets/aff_ticket.html b/tickets/templates/tickets/aff_ticket.html new file mode 100644 index 00000000..b66ad1c5 --- /dev/null +++ b/tickets/templates/tickets/aff_ticket.html @@ -0,0 +1,84 @@ +{% extends 'users/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 Lara 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 i18n %} +{% load humanize %} + +{% block title %}{% trans "Tickets" %}{% endblock %} + +{% block content %} + +

    {% blocktrans with id=ticket.id %}Ticket #{{id}}{% endblocktrans %} +{% if ticket.solved %} +{% trans "Solved" %} +{% else %} +{% trans "Not solved" %} +{% endif %} +

    + +
    +
    + {% trans "Opened by" %} + {% if ticket.user %} + + {{ ticket.user.get_full_name }} + + {% else %} + {% trans "Anonymous user" %} + {% endif %} + {{ ticket.date | naturalday}}. + {% if not ticket.user %} + {% trans "Response address: " %}{{ticket.email}} + {% endif %} +
    +
    + +

    {% trans "Title:" %} {{ticket.title}}

    +

    {% trans "Description:" %} {{ ticket.description }}

    + +
    +
    + {% csrf_token %} + {% bootstrap_form changestatusform %} + + {% if not ticket.solved %} + {% trans "Mark as solved" as tr_mark_solved %} + {% bootstrap_button tr_mark_solved button_type="submit" button_class='btn-info' %} + {% else %} + {% trans "Mark as not solved" as tr_mark_not_solved %} + {% bootstrap_button tr_mark_not_solved button_type="submit" button_class='btn-warning' %} + {% endif %} +
    +
    +
    +
    + + + + +{% endblock %} diff --git a/tickets/templates/tickets/aff_tickets.html b/tickets/templates/tickets/aff_tickets.html new file mode 100644 index 00000000..218c6f0d --- /dev/null +++ b/tickets/templates/tickets/aff_tickets.html @@ -0,0 +1,89 @@ +{% 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 Lara 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 i18n %} + +{% block content %} + + +
    +
    +
    +
    + {{ nbr_tickets }} {% blocktrans count nb=nbr_tickets %}Ticket{% plural %}Tickets{% endblocktrans %} +
    +
    + {{ nbr_tickets_unsolved }} {% blocktrans count nb=nbr_tickets_unsolved %}Ticket not solved{% plural %}Tickets not solved{% endblocktrans %} +
    +
    + {% trans "Last ticket:" %} {{ last_ticket_date }} +
    +
    +
    +
    + + + +
    + + + + + + + + + + + {% for ticket in tickets_list %} + + + {% if ticket.user %} + + {% else %} + + {% endif %} + + + {% if ticket.solved %} + + {% else %} + + {% endif %} + + {% endfor %} + + +
    {% trans "User" %}{% trans "Title" %}{% trans "Date" %}{% trans "Solved" %}
    + + + + {{ ticket.user.get_short_name }}{% trans "Anonymous" %}{{ ticket.title }}{{ ticket.date }}
    + + {% if tickets_list.paginator %} + {% include 'pagination.html' with list=tickets_list go_to_id="tickets" %} + {% endif %} +
    +{% endblock %} diff --git a/tickets/templates/tickets/contact.html b/tickets/templates/tickets/contact.html new file mode 100644 index 00000000..f6c9561b --- /dev/null +++ b/tickets/templates/tickets/contact.html @@ -0,0 +1,13 @@ +{% load i18n %} + +
    +

    {% trans "Tickets" %}

    +
    +
    +
    + {% blocktrans %}If you are experiencing issues with the services offered by {{asso_name}}, you can open a ticket that will be taken care of. If you want to contact us on any other topic, please choose one address below.{% endblocktrans %} +
    + +
    +
    +
    diff --git a/tickets/templates/tickets/form_preferences.html b/tickets/templates/tickets/form_preferences.html new file mode 100644 index 00000000..d5dd223b --- /dev/null +++ b/tickets/templates/tickets/form_preferences.html @@ -0,0 +1,49 @@ +{% 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 Lara Kermarec +Copyright © 2017 Augustin Lemesle +Copyright © 2017 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. +{% endcomment %} + +{% load bootstrap3 %} +{% load i18n %} + +{% block title %}{% trans "Ticket" %}{% endblock %} + +{% block content %} +

    {% trans "Editing of tickets preferences" %}

    + +{% for message in messages %} +
    + + {{ message | safe }} +
    +{% endfor %} + +
    + {% csrf_token %} + {% bootstrap_field preferencesform.publish_address %} + {% bootstrap_field preferencesform.mail_language %} + {% trans "Edit" as tr_edit %} + {% bootstrap_button tr_edit button_type="submit" icon='ok' button_class='btn-success' %} +
    +{% endblock %} diff --git a/tickets/templates/tickets/form_ticket.html b/tickets/templates/tickets/form_ticket.html new file mode 100644 index 00000000..b05803bc --- /dev/null +++ b/tickets/templates/tickets/form_ticket.html @@ -0,0 +1,59 @@ +{% extends 'users/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 Lara Kermarec +Copyright © 2017 Augustin Lemesle +Copyright © 2017 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. +{% endcomment %} + +{% load bootstrap3 %} +{% load massive_bootstrap_form %} +{% load i18n %} + +{% block title %}{% trans "Ticket" %}{% endblock %} + +{% block content %} +

    {% trans "Ticket opening" %}

    + +
    + {% csrf_token %} + {% if not user.is_authenticated %} +

    {% trans "You are not authenticated. Please log in or provide an email address so we can get back to you." %}

    + {% bootstrap_field ticketform.email %} + {% endif %} + {% bootstrap_field ticketform.title %} +
    +

    {% trans "Description of your problem. Please give as much information as possible to help us searching for a solution. Here is some information we might need:" %}

    +
      +
    • +

      {% trans "The type of your problem (membership, connection, payment etc.)." %}

      +
    • +
    • +

      {% trans "The conditions in which you encounter the problem (Wi-Fi/wired connection, on every machines or only one, on a new machine etc.)." %}

      +
    • +
    • +

      {% trans "The locations where you encounter the problem (in your room, in a common space, in a specific building etc.)." %}

      +
    + {% bootstrap_field ticketform.description %} + {% trans "Open the ticket" as tr_open %} + {% bootstrap_button tr_open button_type="submit" icon='ok' button_class='btn-success' %} +
    +{% endblock %} diff --git a/tickets/templates/tickets/index.html b/tickets/templates/tickets/index.html new file mode 100644 index 00000000..80b11351 --- /dev/null +++ b/tickets/templates/tickets/index.html @@ -0,0 +1,34 @@ +{% extends 'users/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 Lara 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 i18n %} + +{% block title%}{% trans "Tickets" %}{% endblock %} + +{% block content %} +

    {% trans "List of tickets" %}

    + {% include 'tickets/aff_tickets.html' with tickets_list=tickets_list %} +{% endblock %} diff --git a/tickets/templates/tickets/navbar.html b/tickets/templates/tickets/navbar.html new file mode 100644 index 00000000..d344f822 --- /dev/null +++ b/tickets/templates/tickets/navbar.html @@ -0,0 +1,2 @@ +{% load i18n %} +
  • {% trans "Manage the tickets" %}
  • diff --git a/tickets/templates/tickets/navbar_logout.html b/tickets/templates/tickets/navbar_logout.html new file mode 100644 index 00000000..7c4dcd20 --- /dev/null +++ b/tickets/templates/tickets/navbar_logout.html @@ -0,0 +1,6 @@ +{% load i18n %} +
  • + + {% trans "Open a ticket" %} + +
  • diff --git a/tickets/templates/tickets/preferences.html b/tickets/templates/tickets/preferences.html new file mode 100644 index 00000000..df51f3e1 --- /dev/null +++ b/tickets/templates/tickets/preferences.html @@ -0,0 +1,36 @@ +{% load i18n %} + +
    + + +
    + + + + {% trans "Edit" %} + +

    + +
    + + + + {% if preferences.publish_address %} + + {% else %} + + {% endif %} + + + + +

    {% trans "Publication email address" %}

    {{ preferences.publish_address }}

    {% trans "No email address, the tickets will not be published." %}

    {% trans "Email language" %}

    {{ language }}

    +
    +
    +
    +
    +
    diff --git a/tickets/templates/tickets/profil.html b/tickets/templates/tickets/profil.html new file mode 100644 index 00000000..05d03ccf --- /dev/null +++ b/tickets/templates/tickets/profil.html @@ -0,0 +1,23 @@ +{% load i18n %} + +
    +
    +

    + {% trans "Tickets" %} +

    +
    +
    + +
    + {% if tickets_list %} + {% include 'tickets/aff_tickets.html' with tickets_list=tickets_list %} + {% else %} +

    {% trans "No tickets" %}

    + {% endif %} +
    +
    +
    diff --git a/tickets/templates/tickets/publication_mail_en b/tickets/templates/tickets/publication_mail_en new file mode 100644 index 00000000..5224a80b --- /dev/null +++ b/tickets/templates/tickets/publication_mail_en @@ -0,0 +1,12 @@ +{% if ticket.user %} {{ ticket.user.get_full_name }} opened a ticket. +Profile: {{site_url}}{% url 'users:profil' ticket.user.id%} +Answer to the address: {{ticket.user.get_mail}}. + +{% else %} +An anonymous user (not authenticated) opened a ticket +Answer to the address:{{ticket.email}}. +{% endif %} + +Title: {{ticket.title}} + +Description: {{ticket.description}} diff --git a/tickets/templates/tickets/publication_mail_fr b/tickets/templates/tickets/publication_mail_fr new file mode 100644 index 00000000..ef1099da --- /dev/null +++ b/tickets/templates/tickets/publication_mail_fr @@ -0,0 +1,12 @@ +{% if ticket.user %} {{ ticket.user.get_full_name }} a ouvert un ticket. +Profil : {{site_url}}{% url 'users:profil' ticket.user.id%} +Répondre à l'adresse : {{ticket.user.get_mail}}. + +{% else %} +Un utilisateur anonyme (non connecté) a ouvert un ticket. +Répondre à l'adresse : {{ticket.email}}. +{% endif %} + +Titre : {{ticket.title}} + +Description : {{ticket.description}} diff --git a/tickets/tests.py b/tickets/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/tickets/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/tickets/urls.py b/tickets/urls.py new file mode 100644 index 00000000..1dfda475 --- /dev/null +++ b/tickets/urls.py @@ -0,0 +1,14 @@ +from django.conf.urls import url + +from . import views + +urlpatterns = [ + url(r"^$", views.aff_tickets, name="aff-tickets"), + url(r"^ticket/(?P[0-9]+)$", views.aff_ticket, name="aff-ticket"), + url( + r"^ticket/edit-preferences-tickets$", + views.edit_preferences, + name="edit-preferences-tickets", + ), + url(r"^new_ticket/$", views.new_ticket, name="new-ticket"), +] diff --git a/tickets/views.py b/tickets/views.py new file mode 100644 index 00000000..d330719a --- /dev/null +++ b/tickets/views.py @@ -0,0 +1,217 @@ +# -*- 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 Lara 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. + +# App de gestion des users pour re2o +# Lara Kermarec, Gabriel Détraz, Lemesle Augustin +# Gplv2 + +from django.contrib import messages +from django.contrib.auth.decorators import login_required +from django.shortcuts import render, redirect +from django.template.loader import render_to_string +from django.views.decorators.cache import cache_page +from django.utils.translation import ugettext as _ +from django.urls import reverse +from django.forms import modelformset_factory +from re2o.views import form + +from re2o.base import re2o_paginator + +from re2o.acl import can_view, can_view_all, can_edit, can_create + +from preferences.models import GeneralOption + +from .models import Ticket + +from .preferences.models import Preferences + +from .forms import NewTicketForm, ChangeStatusTicketForm + +from .preferences.forms import EditPreferencesForm + + +def new_ticket(request): + """ Ticket creation view""" + ticketform = NewTicketForm(request.POST or None) + + if request.method == "POST": + ticketform = NewTicketForm(request.POST) + + if ticketform.is_valid(): + email = ticketform.cleaned_data.get("email") + ticket = ticketform.save(commit=False) + if request.user.is_authenticated: + ticket.user = request.user + ticket.save() + messages.success( + request, + _( + "Your ticket has been succesfully opened. We will take care of it as soon as possible." + ), + ) + return redirect( + reverse("users:profil", kwargs={"userid": str(request.user.id)}) + ) + if not request.user.is_authenticated and email != "": + ticket.save() + messages.success( + request, + _( + "Your ticket has been succesfully opened. We will take care of it as soon as possible." + ), + ) + return redirect(reverse("index")) + else: + messages.error( + request, + _( + "You are not authenticated. Please log in or provide an email address so we can get back to you." + ), + ) + return form( + {"ticketform": ticketform}, "tickets/form_ticket.html", request + ) + + else: + ticketform = NewTicketForm + return form({"ticketform": ticketform}, "tickets/form_ticket.html", request) + + +@login_required +@can_view(Ticket) +def aff_ticket(request, ticket, ticketid): + """View to display only one ticket""" + changestatusform = ChangeStatusTicketForm(request.POST) + if request.method == "POST": + ticket.solved = not ticket.solved + ticket.save() + return render( + request, + "tickets/aff_ticket.html", + {"ticket": ticket, "changestatusform": changestatusform}, + ) + + +@login_required +@can_view_all(Ticket) +def aff_tickets(request): + """ View to display all the tickets """ + tickets_list = Ticket.objects.all().order_by("-date") + nbr_tickets = tickets_list.count() + nbr_tickets_unsolved = tickets_list.filter(solved=False).count() + if nbr_tickets: + last_ticket_date = tickets_list.first().date + else: + last_ticket_date = _("Never") + + pagination_number = GeneralOption.get_cached_value("pagination_number") + + tickets = re2o_paginator(request, tickets_list, pagination_number) + + context = { + "tickets_list": tickets, + "last_ticket_date": last_ticket_date, + "nbr_tickets": nbr_tickets, + "nbr_tickets_unsolved": nbr_tickets_unsolved, + } + + return render(request, "tickets/index.html", context=context) + + +def edit_preferences(request): + """ View to edit the settings of the tickets """ + + preferences_instance, created = Preferences.objects.get_or_create(id=1) + preferencesform = EditPreferencesForm( + request.POST or None, instance=preferences_instance + ) + + if preferencesform.is_valid(): + if preferencesform.changed_data: + preferencesform.save() + messages.success(request, _("The tickets preferences were edited.")) + return redirect(reverse("preferences:display-options")) + else: + messages.error(request, _("Invalid form.")) + return form( + {"preferencesform": preferencesform}, + "tickets/form_preferences.html", + request, + ) + return form( + {"preferencesform": preferencesform}, "tickets/form_preferences.html", request + ) + + +# views cannoniques des apps optionnels +def profil(request, user): + """ View to display the ticket's module on the profil""" + tickets_list = Ticket.objects.filter(user=user).all().order_by("-date") + nbr_tickets = tickets_list.count() + nbr_tickets_unsolved = tickets_list.filter(solved=False).count() + if nbr_tickets: + last_ticket_date = tickets_list.first().date + else: + last_ticket_date = _("Never") + + pagination_number = GeneralOption.get_cached_value("pagination_large_number") + + tickets = re2o_paginator(request, tickets_list, pagination_number) + + context = { + "tickets_list": tickets, + "last_ticket_date": last_ticket_date, + "nbr_tickets": nbr_tickets, + "nbr_tickets_unsolved": nbr_tickets_unsolved, + } + return render_to_string( + "tickets/profil.html", context=context, request=request, using=None + ) + + +def preferences(request): + """ View to display the settings of the tickets in the preferences page""" + pref, created = Preferences.objects.get_or_create(id=1) + context = { + "preferences": pref, + "language": str(pref.LANGUES[pref.mail_language][1]), + } + return render_to_string( + "tickets/preferences.html", context=context, request=request, using=None + ) + + +def contact(request): + """View to display a contact address on the contact page + used here to display a link to open a ticket""" + return render_to_string("tickets/contact.html") + + +def navbar_user(): + """View to display the ticket link in thet user's dropdown in the navbar""" + return ("users", render_to_string("tickets/navbar.html")) + + +def navbar_logout(): + """View to display the ticket link to log out users""" + return render_to_string("tickets/navbar_logout.html") diff --git a/topologie/__init__.py b/topologie/__init__.py index 9e8202ea..2f5cebcd 100644 --- a/topologie/__init__.py +++ b/topologie/__init__.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify diff --git a/topologie/acl.py b/topologie/acl.py index 62e51d8a..d1aa6a0d 100644 --- a/topologie/acl.py +++ b/topologie/acl.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -38,7 +38,9 @@ def can_view(user): 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('topologie') - return can, None if can else _("You don't have the right to view this" - " application.") - + can = user.has_module_perms("topologie") + return ( + can, + None if can else _("You don't have the right to view this application."), + ("topologie",), + ) diff --git a/topologie/admin.py b/topologie/admin.py index d2a25461..bc4ca81e 100644 --- a/topologie/admin.py +++ b/topologie/admin.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -39,58 +39,77 @@ from .models import ( AccessPoint, SwitchBay, Building, + Dormitory, PortProfile, ) class StackAdmin(VersionAdmin): """Administration d'une stack de switches (inclus des switches)""" + pass class SwitchAdmin(VersionAdmin): """Administration d'un switch""" + pass class PortAdmin(VersionAdmin): """Administration d'un port de switches""" + pass class AccessPointAdmin(VersionAdmin): """Administration d'une borne""" + pass class RoomAdmin(VersionAdmin): """Administration d'un chambre""" + pass class ModelSwitchAdmin(VersionAdmin): """Administration d'un modèle de switch""" + pass class ConstructorSwitchAdmin(VersionAdmin): """Administration d'un constructeur d'un switch""" + pass class SwitchBayAdmin(VersionAdmin): """Administration d'une baie de brassage""" + pass class BuildingAdmin(VersionAdmin): """Administration d'un batiment""" + pass + +class DormitoryAdmin(VersionAdmin): + """Administration d'une residence""" + + pass + + class PortProfileAdmin(VersionAdmin): """Administration of a port profile""" + pass + admin.site.register(Port, PortAdmin) admin.site.register(AccessPoint, AccessPointAdmin) admin.site.register(Room, RoomAdmin) @@ -99,5 +118,6 @@ admin.site.register(Stack, StackAdmin) admin.site.register(ModelSwitch, ModelSwitchAdmin) admin.site.register(ConstructorSwitch, ConstructorSwitchAdmin) admin.site.register(Building, BuildingAdmin) +admin.site.register(Dormitory, DormitoryAdmin) admin.site.register(SwitchBay, SwitchBayAdmin) admin.site.register(PortProfile, PortProfileAdmin) diff --git a/topologie/forms.py b/topologie/forms.py index ed6fa5b9..91cc8367 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -38,10 +38,7 @@ from django.db.models import Prefetch from django.utils.translation import ugettext_lazy as _ from machines.models import Interface -from machines.forms import ( - EditMachineForm, - NewMachineForm -) +from machines.forms import EditMachineForm, NewMachineForm from re2o.mixins import FormRevMixin from .models import ( @@ -54,6 +51,7 @@ from .models import ( AccessPoint, SwitchBay, Building, + Dormitory, PortProfile, ModuleSwitch, ModuleOnSwitch, @@ -63,12 +61,13 @@ from .models import ( class PortForm(FormRevMixin, ModelForm): """Formulaire pour la création d'un port d'un switch Relié directement au modèle port""" + class Meta: model = Port - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(PortForm, self).__init__(*args, prefix=prefix, **kwargs) @@ -81,213 +80,240 @@ class EditPortForm(FormRevMixin, ModelForm): Optimisation sur les queryset pour machines et port_related pour optimiser le temps de chargement avec select_related (vraiment lent sans)""" + class Meta(PortForm.Meta): - fields = ['room', 'related', 'machine_interface', 'custom_profile', - 'state', 'details'] + fields = [ + "room", + "related", + "machine_interface", + "custom_profile", + "state", + "details", + ] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(EditPortForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['machine_interface'].queryset = ( - Interface.objects.all().select_related('domain__extension') + self.fields[ + "machine_interface" + ].queryset = Interface.objects.all().select_related("domain__extension") + self.fields["related"].queryset = Port.objects.all().prefetch_related( + "switch__machine_ptr__interface_set__domain__extension" + ) + self.fields["room"].queryset = Room.objects.all().select_related( + "building__dormitory" ) - self.fields['related'].queryset = Port.objects.all().prefetch_related('switch__machine_ptr__interface_set__domain__extension') class AddPortForm(FormRevMixin, ModelForm): """Permet d'ajouter un port de switch. Voir EditPortForm pour plus d'informations""" + class Meta(PortForm.Meta): fields = [ - 'port', - 'room', - 'machine_interface', - 'related', - 'custom_profile', - 'state', - 'details' + "port", + "room", + "machine_interface", + "related", + "custom_profile", + "state", + "details", ] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(AddPortForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['machine_interface'].queryset = ( - Interface.objects.all().select_related('domain__extension') - ) - self.fields['related'].queryset = ( - Port.objects.all().prefetch_related(Prefetch( - 'switch__interface_set', - queryset=(Interface.objects - .select_related('ipv4__ip_type__extension') - .select_related('domain__extension')) - )) + self.fields[ + "machine_interface" + ].queryset = Interface.objects.all().select_related("domain__extension") + self.fields["related"].queryset = Port.objects.all().prefetch_related( + Prefetch( + "switch__interface_set", + queryset=( + Interface.objects.select_related( + "ipv4__ip_type__extension" + ).select_related("domain__extension") + ), + ) ) class StackForm(FormRevMixin, ModelForm): """Permet d'edition d'une stack : stack_id, et switches membres de la stack""" + class Meta: model = Stack - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(StackForm, self).__init__(*args, prefix=prefix, **kwargs) class AddAccessPointForm(NewMachineForm): """Formulaire pour la création d'une borne Relié directement au modèle borne""" + class Meta: model = AccessPoint - fields = ['location', 'name'] + fields = ["location", "name"] class EditAccessPointForm(EditMachineForm): """Edition d'une borne. Edition complète""" + class Meta: model = AccessPoint - fields = '__all__' + fields = "__all__" class EditSwitchForm(EditMachineForm): """Permet d'éditer un switch : nom et nombre de ports""" + class Meta: model = Switch - fields = '__all__' + fields = "__all__" class NewSwitchForm(NewMachineForm): """Permet de créer un switch : emplacement, paramètres machine, membre d'un stack (option), nombre de ports (number)""" + class Meta(EditSwitchForm.Meta): - fields = ['name', 'switchbay', 'number', 'stack', 'stack_member_id'] + fields = ["name", "switchbay", "number", "stack", "stack_member_id"] class EditRoomForm(FormRevMixin, ModelForm): """Permet d'éediter le nom et commentaire d'une prise murale""" + class Meta: model = Room - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(EditRoomForm, self).__init__(*args, prefix=prefix, **kwargs) class CreatePortsForm(forms.Form): """Permet de créer une liste de ports pour un switch.""" + begin = forms.IntegerField(label=_("Start:"), min_value=0) end = forms.IntegerField(label=_("End:"), min_value=0) class EditModelSwitchForm(FormRevMixin, ModelForm): """Permet d'éediter un modèle de switch : nom et constructeur""" - members = forms.ModelMultipleChoiceField( - Switch.objects.all(), - required=False - ) + + members = forms.ModelMultipleChoiceField(Switch.objects.all(), required=False) class Meta: model = ModelSwitch - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(EditModelSwitchForm, self).__init__( - *args, - prefix=prefix, - **kwargs - ) - instance = kwargs.get('instance', None) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) + super(EditModelSwitchForm, self).__init__(*args, prefix=prefix, **kwargs) + instance = kwargs.get("instance", None) if instance: - self.initial['members'] = Switch.objects.filter(model=instance) + self.initial["members"] = Switch.objects.filter(model=instance) def save(self, commit=True): instance = super().save(commit) - instance.switch_set = self.cleaned_data['members'] + instance.switch_set = self.cleaned_data["members"] return instance class EditConstructorSwitchForm(FormRevMixin, ModelForm): """Permet d'éediter le nom d'un constructeur""" + class Meta: model = ConstructorSwitch - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(EditConstructorSwitchForm, self).__init__( - *args, - prefix=prefix, - **kwargs - ) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) + super(EditConstructorSwitchForm, self).__init__(*args, prefix=prefix, **kwargs) class EditSwitchBayForm(FormRevMixin, ModelForm): """Permet d'éditer une baie de brassage""" - members = forms.ModelMultipleChoiceField( - Switch.objects.all(), - required=False - ) + + members = forms.ModelMultipleChoiceField(Switch.objects.all(), required=False) class Meta: model = SwitchBay - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(EditSwitchBayForm, self).__init__(*args, prefix=prefix, **kwargs) - instance = kwargs.get('instance', None) + instance = kwargs.get("instance", None) if instance: - self.initial['members'] = Switch.objects.filter(switchbay=instance) + self.initial["members"] = Switch.objects.filter(switchbay=instance) def save(self, commit=True): instance = super().save(commit) - instance.switch_set = self.cleaned_data['members'] + instance.switch_set = self.cleaned_data["members"] return instance class EditBuildingForm(FormRevMixin, ModelForm): """Permet d'éditer le batiment""" + class Meta: model = Building - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(EditBuildingForm, self).__init__(*args, prefix=prefix, **kwargs) + +class EditDormitoryForm(FormRevMixin, ModelForm): + """Enable dormitory edition""" + + class Meta: + model = Dormitory + fields = "__all__" + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop("prefix", self.Meta.model.__name__) + super(EditDormitoryForm, self).__init__(*args, prefix=prefix, **kwargs) + + class EditPortProfileForm(FormRevMixin, ModelForm): """Form to edit a port profile""" + class Meta: model = PortProfile - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(EditPortProfileForm, self).__init__(*args, - prefix=prefix, - **kwargs) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) + super(EditPortProfileForm, self).__init__(*args, prefix=prefix, **kwargs) + class EditModuleForm(FormRevMixin, ModelForm): """Add and edit module instance""" + class Meta: model = ModuleSwitch - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(EditModuleForm, self).__init__(*args, prefix=prefix, **kwargs) class EditSwitchModuleForm(FormRevMixin, ModelForm): """Add/edit a switch to a module""" + class Meta: model = ModuleOnSwitch - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(EditSwitchModuleForm, self).__init__(*args, prefix=prefix, **kwargs) diff --git a/topologie/locale/fr/LC_MESSAGES/django.po b/topologie/locale/fr/LC_MESSAGES/django.po index b6242bc0..ef0afb98 100644 --- a/topologie/locale/fr/LC_MESSAGES/django.po +++ b/topologie/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-01-09 00:01+0100\n" +"POT-Creation-Date: 2019-11-20 01:24+0100\n" "PO-Revision-Date: 2018-06-25 14:53+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -30,253 +30,268 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: acl.py:42 +#: topologie/acl.py:44 msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: forms.py:181 +#: topologie/forms.py:202 msgid "Start:" msgstr "Début :" -#: forms.py:182 +#: topologie/forms.py:203 msgid "End:" msgstr "Fin :" -#: models.py:74 +#: topologie/models.py:70 msgid "Can view a stack object" msgstr "Peut voir un objet pile" -#: models.py:76 +#: topologie/models.py:71 msgid "switches stack" msgstr "pile de commutateurs réseau" -#: models.py:77 +#: topologie/models.py:72 msgid "switches stacks" msgstr "piles de commutateurs réseau" -#: models.py:92 +#: topologie/models.py:87 msgid "The maximum ID is less than the minimum ID." msgstr "L'ID maximum est inférieur l'ID minimum." -#: models.py:105 -msgid "Details about the AP's location" -msgstr "Détails sur l'emplacement du point d'accès sans fil" +#: topologie/models.py:99 +msgid "Details about the AP's location." +msgstr "Détails sur l'emplacement du point d'accès sans fil." -#: models.py:112 +#: topologie/models.py:105 msgid "Can view an access point object" msgstr "Peut voir un objet point d'accès sans fil" -#: models.py:114 +#: topologie/models.py:106 topologie/views.py:664 topologie/views.py:716 msgid "access point" msgstr "point d'accès sans fil" -#: models.py:115 +#: topologie/models.py:107 msgid "access points" msgstr "points d'accès sans fil" -#: models.py:211 -msgid "Number of ports" -msgstr "Nombre de ports" +#: topologie/models.py:190 +msgid "Number of ports." +msgstr "Nombre de ports." -#: models.py:228 templates/topologie/aff_switch.html:48 -#: templates/topologie/index_p.html:38 views.py:843 -msgid "Switch model" -msgstr "Modèle de commutateur réseau" +#: topologie/models.py:200 +msgid "Switch model." +msgstr "Modèle de commutateur réseau." -#: models.py:241 -msgid "RADIUS key of the switch" -msgstr "Clé RADIUS du commutateur réseau" +#: topologie/models.py:210 +msgid "RADIUS key of the switch." +msgstr "Clé RADIUS du commutateur réseau." -#: models.py:248 -msgid "Management credentials for the switch" -msgstr "Identifiants de gestion du commutateur réseau" +#: topologie/models.py:217 +msgid "Management credentials for the switch." +msgstr "Identifiants de gestion du commutateur réseau." -#: models.py:252 -msgid "Automatic provision for the switch" -msgstr "Provision automatique pour le commutateur réseau" +#: topologie/models.py:220 +msgid "Automatic provision for the switch." +msgstr "Provision automatique pour le commutateur réseau." -#: models.py:259 +#: topologie/models.py:225 msgid "Can view a switch object" msgstr "Peut voir un objet commutateur réseau" -#: models.py:261 +#: topologie/models.py:226 topologie/views.py:542 topologie/views.py:615 msgid "switch" msgstr "commutateur réseau" -#: models.py:262 +#: topologie/models.py:227 msgid "switches" msgstr "commutateurs réseau" -#: models.py:273 +#: topologie/models.py:241 msgid "The switch ID exceeds the limits allowed by the stack." msgstr "L'ID du commutateur réseau dépasse les bornes autorisées par la pile." -#: models.py:278 +#: topologie/models.py:248 msgid "The stack member ID can't be void." msgstr "L'ID de membre dans la pile ne peut-être vide." -#: models.py:286 +#: topologie/models.py:255 msgid "The end port is less than the start port." msgstr "Le port de fin est inférieur au port de début." -#: models.py:293 +#: topologie/models.py:263 msgid "This switch can't have that many ports." msgstr "Ce commutateur réseau ne peut pas avoir autant de ports." -#: models.py:295 -msgid "Creation" -msgstr "Création" - -#: models.py:406 +#: topologie/models.py:471 msgid "The switch model is modular." msgstr "Le modèle de commutateur réseau est modulaire." -#: models.py:410 +#: topologie/models.py:474 msgid "The switch is considered as a module." msgstr "Le commutateur réseau est considéré comme un module." -#: models.py:415 +#: topologie/models.py:478 msgid "Can view a switch model object" msgstr "Peut voir un objet modèle de commutateur réseau" -#: models.py:417 +#: topologie/models.py:479 topologie/views.py:832 msgid "switch model" msgstr "modèle de commutateur réseau" -#: models.py:418 +#: topologie/models.py:480 msgid "switch models" msgstr "modèles de commutateur réseau" -#: models.py:431 -msgid "Reference of a module" -msgstr "Référence d'un module" +#: topologie/models.py:494 +msgid "Reference of a module." +msgstr "Référence d'un module." -#: models.py:432 -msgid "Module reference" -msgstr "Référence de module" +#: topologie/models.py:495 +msgid "module reference" +msgstr "référence de module" -#: models.py:438 models.py:439 templates/topologie/aff_modules.html:37 -msgid "Comment" -msgstr "Commentaire" +#: topologie/models.py:501 +msgid "Comment." +msgstr "Commentaire." -#: models.py:444 +#: topologie/models.py:502 +msgid "comment" +msgstr "commentaire" + +#: topologie/models.py:506 msgid "Can view a switch module object" msgstr "Peut voir un objet module de commutateur réseau" -#: models.py:446 +#: topologie/models.py:507 msgid "switch module" msgstr "module de commutateur réseau" -#: models.py:447 +#: topologie/models.py:508 msgid "switch modules" msgstr "modules de commutateur réseau" -#: models.py:460 -msgid "Slot on switch" -msgstr "Emplacement sur le commutateur réseau" +#: topologie/models.py:520 +msgid "Slot on switch." +msgstr "Emplacement sur le commutateur réseau." -#: models.py:461 templates/topologie/aff_modules.html:48 -#: templates/topologie/aff_modules.html:82 -msgid "Slot" -msgstr "Emplacement" +#: topologie/models.py:520 +msgid "slot" +msgstr "emplacement " -#: models.py:466 +#: topologie/models.py:527 msgid "Can view a link between switch and module object" msgstr "Peut voir un objet lien entre commutateur réseau et module" -#: models.py:469 +#: topologie/models.py:530 msgid "link between switch and module" msgstr "lien entre commutateur réseau et module" -#: models.py:470 +#: topologie/models.py:531 msgid "links between switch and module" msgstr "liens entre commutateur réseau et module" -#: models.py:474 -msgid "On slot " -msgstr "Sur l'emplacement " +#: topologie/models.py:535 +#, python-format +msgid "On slot %(slot)s of %(switch)s" +msgstr "Sur l'emplacement %(slot)s de %(switch)s" -#: models.py:474 -msgid " of " -msgstr " de " - -#: models.py:484 +#: topologie/models.py:545 msgid "Can view a switch constructor object" msgstr "Peut voir un objet constructeur de commutateur réseau" -#: models.py:487 +#: topologie/models.py:547 topologie/views.py:1079 msgid "switch constructor" msgstr "constructeur de commutateur réseau" -#: models.py:510 +#: topologie/models.py:548 +msgid "switch constructors" +msgstr "constructeurs de commutateur réseau" + +#: topologie/models.py:562 msgid "Can view a switch bay object" msgstr "Peut voir un objet baie de brassage" -#: models.py:512 +#: topologie/models.py:563 topologie/views.py:892 msgid "switch bay" msgstr "baie de brassage" -#: models.py:513 +#: topologie/models.py:564 msgid "switch bays" msgstr "baies de brassage" -#: models.py:526 +#: topologie/models.py:577 +msgid "Can view a dormitory object" +msgstr "Peut voir un objet résidence" + +#: topologie/models.py:578 topologie/views.py:1016 +msgid "dormitory" +msgstr "résidence" + +#: topologie/models.py:579 +msgid "dormitories" +msgstr "résidences" + +#: topologie/models.py:605 msgid "Can view a building object" msgstr "Peut voir un objet bâtiment" -#: models.py:528 +#: topologie/models.py:606 topologie/views.py:953 msgid "building" msgstr "bâtiment" -#: models.py:529 +#: topologie/models.py:607 msgid "buildings" msgstr "bâtiments" -#: models.py:588 models.py:589 -msgid "Port state Active" -msgstr "État du port Actif" +#: topologie/models.py:658 +msgid "Port state Active." +msgstr "État du port Actif." -#: models.py:596 +#: topologie/models.py:659 +msgid "port state Active" +msgstr "état du port Actif" + +#: topologie/models.py:665 msgid "Can view a port object" msgstr "Peut voir un objet port" -#: models.py:598 +#: topologie/models.py:666 msgid "port" msgstr "port" -#: models.py:599 +#: topologie/models.py:667 msgid "ports" msgstr "ports" -#: models.py:605 +#: topologie/models.py:673 msgid "Uplink: " msgstr "Liaison montante : " -#: models.py:607 +#: topologie/models.py:675 msgid "Machine: " msgstr "Machine : " -#: models.py:609 +#: topologie/models.py:677 msgid "Room: " msgstr "Chambre : " -#: models.py:611 +#: topologie/models.py:679 msgid "Unknown" msgstr "Inconnu" -#: models.py:678 +#: topologie/models.py:740 msgid "The port can't exist, its number is too great." msgstr "Le port ne peut pas exister, son numéro est trop grand." -#: models.py:684 +#: topologie/models.py:751 msgid "Room, interface and related port are mutually exclusive." msgstr "Chambre, interface et port relié sont mutuellement exclusifs." -#: models.py:687 +#: topologie/models.py:754 msgid "A port can't be related to itself." msgstr "Un port ne peut être relié à lui-même." -#: models.py:691 +#: topologie/models.py:759 msgid "" "The related port is already used, please clear it before creating the " "relation." @@ -284,356 +299,418 @@ msgstr "" "Le port relié est déjà utilisé, veuillez le modifier avant de créer la " "relation." -#: models.py:712 +#: topologie/models.py:781 msgid "Can view a room object" msgstr "Peut voir un objet chambre" -#: models.py:714 +#: topologie/models.py:782 topologie/views.py:773 msgid "room" msgstr "chambre" -#: models.py:715 +#: topologie/models.py:783 msgid "rooms" msgstr "chambres" -#: models.py:726 +#: topologie/models.py:793 msgid "MAC-RADIUS" msgstr "MAC-RADIUS" -#: models.py:743 templates/topologie/aff_chambres.html:36 -#: templates/topologie/aff_port.html:38 views.py:784 +#: topologie/models.py:806 topologie/templates/topologie/aff_chambres.html:36 +#: topologie/templates/topologie/aff_port.html:38 msgid "Room" msgstr "Chambre" -#: models.py:744 templates/topologie/aff_ap.html:36 +#: topologie/models.py:807 topologie/templates/topologie/aff_ap.html:36 msgid "Access point" msgstr "Point d'accès sans fil" -#: models.py:745 +#: topologie/models.py:808 msgid "Uplink" msgstr "Liaison montante" -#: models.py:746 +#: topologie/models.py:809 msgid "Organisation machine" msgstr "Machine d'association" -#: models.py:747 +#: topologie/models.py:810 msgid "Nothing" msgstr "Rien" -#: models.py:749 templates/topologie/aff_port_profile.html:37 -#: templates/topologie/aff_vlanoptions.html:34 -msgid "Name" -msgstr "Nom" +#: topologie/models.py:812 +msgid "name" +msgstr "nom" -#: models.py:756 -msgid "Default profile" -msgstr "Profil par défaut" +#: topologie/models.py:818 +msgid "default profile" +msgstr "profil par défaut" -#: models.py:764 +#: topologie/models.py:826 +msgid "profile on dormitory" +msgstr "profil sur la résidence" + +#: topologie/models.py:834 msgid "VLAN untagged" msgstr "VLAN untagged" -#: models.py:770 +#: topologie/models.py:840 msgid "VLAN(s) tagged" msgstr "VLAN(s) tagged" -#: models.py:775 -msgid "Type of RADIUS authentication : inactive, MAC-address or 802.1X" -msgstr "Type d'authentification RADIUS : inactive, MAC-address ou 802.1X" +#: topologie/models.py:846 +msgid "Type of RADIUS authentication: inactive, MAC-address or 802.1X." +msgstr "Type d'authentification RADIUS : inactive, MAC-address ou 802.1X." -#: models.py:777 +#: topologie/models.py:848 msgid "RADIUS type" msgstr "Type de RADIUS" -#: models.py:783 -msgid "In case of MAC-authentication : mode COMMON or STRICT on this port" +#: topologie/models.py:855 +msgid "In case of MAC-authentication: mode COMMON or STRICT on this port." msgstr "" "Dans le cas d'authentification par adresse MAC : mode COMMON ou STRICT sur " -"ce port" +"ce port." -#: models.py:785 +#: topologie/models.py:857 msgid "RADIUS mode" msgstr "Mode de RADIUS" -#: models.py:791 -msgid "Port speed limit" -msgstr "Limite de vitesse du port" +#: topologie/models.py:860 +msgid "Port speed limit." +msgstr "Limite de vitesse du port." -#: models.py:796 -msgid "Limit of MAC-address on this port" -msgstr "Limite de MAC-address sur ce port" +#: topologie/models.py:865 +msgid "Limit of MAC-address on this port." +msgstr "Limite de MAC-address sur ce port." -#: models.py:797 +#: topologie/models.py:866 msgid "MAC limit" msgstr "Limite MAC" -#: models.py:801 -msgid "Flow control" -msgstr "Contrôle du flux" +#: topologie/models.py:868 +msgid "Flow control." +msgstr "Contrôle du flux." -#: models.py:805 -msgid "Protect against rogue DHCP" -msgstr "Protège contre les DHCP pirates" +#: topologie/models.py:871 +msgid "Protect against rogue DHCP." +msgstr "Protège contre les DHCP pirates." -#: models.py:806 templates/topologie/aff_vlanoptions.html:36 +#: topologie/models.py:872 +#: topologie/templates/topologie/aff_vlanoptions.html:36 msgid "DHCP snooping" msgstr "DHCP snooping" -#: models.py:810 -msgid "Protect against rogue DHCPv6" -msgstr "Protège contre les DHCPv6 pirates" +#: topologie/models.py:876 +msgid "Protect against rogue DHCPv6." +msgstr "Protège contre les DHCPv6 pirates." -#: models.py:811 templates/topologie/aff_vlanoptions.html:37 +#: topologie/models.py:877 +#: topologie/templates/topologie/aff_vlanoptions.html:37 msgid "DHCPv6 snooping" msgstr "DHCPv6 snooping" -#: models.py:815 -msgid "Check if IP adress is DHCP assigned" -msgstr "Vérifie si l'adresse IP est attribuée par DHCP" +#: topologie/models.py:881 +msgid "Check if IP address is DHCP assigned." +msgstr "Vérifie si l'adresse IP est attribuée par DHCP." -#: models.py:816 +#: topologie/models.py:882 msgid "ARP protection" msgstr "Protection ARP" -#: models.py:820 -msgid "Protect against rogue RA" -msgstr "Protège contre les RA pirates" +#: topologie/models.py:886 +msgid "Protect against rogue RA." +msgstr "Protège contre les RA pirates." -#: models.py:821 +#: topologie/models.py:887 msgid "RA guard" msgstr "RA guard" -#: models.py:825 -msgid "Protect against loop" -msgstr "Protège contre une boucle" +#: topologie/models.py:891 +msgid "Protect against loop." +msgstr "Protège contre une boucle." -#: models.py:826 -msgid "Loop protection" -msgstr "Protection contre une boucle" +#: topologie/models.py:892 +msgid "loop protection" +msgstr "protection contre une boucle" -#: models.py:831 +#: topologie/models.py:896 msgid "Can view a port profile object" msgstr "Peut voir un objet profil de port" -#: models.py:833 +#: topologie/models.py:897 topologie/views.py:1130 msgid "port profile" msgstr "profil de port" -#: models.py:834 +#: topologie/models.py:898 msgid "port profiles" msgstr "profils de port" -#: templates/topologie/aff_ap.html:38 +#: topologie/models.py:936 +msgid "A default profile for all dormitories of that type already exists." +msgstr "" +"Un profil par défaut pour toutes les résidences de ce type existe déjà. " + +#: topologie/templates/topologie/aff_ap.html:38 msgid "MAC address" msgstr "Adresse MAC" -#: templates/topologie/aff_ap.html:40 templates/topologie/aff_switch.html:39 +#: topologie/templates/topologie/aff_ap.html:40 +#: topologie/templates/topologie/aff_switch.html:39 msgid "IPv4 address" msgstr "Adresse IPv4" -#: templates/topologie/aff_ap.html:42 templates/topologie/aff_chambres.html:38 -#: templates/topologie/aff_port.html:46 templates/topologie/aff_stacks.html:36 -#: templates/topologie/aff_switch.html:49 -#: templates/topologie/edit_stack_sw.html:34 +#: topologie/templates/topologie/aff_ap.html:42 +#: topologie/templates/topologie/aff_chambres.html:40 +#: topologie/templates/topologie/aff_port.html:46 +#: topologie/templates/topologie/aff_stacks.html:36 +#: topologie/templates/topologie/aff_switch.html:49 +#: topologie/templates/topologie/edit_stack_sw.html:34 msgid "Details" msgstr "Détails" -#: templates/topologie/aff_ap.html:43 +#: topologie/templates/topologie/aff_ap.html:43 msgid "Location" msgstr "Emplacement" -#: templates/topologie/aff_building.html:36 -#: templates/topologie/aff_switch_bay.html:38 views.py:953 +#: topologie/templates/topologie/aff_building.html:36 +#: topologie/templates/topologie/aff_chambres.html:37 +#: topologie/templates/topologie/aff_switch_bay.html:38 msgid "Building" msgstr "Bâtiment" -#: templates/topologie/aff_building.html:38 -#: templates/topologie/index_ap.html:33 templates/topologie/sidebar.html:47 +#: topologie/templates/topologie/aff_building.html:38 +#: topologie/templates/topologie/index_ap.html:33 +#: topologie/templates/topologie/sidebar.html:47 msgid "Access points" msgstr "Points d'accès sans fil" -#: templates/topologie/aff_constructor_switch.html:36 -#: templates/topologie/aff_model_switch.html:40 views.py:1013 +#: topologie/templates/topologie/aff_constructor_switch.html:36 +#: topologie/templates/topologie/aff_model_switch.html:40 msgid "Switch constructor" msgstr "Constructeur de commutateur réseau" -#: templates/topologie/aff_model_switch.html:36 -#: templates/topologie/aff_modules.html:36 -#: templates/topologie/aff_modules.html:81 +#: topologie/templates/topologie/aff_dormitory.html:36 +msgid "Dormitory" +msgstr "Résidence" + +#: topologie/templates/topologie/aff_dormitory.html:38 +#: topologie/templates/topologie/index_physical_grouping.html:50 +msgid "Buildings" +msgstr "Bâtiments" + +#: topologie/templates/topologie/aff_model_switch.html:36 +#: topologie/templates/topologie/aff_modules.html:36 +#: topologie/templates/topologie/aff_modules.html:81 msgid "Reference" msgstr "Référence" -#: templates/topologie/aff_model_switch.html:38 +#: topologie/templates/topologie/aff_model_switch.html:38 msgid "Commercial name" msgstr "Nom commercial" -#: templates/topologie/aff_model_switch.html:42 -#: templates/topologie/aff_modules.html:38 templates/topologie/index.html:66 -#: templates/topologie/sidebar.html:35 +#: topologie/templates/topologie/aff_model_switch.html:39 +msgid "Firmware" +msgstr "Micrologiciel" + +#: topologie/templates/topologie/aff_model_switch.html:42 +#: topologie/templates/topologie/aff_modules.html:38 +#: topologie/templates/topologie/index.html:66 +#: topologie/templates/topologie/sidebar.html:35 msgid "Switches" msgstr "Commutateurs réseau" -#: templates/topologie/aff_modules.html:48 +#: topologie/templates/topologie/aff_modules.html:37 +msgid "Comment" +msgstr "Commentaire" + +#: topologie/templates/topologie/aff_modules.html:48 +#: topologie/templates/topologie/aff_modules.html:82 +msgid "Slot" +msgstr "Emplacement" + +#: topologie/templates/topologie/aff_modules.html:48 msgid "of" msgstr "de" -#: templates/topologie/aff_modules.html:76 +#: topologie/templates/topologie/aff_modules.html:76 msgid "All modular switchs" msgstr "Tous les commutateurs réseau modulaires" -#: templates/topologie/aff_modules.html:80 templates/topologie/aff_port.html:36 +#: topologie/templates/topologie/aff_modules.html:80 +#: topologie/templates/topologie/aff_port.html:36 msgid "Switch" msgstr "Commutateur réseau" -#: templates/topologie/aff_port.html:33 +#: topologie/templates/topologie/aff_port.html:33 msgid "Port" msgstr "Port" -#: templates/topologie/aff_port.html:40 +#: topologie/templates/topologie/aff_port.html:40 msgid "Interface" msgstr "Interface" -#: templates/topologie/aff_port.html:42 +#: topologie/templates/topologie/aff_port.html:42 msgid "Related port" msgstr "Port relié" -#: templates/topologie/aff_port.html:44 +#: topologie/templates/topologie/aff_port.html:44 msgid "Port state" msgstr "État du port" -#: templates/topologie/aff_port.html:45 views.py:1065 +#: topologie/templates/topologie/aff_port.html:45 msgid "Port profile" msgstr "Profil de port" -#: templates/topologie/aff_port.html:85 +#: topologie/templates/topologie/aff_port.html:85 msgid "Active" msgstr "Actif" -#: templates/topologie/aff_port.html:87 +#: topologie/templates/topologie/aff_port.html:87 msgid "Disabled" msgstr "Désactivé" -#: templates/topologie/aff_port.html:92 +#: topologie/templates/topologie/aff_port.html:92 msgid "Default: " msgstr "Par défaut : " -#: templates/topologie/aff_port_profile.html:38 +#: topologie/templates/topologie/aff_port_profile.html:37 +#: topologie/templates/topologie/aff_vlanoptions.html:34 +msgid "Name" +msgstr "Nom" + +#: topologie/templates/topologie/aff_port_profile.html:38 msgid "Default for" msgstr "Par défaut pour" -#: templates/topologie/aff_port_profile.html:39 +#: topologie/templates/topologie/aff_port_profile.html:39 msgid "VLANs" msgstr "VLANs" -#: templates/topologie/aff_port_profile.html:40 +#: topologie/templates/topologie/aff_port_profile.html:40 msgid "RADIUS settings" msgstr "Paramètres RADIUS" -#: templates/topologie/aff_port_profile.html:41 +#: topologie/templates/topologie/aff_port_profile.html:41 msgid "Speed limit" msgstr "Limite de vitesse" -#: templates/topologie/aff_port_profile.html:42 +#: topologie/templates/topologie/aff_port_profile.html:42 msgid "MAC address limit" msgstr "Limite d'adresse MAC" -#: templates/topologie/aff_port_profile.html:43 +#: topologie/templates/topologie/aff_port_profile.html:43 msgid "Security" msgstr "Sécurité" -#: templates/topologie/aff_port_profile.html:53 +#: topologie/templates/topologie/aff_port_profile.html:50 +#, python-format +msgid " on %(dorm)s" +msgstr " sur %(dorm)s" + +#: topologie/templates/topologie/aff_port_profile.html:50 +msgid "Everywhere" +msgstr "Partout" + +#: topologie/templates/topologie/aff_port_profile.html:53 msgid "Untagged: " msgstr "Untagged :" -#: templates/topologie/aff_port_profile.html:57 +#: topologie/templates/topologie/aff_port_profile.html:57 msgid "Tagged: " msgstr "Tagged : " -#: templates/topologie/aff_port_profile.html:61 +#: topologie/templates/topologie/aff_port_profile.html:61 msgid "RADIUS type: " msgstr "Type de RADIUS : " -#: templates/topologie/aff_port_profile.html:64 +#: topologie/templates/topologie/aff_port_profile.html:64 msgid "RADIUS mode: " msgstr "Mode de RADIUS : " -#: templates/topologie/aff_repr_switch.html:67 -#: templates/topologie/aff_repr_switch.html:110 +#: topologie/templates/topologie/aff_repr_switch.html:67 +#: topologie/templates/topologie/aff_repr_switch.html:110 msgid "Empty" msgstr "Vide" -#: templates/topologie/aff_stacks.html:32 -#: templates/topologie/aff_switch.html:45 -#: templates/topologie/edit_stack_sw.html:32 -#: templates/topologie/index_p.html:39 +#: topologie/templates/topologie/aff_stacks.html:32 +#: topologie/templates/topologie/aff_switch.html:45 +#: topologie/templates/topologie/edit_stack_sw.html:32 +#: topologie/templates/topologie/index_p.html:39 msgid "Stack" msgstr "Pile" -#: templates/topologie/aff_stacks.html:34 -#: templates/topologie/aff_vlanoptions.html:33 +#: topologie/templates/topologie/aff_stacks.html:34 +#: topologie/templates/topologie/aff_vlanoptions.html:33 msgid "ID" msgstr "ID" -#: templates/topologie/aff_stacks.html:37 +#: topologie/templates/topologie/aff_stacks.html:37 msgid "Members" msgstr "Membres" -#: templates/topologie/aff_switch.html:37 templates/topologie/topo_more.html:56 +#: topologie/templates/topologie/aff_switch.html:37 +#: topologie/templates/topologie/topo_more.html:56 msgid "DNS name" msgstr "Nom DNS" -#: templates/topologie/aff_switch.html:41 -#: templates/topologie/aff_switch_bay.html:36 -#: templates/topologie/index_p.html:37 views.py:898 +#: topologie/templates/topologie/aff_switch.html:41 +#: topologie/templates/topologie/aff_switch_bay.html:36 +#: topologie/templates/topologie/index_p.html:37 msgid "Switch bay" msgstr "Baie de brassage" -#: templates/topologie/aff_switch.html:43 +#: topologie/templates/topologie/aff_switch.html:43 msgid "Ports" msgstr "Ports" -#: templates/topologie/aff_switch.html:47 -#: templates/topologie/edit_stack_sw.html:33 -msgid "Stack ID" -msgstr "ID de la pile" +#: topologie/templates/topologie/aff_switch.html:47 +msgid "Stack member ID" +msgstr "ID de membre dans la pile" -#: templates/topologie/aff_switch.html:76 +#: topologie/templates/topologie/aff_switch.html:48 +#: topologie/templates/topologie/index_p.html:38 +msgid "Switch model" +msgstr "Modèle de commutateur réseau" + +#: topologie/templates/topologie/aff_switch.html:76 msgid "Creation of ports" msgstr "Création de ports" -#: templates/topologie/aff_switch_bay.html:40 +#: topologie/templates/topologie/aff_switch_bay.html:40 msgid "Information" msgstr "Informations" -#: templates/topologie/aff_switch_bay.html:41 +#: topologie/templates/topologie/aff_switch_bay.html:41 msgid "Switches of the bay" msgstr "Commutateurs réseau de la baie" -#: templates/topologie/aff_vlanoptions.html:35 +#: topologie/templates/topologie/aff_vlanoptions.html:35 msgid "ARP protect" msgstr "Protection ARP" -#: templates/topologie/aff_vlanoptions.html:38 +#: topologie/templates/topologie/aff_vlanoptions.html:38 msgid "IGMP" msgstr "IGMP" -#: templates/topologie/aff_vlanoptions.html:39 +#: topologie/templates/topologie/aff_vlanoptions.html:39 msgid "MLD" msgstr "MLD" -#: templates/topologie/delete.html:29 templates/topologie/index.html:30 -#: templates/topologie/index_ap.html:30 -#: templates/topologie/index_model_switch.html:30 -#: templates/topologie/index_module.html:30 templates/topologie/index_p.html:30 -#: templates/topologie/index_physical_grouping.html:30 -#: templates/topologie/index_portprofile.html:29 -#: templates/topologie/index_room.html:30 templates/topologie/switch.html:30 -#: templates/topologie/topo.html:30 templates/topologie/topo_more.html:30 +#: topologie/templates/topologie/delete.html:29 +#: topologie/templates/topologie/index.html:30 +#: topologie/templates/topologie/index_ap.html:30 +#: topologie/templates/topologie/index_model_switch.html:30 +#: topologie/templates/topologie/index_module.html:30 +#: topologie/templates/topologie/index_p.html:30 +#: topologie/templates/topologie/index_physical_grouping.html:30 +#: topologie/templates/topologie/index_portprofile.html:29 +#: topologie/templates/topologie/index_room.html:30 +#: topologie/templates/topologie/switch.html:30 +#: topologie/templates/topologie/topo.html:30 +#: topologie/templates/topologie/topo_more.html:30 msgid "Topology" msgstr "Topologie" -#: templates/topologie/delete.html:35 +#: topologie/templates/topologie/delete.html:35 #, python-format msgid "" "Warning: are you sure you want to delete this %(objet_name)s object " @@ -642,188 +719,207 @@ msgstr "" "Attention : voulez-vous vraiment supprimer cet objet %(objet_name)s " "( %(objet)s ) ?" -#: templates/topologie/delete.html:36 +#: topologie/templates/topologie/delete.html:36 +#: topologie/templates/topologie/switch.html:46 +#: topologie/templates/topologie/topo_more.html:59 msgid "Confirm" msgstr "Confirmer" -#: templates/topologie/index.html:55 +#: topologie/templates/topologie/edit_stack_sw.html:33 +msgid "Stack ID" +msgstr "ID de la pile" + +#: topologie/templates/topologie/index.html:55 msgid "Topology of the switches" msgstr "Topologie des commutateurs réseau" -#: templates/topologie/index.html:68 -msgid " Add a switch" -msgstr " Ajouter un commutateur réseau" +#: topologie/templates/topologie/index.html:68 +msgid "Add a switch" +msgstr "Ajouter un commutateur réseau" -#: templates/topologie/index_ap.html:35 -msgid " Add an access point" -msgstr " Ajouter un point d'accès sans fil" +#: topologie/templates/topologie/index_ap.html:35 +msgid "Add an access point" +msgstr "Ajouter un point d'accès sans fil" -#: templates/topologie/index_model_switch.html:33 +#: topologie/templates/topologie/index_model_switch.html:33 msgid "Switch models" msgstr "Modèles de commutateur réseau" -#: templates/topologie/index_model_switch.html:36 -msgid " Add a switch model" -msgstr " Ajouter un modèle de commutateur réseau" +#: topologie/templates/topologie/index_model_switch.html:36 +msgid "Add a switch model" +msgstr "Ajouter un modèle de commutateur réseau" -#: templates/topologie/index_model_switch.html:42 +#: topologie/templates/topologie/index_model_switch.html:42 msgid "Switch constructors" msgstr "Constructeurs de commutateur réseau" -#: templates/topologie/index_model_switch.html:45 -msgid " Add a switch constructor" -msgstr " Ajouter un constructeur de commutateur réseau" +#: topologie/templates/topologie/index_model_switch.html:45 +msgid "Add a switch constructor" +msgstr "Ajouter un constructeur de commutateur réseau" -#: templates/topologie/index_module.html:33 templates/topologie/sidebar.html:39 +#: topologie/templates/topologie/index_module.html:33 +#: topologie/templates/topologie/sidebar.html:39 msgid "Switch modules" msgstr "Modules de commutateur réseau" -#: templates/topologie/index_module.html:35 -msgid " Add a module" -msgstr " Ajouter un module" +#: topologie/templates/topologie/index_module.html:35 +msgid "Add a module" +msgstr "Ajouter un module" -#: templates/topologie/index_p.html:33 +#: topologie/templates/topologie/index_p.html:33 msgid "Switch:" msgstr "Commutateur réseau :" -#: templates/topologie/index_physical_grouping.html:33 +#: topologie/templates/topologie/index_p.html:51 +msgid "Add a port" +msgstr "Ajouter un port" + +#: topologie/templates/topologie/index_p.html:52 +msgid "Add ports to the port list" +msgstr "Ajouter des ports à la liste des ports" + +#: topologie/templates/topologie/index_physical_grouping.html:33 msgid "Stacks" msgstr "Piles" -#: templates/topologie/index_physical_grouping.html:36 -msgid " Add a stack" -msgstr " Ajouter une pile" +#: topologie/templates/topologie/index_physical_grouping.html:36 +msgid "Add a stack" +msgstr "Ajouter une pile" -#: templates/topologie/index_physical_grouping.html:41 +#: topologie/templates/topologie/index_physical_grouping.html:41 msgid "Switch bays" msgstr "Baies de brassage" -#: templates/topologie/index_physical_grouping.html:44 -msgid " Add a switch bay" -msgstr " Ajouter une baie de brassage" +#: topologie/templates/topologie/index_physical_grouping.html:44 +msgid "Add a switch bay" +msgstr "Ajouter une baie de brassage" -#: templates/topologie/index_physical_grouping.html:50 -msgid "Buildings" -msgstr "Bâtiments" +#: topologie/templates/topologie/index_physical_grouping.html:53 +msgid "Add a building" +msgstr "Ajouter un bâtiment" -#: templates/topologie/index_physical_grouping.html:53 -msgid " Add a building" -msgstr " Ajouter un bâtiment" +#: topologie/templates/topologie/index_physical_grouping.html:60 +msgid "Dormitories" +msgstr "Résidences" -#: templates/topologie/index_portprofile.html:34 -#: templates/topologie/sidebar.html:43 +#: topologie/templates/topologie/index_physical_grouping.html:63 +msgid "Add a dormitory" +msgstr "Ajouter une résidence" + +#: topologie/templates/topologie/index_portprofile.html:34 +#: topologie/templates/topologie/sidebar.html:43 msgid "Port profiles" msgstr "Profils de port" -#: templates/topologie/index_portprofile.html:36 -msgid " Add a port profile" -msgstr " Ajouter un profil de port" +#: topologie/templates/topologie/index_portprofile.html:36 +msgid "Add a port profile" +msgstr "Ajouter un profil de port" -#: templates/topologie/index_portprofile.html:42 +#: topologie/templates/topologie/index_portprofile.html:42 msgid "VLAN security" msgstr "Sécurité de VLAN" -#: templates/topologie/index_room.html:33 +#: topologie/templates/topologie/index_room.html:33 msgid "Rooms" msgstr "Chambres" -#: templates/topologie/index_room.html:35 -msgid " Add a room" -msgstr " Ajouter une chambre" +#: topologie/templates/topologie/index_room.html:35 +msgid "Add a room" +msgstr "Ajouter une chambre" -#: templates/topologie/sidebar.html:31 +#: topologie/templates/topologie/sidebar.html:31 msgid "Rooms and premises" msgstr "Chambres et locaux" -#: templates/topologie/sidebar.html:51 +#: topologie/templates/topologie/sidebar.html:51 msgid "Physical grouping" msgstr "Groupements physiques" -#: templates/topologie/sidebar.html:55 +#: topologie/templates/topologie/sidebar.html:55 msgid "Switch models and constructors" msgstr "Modèles et constructeurs de commutateur réseau" -#: templates/topologie/switch.html:39 templates/topologie/topo.html:36 -msgid " Go to the ports list" -msgstr " Aller à la liste des ports" +#: topologie/templates/topologie/switch.html:39 +#: topologie/templates/topologie/topo.html:36 +msgid "Go to the ports list" +msgstr "Aller à la liste des ports" -#: templates/topologie/switch.html:43 +#: topologie/templates/topologie/switch.html:43 msgid "Specific settings for the switch" msgstr "Réglages spécifiques pour le commutateur réseau" -#: templates/topologie/switch.html:46 views.py:441 views.py:745 views.py:800 -#: views.py:859 views.py:914 views.py:969 views.py:1027 views.py:1081 -msgid "Create" -msgstr "Créer" - -#: templates/topologie/topo_more.html:48 +#: topologie/templates/topologie/topo_more.html:48 #, python-format msgid "Specific settings for the %(device)s object" msgstr "Réglages spécifiques pour l'objet %(device)s" -#: templates/topologie/topo_more.html:52 +#: topologie/templates/topologie/topo_more.html:52 #, python-format msgid "General settings for the machine linked to the %(device)s object" msgstr "Réglages généraux pour la machine liée à l'objet %(device)s" -#: templates/topologie/topo_more.html:59 -msgid "Create or edit" -msgstr "Créer ou modifier" - -#: views.py:347 +#: topologie/views.py:350 msgid "The VLAN was edited." msgstr "Le VLAN a été modifié." -#: views.py:350 views.py:403 views.py:457 views.py:762 views.py:821 -#: views.py:876 views.py:931 views.py:990 views.py:1045 views.py:1098 -#: views.py:1152 +#: topologie/views.py:353 topologie/views.py:403 topologie/views.py:460 +#: topologie/views.py:748 topologie/views.py:805 topologie/views.py:865 +#: topologie/views.py:927 topologie/views.py:988 topologie/views.py:1052 +#: topologie/views.py:1112 topologie/views.py:1161 topologie/views.py:1217 msgid "Edit" msgstr "Modifier" -#: views.py:363 views.py:554 +#: topologie/views.py:364 topologie/views.py:556 msgid "Nonexistent switch." msgstr "Commutateur réseau inexistant." -#: views.py:371 +#: topologie/views.py:372 msgid "The port was added." msgstr "Le port a été ajouté." -#: views.py:373 +#: topologie/views.py:374 msgid "The port already exists." msgstr "Le port existe déjà." -#: views.py:379 views.py:1135 +#: topologie/views.py:377 topologie/views.py:445 topologie/views.py:733 +#: topologie/views.py:787 topologie/views.py:848 topologie/views.py:909 +#: topologie/views.py:970 topologie/views.py:1032 topologie/views.py:1095 +#: topologie/views.py:1146 topologie/views.py:1200 msgid "Add" msgstr "Ajouter" -#: views.py:394 +#: topologie/views.py:393 msgid "The port was edited." msgstr "Le port a été modifié." -#: views.py:417 +#: topologie/views.py:417 msgid "The port was deleted." msgstr "Le port a été supprimé." -#: views.py:421 +#: topologie/views.py:423 #, python-format msgid "The port %s is used by another object, impossible to delete it." msgstr "Le port %s est utilisé par un autre objet, impossible de le supprimer." -#: views.py:438 +#: topologie/views.py:442 msgid "The stack was created." msgstr "La pile a été créée." -#: views.py:470 +#: topologie/views.py:457 +msgid "The stack was edited." +msgstr "La pile a été modifiée." + +#: topologie/views.py:471 msgid "The stack was deleted." msgstr "La pile a été supprimée." -#: views.py:474 +#: topologie/views.py:477 #, python-format msgid "The stack %s is used by another object, impossible to deleted it." msgstr "" "La pile %s est utilisée par un autre objet, impossible de la supprimer." -#: views.py:516 views.py:651 views.py:706 +#: topologie/views.py:516 topologie/views.py:638 topologie/views.py:692 msgid "" "The organisation's user doesn't exist yet, please create or link it in the " "preferences." @@ -831,113 +927,131 @@ msgstr "" "L'utilisateur de l'association n'existe pas encore, veuillez le créer ou le " "relier dans les préférences." -#: views.py:531 +#: topologie/views.py:533 msgid "The switch was created." msgstr "Le commutateur réseau a été créé." -#: views.py:568 +#: topologie/views.py:569 msgid "The ports were created." msgstr "Les ports ont été créés." -#: views.py:612 +#: topologie/views.py:605 msgid "The switch was edited." msgstr "Le commutateur réseau a été modifié." -#: views.py:666 +#: topologie/views.py:655 msgid "The access point was created." msgstr "Le point d'accès sans fil a été créé." -#: views.py:719 +#: topologie/views.py:707 msgid "The access point was edited." msgstr "Le point d'accès sans fil a été modifié." -#: views.py:742 +#: topologie/views.py:730 msgid "The room was created." msgstr "La chambre a été créée." -#: views.py:759 +#: topologie/views.py:745 msgid "The room was edited." msgstr "La chambre a été modifiée." -#: views.py:775 +#: topologie/views.py:759 msgid "The room was deleted." msgstr "La chambre a été supprimée." -#: views.py:779 +#: topologie/views.py:765 #, python-format msgid "The room %s is used by another object, impossible to deleted it." msgstr "" "La chambre %s est utilisée par un autre objet, impossible de la supprimer." -#: views.py:797 +#: topologie/views.py:784 msgid "The switch model was created." msgstr "Le modèle de commutateur réseau a été créé." -#: views.py:818 +#: topologie/views.py:802 msgid "The switch model was edited." msgstr "Le modèle de commutateur réseau a été modifié." -#: views.py:834 +#: topologie/views.py:818 msgid "The switch model was deleted." msgstr "Le modèle de commutateur réseau a été supprimé." -#: views.py:838 +#: topologie/views.py:824 #, python-format msgid "The switch model %s is used by another object, impossible to delete it." msgstr "" "Le modèle de commutateur réseau %s est utilisé par un autre objet, " "impossible de le supprimer." -#: views.py:856 +#: topologie/views.py:845 msgid "The switch bay was created." msgstr "La baie de brassage a été créée." -#: views.py:873 +#: topologie/views.py:862 msgid "The switch bay was edited." msgstr "La baie de brassage a été modifiée." -#: views.py:889 +#: topologie/views.py:878 msgid "The switch bay was deleted." msgstr "La baie de brassage a été supprimée." -#: views.py:893 +#: topologie/views.py:884 #, python-format msgid "The switch bay %s is used by another object, impossible to delete it." msgstr "" "La baie de brassage %s est utilisée par un autre objet, impossible de la " "supprimer." -#: views.py:911 +#: topologie/views.py:906 msgid "The building was created." msgstr "Le bâtiment a été créé." -#: views.py:928 +#: topologie/views.py:924 msgid "The building was edited." msgstr "Le bâtiment a été modifié." -#: views.py:944 +#: topologie/views.py:939 msgid "The building was deleted." msgstr "Le bâtiment a été supprimé." -#: views.py:948 +#: topologie/views.py:945 #, python-format msgid "The building %s is used by another object, impossible to delete it." msgstr "" "Le bâtiment %s est utilisé par un autre objet, impossible de le supprimer." -#: views.py:966 +#: topologie/views.py:967 +msgid "The dormitory was created." +msgstr "La résidence a été créée." + +#: topologie/views.py:985 +msgid "The dormitory was edited." +msgstr "La résidence a été modifiée." + +#: topologie/views.py:1002 +msgid "The dormitory was deleted." +msgstr "La résidence a été supprimée." + +#: topologie/views.py:1008 +#, python-format +msgid "The dormitory %s is used by another object, impossible to delete it." +msgstr "" +"La résidence %s est utilisée par un autre objet, impossible de la supprimer." + +#: topologie/views.py:1029 msgid "The switch constructor was created." msgstr "Le constructeur de commutateur réseau a été créé." -#: views.py:987 +#: topologie/views.py:1049 msgid "The switch constructor was edited." msgstr "Le constructeur de commutateur réseau a été modifié." -#: views.py:1003 +#: topologie/views.py:1065 msgid "The switch constructor was deleted." msgstr "Le constructeur de commutateur réseau a été supprimé." -#: views.py:1007 +#: topologie/views.py:1071 #, python-format msgid "" "The switch constructor %s is used by another object, impossible to delete it." @@ -945,49 +1059,49 @@ msgstr "" "Le constructeur de commutateur réseau %s est utilisé par un autre objet, " "impossible de le supprimer." -#: views.py:1024 +#: topologie/views.py:1092 msgid "The port profile was created." msgstr "Le profil de port a été créé." -#: views.py:1042 +#: topologie/views.py:1109 msgid "The port profile was edited." msgstr "Le profil de port a été modifié." -#: views.py:1059 +#: topologie/views.py:1125 msgid "The port profile was deleted." msgstr "Le profil de port a été supprimé." -#: views.py:1062 +#: topologie/views.py:1127 msgid "Impossible to delete the port profile." msgstr "Impossible de supprimer le profil de port." -#: views.py:1078 +#: topologie/views.py:1143 msgid "The module was created." msgstr "Le module a été créé." -#: views.py:1095 views.py:1149 +#: topologie/views.py:1158 topologie/views.py:1214 msgid "The module was edited." msgstr "Le module a été modifié." -#: views.py:1111 views.py:1165 +#: topologie/views.py:1172 topologie/views.py:1228 msgid "The module was deleted." msgstr "Le module a été supprimé." -#: views.py:1115 views.py:1169 +#: topologie/views.py:1178 topologie/views.py:1234 #, python-format msgid "The module %s is used by another object, impossible to deleted it." msgstr "" "Le module %s est utilisé par un autre objet, impossible de le supprimer." -#: views.py:1120 views.py:1174 -msgid "Module" -msgstr "Module" +#: topologie/views.py:1186 topologie/views.py:1242 +msgid "module" +msgstr "module" -#: views.py:1132 +#: topologie/views.py:1197 msgid "The module was added." msgstr "Le module a été ajouté." -#: views.py:1291 +#: topologie/views.py:1408 msgid "" "The default Django template isn't used. This can lead to rendering errors. " "Check the parameters." diff --git a/topologie/migrations/0001_initial.py b/topologie/migrations/0001_initial.py index 78cd6524..5f66ecdb 100644 --- a/topologie/migrations/0001_initial.py +++ b/topologie/migrations/0001_initial.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,17 +28,24 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='Switch', + name="Switch", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, verbose_name='ID', serialize=False)), - ('building', models.CharField(max_length=10)), - ('number', models.IntegerField()), - ('details', models.CharField(max_length=255, blank=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + verbose_name="ID", + serialize=False, + ), + ), + ("building", models.CharField(max_length=10)), + ("number", models.IntegerField()), + ("details", models.CharField(max_length=255, blank=True)), ], - ), + ) ] diff --git a/topologie/migrations/0002_auto_20160703_1118.py b/topologie/migrations/0002_auto_20160703_1118.py index 3440a1c7..cae34d32 100644 --- a/topologie/migrations/0002_auto_20160703_1118.py +++ b/topologie/migrations/0002_auto_20160703_1118.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,24 +29,39 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('contenttypes', '0002_remove_content_type_name'), - ('topologie', '0001_initial'), + ("contenttypes", "0002_remove_content_type_name"), + ("topologie", "0001_initial"), ] operations = [ migrations.CreateModel( - name='Port', + name="Port", fields=[ - ('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')), - ('port', models.IntegerField()), - ('details', models.CharField(max_length=255, blank=True)), - ('_object_id', models.PositiveIntegerField(null=True, blank=True)), - ('_content_type', models.ForeignKey(null=True, blank=True, to='contenttypes.ContentType')), - ('switch', models.ForeignKey(related_name='ports', to='topologie.Switch')), + ( + "id", + models.AutoField( + primary_key=True, + serialize=False, + auto_created=True, + verbose_name="ID", + ), + ), + ("port", models.IntegerField()), + ("details", models.CharField(max_length=255, blank=True)), + ("_object_id", models.PositiveIntegerField(null=True, blank=True)), + ( + "_content_type", + models.ForeignKey( + null=True, blank=True, to="contenttypes.ContentType" + ), + ), + ( + "switch", + models.ForeignKey(related_name="ports", to="topologie.Switch"), + ), ], ), migrations.AlterUniqueTogether( - name='port', - unique_together=set([('_content_type', '_object_id')]), + name="port", unique_together=set([("_content_type", "_object_id")]) ), ] diff --git a/topologie/migrations/0003_room.py b/topologie/migrations/0003_room.py index 89112861..8d476fcd 100644 --- a/topologie/migrations/0003_room.py +++ b/topologie/migrations/0003_room.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,18 +28,24 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0002_auto_20160703_1118'), - ] + dependencies = [("topologie", "0002_auto_20160703_1118")] operations = [ migrations.CreateModel( - name='Room', + name="Room", fields=[ - ('id', models.AutoField(serialize=False, auto_created=True, verbose_name='ID', primary_key=True)), - ('details', models.CharField(max_length=255, blank=True)), - ('building', models.CharField(max_length=255, unique=True)), - ('number', models.IntegerField()), + ( + "id", + models.AutoField( + serialize=False, + auto_created=True, + verbose_name="ID", + primary_key=True, + ), + ), + ("details", models.CharField(max_length=255, blank=True)), + ("building", models.CharField(max_length=255, unique=True)), + ("number", models.IntegerField()), ], - ), + ) ] diff --git a/topologie/migrations/0004_auto_20160703_1122.py b/topologie/migrations/0004_auto_20160703_1122.py index b5b9d14a..5b8a6038 100644 --- a/topologie/migrations/0004_auto_20160703_1122.py +++ b/topologie/migrations/0004_auto_20160703_1122.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,13 +28,10 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0003_room'), - ] + dependencies = [("topologie", "0003_room")] operations = [ migrations.AlterUniqueTogether( - name='switch', - unique_together=set([('building', 'number')]), - ), + name="switch", unique_together=set([("building", "number")]) + ) ] diff --git a/topologie/migrations/0005_auto_20160703_1123.py b/topologie/migrations/0005_auto_20160703_1123.py index 0ceb531a..78510538 100644 --- a/topologie/migrations/0005_auto_20160703_1123.py +++ b/topologie/migrations/0005_auto_20160703_1123.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,13 +28,10 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0004_auto_20160703_1122'), - ] + dependencies = [("topologie", "0004_auto_20160703_1122")] operations = [ migrations.AlterUniqueTogether( - name='room', - unique_together=set([('building', 'number')]), - ), + name="room", unique_together=set([("building", "number")]) + ) ] diff --git a/topologie/migrations/0006_auto_20160703_1129.py b/topologie/migrations/0006_auto_20160703_1129.py index c4f8ebba..df20974c 100644 --- a/topologie/migrations/0006_auto_20160703_1129.py +++ b/topologie/migrations/0006_auto_20160703_1129.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,29 +28,22 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0005_auto_20160703_1123'), - ] + dependencies = [("topologie", "0005_auto_20160703_1123")] operations = [ migrations.AddField( - model_name='room', - name='room', + model_name="room", + name="room", field=models.IntegerField(default=1), preserve_default=False, ), migrations.AlterField( - model_name='room', - name='building', - field=models.CharField(max_length=255), + model_name="room", name="building", field=models.CharField(max_length=255) ), migrations.AlterField( - model_name='room', - name='number', - field=models.IntegerField(blank=True), + model_name="room", name="number", field=models.IntegerField(blank=True) ), migrations.AlterUniqueTogether( - name='room', - unique_together=set([('building', 'room', 'number')]), + name="room", unique_together=set([("building", "room", "number")]) ), ] diff --git a/topologie/migrations/0007_auto_20160703_1148.py b/topologie/migrations/0007_auto_20160703_1148.py index 28ab8699..df01f4fa 100644 --- a/topologie/migrations/0007_auto_20160703_1148.py +++ b/topologie/migrations/0007_auto_20160703_1148.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0006_auto_20160703_1129'), - ] + dependencies = [("topologie", "0006_auto_20160703_1129")] operations = [ migrations.AlterField( - model_name='room', - name='number', + model_name="room", + name="number", field=models.IntegerField(null=True, blank=True), - ), + ) ] diff --git a/topologie/migrations/0008_port_room.py b/topologie/migrations/0008_port_room.py index 9ffb3bef..5dba66aa 100644 --- a/topologie/migrations/0008_port_room.py +++ b/topologie/migrations/0008_port_room.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,15 +29,18 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0007_auto_20160703_1148'), - ] + dependencies = [("topologie", "0007_auto_20160703_1148")] operations = [ migrations.AddField( - model_name='port', - name='room', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, blank=True, default=1, to='topologie.Room'), + model_name="port", + name="room", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + blank=True, + default=1, + to="topologie.Room", + ), preserve_default=False, - ), + ) ] diff --git a/topologie/migrations/0009_auto_20160703_1200.py b/topologie/migrations/0009_auto_20160703_1200.py index 4d17d332..6db065f2 100644 --- a/topologie/migrations/0009_auto_20160703_1200.py +++ b/topologie/migrations/0009_auto_20160703_1200.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,14 +29,17 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0008_port_room'), - ] + dependencies = [("topologie", "0008_port_room")] operations = [ migrations.AlterField( - model_name='port', - name='room', - field=models.ForeignKey(to='topologie.Room', on_delete=django.db.models.deletion.PROTECT, blank=True, null=True), - ), + model_name="port", + name="room", + field=models.ForeignKey( + to="topologie.Room", + on_delete=django.db.models.deletion.PROTECT, + blank=True, + null=True, + ), + ) ] diff --git a/topologie/migrations/0010_auto_20160704_2148.py b/topologie/migrations/0010_auto_20160704_2148.py index a38e92e6..5e51a5da 100644 --- a/topologie/migrations/0010_auto_20160704_2148.py +++ b/topologie/migrations/0010_auto_20160704_2148.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,30 +28,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0009_auto_20160703_1200'), - ] + dependencies = [("topologie", "0009_auto_20160703_1200")] operations = [ - migrations.RenameField( - model_name='room', - old_name='building', - new_name='name', - ), - migrations.AlterUniqueTogether( - name='room', - unique_together=set([]), - ), - migrations.RemoveField( - model_name='room', - name='details', - ), - migrations.RemoveField( - model_name='room', - name='number', - ), - migrations.RemoveField( - model_name='room', - name='room', - ), + migrations.RenameField(model_name="room", old_name="building", new_name="name"), + migrations.AlterUniqueTogether(name="room", unique_together=set([])), + migrations.RemoveField(model_name="room", name="details"), + migrations.RemoveField(model_name="room", name="number"), + migrations.RemoveField(model_name="room", name="room"), ] diff --git a/topologie/migrations/0011_auto_20160704_2153.py b/topologie/migrations/0011_auto_20160704_2153.py index 0ffc658f..18caec08 100644 --- a/topologie/migrations/0011_auto_20160704_2153.py +++ b/topologie/migrations/0011_auto_20160704_2153.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0010_auto_20160704_2148'), - ] + dependencies = [("topologie", "0010_auto_20160704_2148")] operations = [ migrations.AlterField( - model_name='room', - name='name', + model_name="room", + name="name", field=models.CharField(max_length=255, unique=True), - ), + ) ] diff --git a/topologie/migrations/0012_port_machine_interface.py b/topologie/migrations/0012_port_machine_interface.py index a248242b..e9518085 100644 --- a/topologie/migrations/0012_port_machine_interface.py +++ b/topologie/migrations/0012_port_machine_interface.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -30,14 +30,19 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('machines', '0014_auto_20160706_1220'), - ('topologie', '0011_auto_20160704_2153'), + ("machines", "0014_auto_20160706_1220"), + ("topologie", "0011_auto_20160704_2153"), ] operations = [ migrations.AddField( - model_name='port', - name='machine_interface', - field=models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, null=True, blank=True, to='machines.Interface'), - ), + model_name="port", + name="machine_interface", + field=models.OneToOneField( + on_delete=django.db.models.deletion.PROTECT, + null=True, + blank=True, + to="machines.Interface", + ), + ) ] diff --git a/topologie/migrations/0013_port_related.py b/topologie/migrations/0013_port_related.py index 405fa211..057dc48c 100644 --- a/topologie/migrations/0013_port_related.py +++ b/topologie/migrations/0013_port_related.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0012_port_machine_interface'), - ] + dependencies = [("topologie", "0012_port_machine_interface")] operations = [ migrations.AddField( - model_name='port', - name='related', - field=models.OneToOneField(null=True, to='topologie.Port', blank=True, related_name='related_port'), - ), + model_name="port", + name="related", + field=models.OneToOneField( + null=True, to="topologie.Port", blank=True, related_name="related_port" + ), + ) ] diff --git a/topologie/migrations/0014_auto_20160706_1238.py b/topologie/migrations/0014_auto_20160706_1238.py index aee43d73..9a3f44e8 100644 --- a/topologie/migrations/0014_auto_20160706_1238.py +++ b/topologie/migrations/0014_auto_20160706_1238.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,21 +28,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0013_port_related'), - ] + dependencies = [("topologie", "0013_port_related")] operations = [ migrations.AlterUniqueTogether( - name='port', - unique_together=set([('switch', 'port')]), - ), - migrations.RemoveField( - model_name='port', - name='_content_type', - ), - migrations.RemoveField( - model_name='port', - name='_object_id', + name="port", unique_together=set([("switch", "port")]) ), + migrations.RemoveField(model_name="port", name="_content_type"), + migrations.RemoveField(model_name="port", name="_object_id"), ] diff --git a/topologie/migrations/0015_auto_20160706_1452.py b/topologie/migrations/0015_auto_20160706_1452.py index 849a3dfe..dc9c8c4b 100644 --- a/topologie/migrations/0015_auto_20160706_1452.py +++ b/topologie/migrations/0015_auto_20160706_1452.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,18 +28,15 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0014_auto_20160706_1238'), - ] + dependencies = [("topologie", "0014_auto_20160706_1238")] operations = [ - migrations.RemoveField( - model_name='port', - name='related', - ), + migrations.RemoveField(model_name="port", name="related"), migrations.AddField( - model_name='port', - name='related', - field=models.ManyToManyField(related_name='_port_related_+', to='topologie.Port', blank=True), + model_name="port", + name="related", + field=models.ManyToManyField( + related_name="_port_related_+", to="topologie.Port", blank=True + ), ), ] diff --git a/topologie/migrations/0016_auto_20160706_1531.py b/topologie/migrations/0016_auto_20160706_1531.py index 3187b0ad..dcbed85e 100644 --- a/topologie/migrations/0016_auto_20160706_1531.py +++ b/topologie/migrations/0016_auto_20160706_1531.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,18 +28,15 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0015_auto_20160706_1452'), - ] + dependencies = [("topologie", "0015_auto_20160706_1452")] operations = [ - migrations.RemoveField( - model_name='port', - name='related', - ), + migrations.RemoveField(model_name="port", name="related"), migrations.AddField( - model_name='port', - name='related', - field=models.OneToOneField(blank=True, to='topologie.Port', related_name='related_port', null=True), + model_name="port", + name="related", + field=models.OneToOneField( + blank=True, to="topologie.Port", related_name="related_port", null=True + ), ), ] diff --git a/topologie/migrations/0017_auto_20160718_1141.py b/topologie/migrations/0017_auto_20160718_1141.py index 178b9acd..0d56607a 100644 --- a/topologie/migrations/0017_auto_20160718_1141.py +++ b/topologie/migrations/0017_auto_20160718_1141.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,14 +29,17 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0016_auto_20160706_1531'), - ] + dependencies = [("topologie", "0016_auto_20160706_1531")] operations = [ migrations.AlterField( - model_name='port', - name='machine_interface', - field=models.OneToOneField(to='machines.Interface', on_delete=django.db.models.deletion.SET_NULL, null=True, blank=True), - ), + model_name="port", + name="machine_interface", + field=models.OneToOneField( + to="machines.Interface", + on_delete=django.db.models.deletion.SET_NULL, + null=True, + blank=True, + ), + ) ] diff --git a/topologie/migrations/0018_room_details.py b/topologie/migrations/0018_room_details.py index 84bb19a6..fcb64901 100644 --- a/topologie/migrations/0018_room_details.py +++ b/topologie/migrations/0018_room_details.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0017_auto_20160718_1141'), - ] + dependencies = [("topologie", "0017_auto_20160718_1141")] operations = [ migrations.AddField( - model_name='room', - name='details', + model_name="room", + name="details", field=models.CharField(blank=True, max_length=255), - ), + ) ] diff --git a/topologie/migrations/0019_auto_20161026_1348.py b/topologie/migrations/0019_auto_20161026_1348.py index 84562039..9000cc64 100644 --- a/topologie/migrations/0019_auto_20161026_1348.py +++ b/topologie/migrations/0019_auto_20161026_1348.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,29 +29,23 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('machines', '0026_auto_20161026_1348'), - ('topologie', '0018_room_details'), + ("machines", "0026_auto_20161026_1348"), + ("topologie", "0018_room_details"), ] operations = [ migrations.AddField( - model_name='switch', - name='location', - field=models.CharField(default='test', max_length=255), + model_name="switch", + name="location", + field=models.CharField(default="test", max_length=255), preserve_default=False, ), migrations.AddField( - model_name='switch', - name='switch_interface', - field=models.OneToOneField(default=1, to='machines.Interface'), + model_name="switch", + name="switch_interface", + field=models.OneToOneField(default=1, to="machines.Interface"), preserve_default=False, ), - migrations.AlterUniqueTogether( - name='switch', - unique_together=set([]), - ), - migrations.RemoveField( - model_name='switch', - name='building', - ), + migrations.AlterUniqueTogether(name="switch", unique_together=set([])), + migrations.RemoveField(model_name="switch", name="building"), ] diff --git a/topologie/migrations/0020_auto_20161119_0033.py b/topologie/migrations/0020_auto_20161119_0033.py index 6bb23fc9..f6714a0b 100644 --- a/topologie/migrations/0020_auto_20161119_0033.py +++ b/topologie/migrations/0020_auto_20161119_0033.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,14 +29,17 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0019_auto_20161026_1348'), - ] + dependencies = [("topologie", "0019_auto_20161026_1348")] operations = [ migrations.AlterField( - model_name='port', - name='machine_interface', - field=models.ForeignKey(blank=True, to='machines.Interface', null=True, on_delete=django.db.models.deletion.SET_NULL), - ), + model_name="port", + name="machine_interface", + field=models.ForeignKey( + blank=True, + to="machines.Interface", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + ), + ) ] diff --git a/topologie/migrations/0021_port_radius.py b/topologie/migrations/0021_port_radius.py index 9f49bacd..db0a401f 100644 --- a/topologie/migrations/0021_port_radius.py +++ b/topologie/migrations/0021_port_radius.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,25 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0020_auto_20161119_0033'), - ] + dependencies = [("topologie", "0020_auto_20161119_0033")] operations = [ migrations.AddField( - model_name='port', - name='radius', - field=models.CharField(choices=[('NO', 'NO'), ('STRICT', 'STRICT'), ('BLOQ', 'BLOQ'), ('COMMON', 'COMMON'), (7, 7), (8, 8), (42, 42), (69, 69)], max_length=32, default='NO'), - ), + model_name="port", + name="radius", + field=models.CharField( + choices=[ + ("NO", "NO"), + ("STRICT", "STRICT"), + ("BLOQ", "BLOQ"), + ("COMMON", "COMMON"), + (7, 7), + (8, 8), + (42, 42), + (69, 69), + ], + max_length=32, + default="NO", + ), + ) ] diff --git a/topologie/migrations/0022_auto_20161211_1622.py b/topologie/migrations/0022_auto_20161211_1622.py index 92413125..52e0fcbb 100644 --- a/topologie/migrations/0022_auto_20161211_1622.py +++ b/topologie/migrations/0022_auto_20161211_1622.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,25 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0021_port_radius'), - ] + dependencies = [("topologie", "0021_port_radius")] operations = [ migrations.AlterField( - model_name='port', - name='radius', - field=models.CharField(max_length=32, default='NO', choices=[('NO', 'NO'), ('STRICT', 'STRICT'), ('BLOQ', 'BLOQ'), ('COMMON', 'COMMON'), ('7', '7'), ('8', '8'), ('42', '42'), ('69', '69')]), - ), + model_name="port", + name="radius", + field=models.CharField( + max_length=32, + default="NO", + choices=[ + ("NO", "NO"), + ("STRICT", "STRICT"), + ("BLOQ", "BLOQ"), + ("COMMON", "COMMON"), + ("7", "7"), + ("8", "8"), + ("42", "42"), + ("69", "69"), + ], + ), + ) ] diff --git a/topologie/migrations/0023_auto_20170817_1654.py b/topologie/migrations/0023_auto_20170817_1654.py index 0f84d7de..6808e576 100644 --- a/topologie/migrations/0023_auto_20170817_1654.py +++ b/topologie/migrations/0023_auto_20170817_1654.py @@ -8,34 +8,44 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0022_auto_20161211_1622'), - ] + dependencies = [("topologie", "0022_auto_20161211_1622")] operations = [ migrations.CreateModel( - name='Stack', + name="Stack", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(blank=True, max_length=32, null=True)), - ('stack_id', models.CharField(max_length=32, unique=True)), - ('details', models.CharField(blank=True, max_length=255, null=True)), - ('member_id_min', models.IntegerField()), - ('member_id_max', models.IntegerField()), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(blank=True, max_length=32, null=True)), + ("stack_id", models.CharField(max_length=32, unique=True)), + ("details", models.CharField(blank=True, max_length=255, null=True)), + ("member_id_min", models.IntegerField()), + ("member_id_max", models.IntegerField()), ], ), migrations.AddField( - model_name='switch', - name='stack_member_id', + model_name="switch", + name="stack_member_id", field=models.IntegerField(blank=True, null=True), ), migrations.AddField( - model_name='switch', - name='stack', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='topologie.Stack'), + model_name="switch", + name="stack", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="topologie.Stack", + ), ), migrations.AlterUniqueTogether( - name='switch', - unique_together=set([('stack', 'stack_member_id')]), + name="switch", unique_together=set([("stack", "stack_member_id")]) ), ] diff --git a/topologie/migrations/0023_auto_20170826_1530.py b/topologie/migrations/0023_auto_20170826_1530.py index 594e7e9f..ed879cff 100644 --- a/topologie/migrations/0023_auto_20170826_1530.py +++ b/topologie/migrations/0023_auto_20170826_1530.py @@ -7,14 +7,28 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0022_auto_20161211_1622'), - ] + dependencies = [("topologie", "0022_auto_20161211_1622")] operations = [ migrations.AlterField( - model_name='port', - name='radius', - field=models.CharField(choices=[('NO', 'NO'), ('STRICT', 'STRICT'), ('BLOQ', 'BLOQ'), ('COMMON', 'COMMON'), ('3', '3'), ('7', '7'), ('8', '8'), ('13', '13'), ('20', '20'), ('42', '42'), ('69', '69')], default='NO', max_length=32), - ), + model_name="port", + name="radius", + field=models.CharField( + choices=[ + ("NO", "NO"), + ("STRICT", "STRICT"), + ("BLOQ", "BLOQ"), + ("COMMON", "COMMON"), + ("3", "3"), + ("7", "7"), + ("8", "8"), + ("13", "13"), + ("20", "20"), + ("42", "42"), + ("69", "69"), + ], + default="NO", + max_length=32, + ), + ) ] diff --git a/topologie/migrations/0024_auto_20170818_1021.py b/topologie/migrations/0024_auto_20170818_1021.py index 0d11be65..4caf7d3e 100644 --- a/topologie/migrations/0024_auto_20170818_1021.py +++ b/topologie/migrations/0024_auto_20170818_1021.py @@ -8,14 +8,17 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0023_auto_20170817_1654'), - ] + dependencies = [("topologie", "0023_auto_20170817_1654")] operations = [ migrations.AlterField( - model_name='switch', - name='stack', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.Stack'), - ), + model_name="switch", + name="stack", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="topologie.Stack", + ), + ) ] diff --git a/topologie/migrations/0024_auto_20170826_1800.py b/topologie/migrations/0024_auto_20170826_1800.py index ce108929..ee1ea2d2 100644 --- a/topologie/migrations/0024_auto_20170826_1800.py +++ b/topologie/migrations/0024_auto_20170826_1800.py @@ -7,14 +7,21 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0023_auto_20170826_1530'), - ] + dependencies = [("topologie", "0023_auto_20170826_1530")] operations = [ migrations.AlterField( - model_name='port', - name='radius', - field=models.CharField(choices=[('NO', 'NO'), ('STRICT', 'STRICT'), ('BLOQ', 'BLOQ'), ('COMMON', 'COMMON')], default='NO', max_length=32), - ), + model_name="port", + name="radius", + field=models.CharField( + choices=[ + ("NO", "NO"), + ("STRICT", "STRICT"), + ("BLOQ", "BLOQ"), + ("COMMON", "COMMON"), + ], + default="NO", + max_length=32, + ), + ) ] diff --git a/topologie/migrations/0025_merge_20170902_1242.py b/topologie/migrations/0025_merge_20170902_1242.py index 23632040..52a5a365 100644 --- a/topologie/migrations/0025_merge_20170902_1242.py +++ b/topologie/migrations/0025_merge_20170902_1242.py @@ -8,9 +8,8 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('topologie', '0024_auto_20170818_1021'), - ('topologie', '0024_auto_20170826_1800'), + ("topologie", "0024_auto_20170818_1021"), + ("topologie", "0024_auto_20170826_1800"), ] - operations = [ - ] + operations = [] diff --git a/topologie/migrations/0026_auto_20170902_1245.py b/topologie/migrations/0026_auto_20170902_1245.py index e6bd4d96..d7389554 100644 --- a/topologie/migrations/0026_auto_20170902_1245.py +++ b/topologie/migrations/0026_auto_20170902_1245.py @@ -7,14 +7,28 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0025_merge_20170902_1242'), - ] + dependencies = [("topologie", "0025_merge_20170902_1242")] operations = [ migrations.AlterField( - model_name='port', - name='radius', - field=models.CharField(choices=[('NO', 'NO'), ('STRICT', 'STRICT'), ('BLOQ', 'BLOQ'), ('COMMON', 'COMMON'), ('3', '3'), ('7', '7'), ('8', '8'), ('13', '13'), ('20', '20'), ('42', '42'), ('69', '69')], default='NO', max_length=32), - ), + model_name="port", + name="radius", + field=models.CharField( + choices=[ + ("NO", "NO"), + ("STRICT", "STRICT"), + ("BLOQ", "BLOQ"), + ("COMMON", "COMMON"), + ("3", "3"), + ("7", "7"), + ("8", "8"), + ("13", "13"), + ("20", "20"), + ("42", "42"), + ("69", "69"), + ], + default="NO", + max_length=32, + ), + ) ] diff --git a/topologie/migrations/0027_auto_20170905_1442.py b/topologie/migrations/0027_auto_20170905_1442.py index 5f6a682e..7a33fab9 100644 --- a/topologie/migrations/0027_auto_20170905_1442.py +++ b/topologie/migrations/0027_auto_20170905_1442.py @@ -7,18 +7,22 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0026_auto_20170902_1245'), - ] + dependencies = [("topologie", "0026_auto_20170902_1245")] operations = [ - migrations.AlterModelOptions( - name='room', - options={'ordering': ['name']}, - ), + migrations.AlterModelOptions(name="room", options={"ordering": ["name"]}), migrations.AlterField( - model_name='port', - name='radius', - field=models.CharField(choices=[('NO', 'NO'), ('STRICT', 'STRICT'), ('BLOQ', 'BLOQ'), ('COMMON', 'COMMON')], default='NO', max_length=32), + model_name="port", + name="radius", + field=models.CharField( + choices=[ + ("NO", "NO"), + ("STRICT", "STRICT"), + ("BLOQ", "BLOQ"), + ("COMMON", "COMMON"), + ], + default="NO", + max_length=32, + ), ), ] diff --git a/topologie/migrations/0028_auto_20170913_1503.py b/topologie/migrations/0028_auto_20170913_1503.py index 139af559..5ab48720 100644 --- a/topologie/migrations/0028_auto_20170913_1503.py +++ b/topologie/migrations/0028_auto_20170913_1503.py @@ -7,14 +7,27 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0027_auto_20170905_1442'), - ] + dependencies = [("topologie", "0027_auto_20170905_1442")] operations = [ migrations.AlterField( - model_name='port', - name='radius', - field=models.CharField(choices=[('NO', 'NO'), ('STRICT', 'STRICT'), ('BLOQ', 'BLOQ'), ('COMMON', 'COMMON'), ('2', '2'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('20', '20')], default='NO', max_length=32), - ), + model_name="port", + name="radius", + field=models.CharField( + choices=[ + ("NO", "NO"), + ("STRICT", "STRICT"), + ("BLOQ", "BLOQ"), + ("COMMON", "COMMON"), + ("2", "2"), + ("4", "4"), + ("5", "5"), + ("6", "6"), + ("7", "7"), + ("20", "20"), + ], + default="NO", + max_length=32, + ), + ) ] diff --git a/topologie/migrations/0029_auto_20171002_0334.py b/topologie/migrations/0029_auto_20171002_0334.py index 73ddff1b..09b3466a 100644 --- a/topologie/migrations/0029_auto_20171002_0334.py +++ b/topologie/migrations/0029_auto_20171002_0334.py @@ -7,14 +7,28 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0028_auto_20170913_1503'), - ] + dependencies = [("topologie", "0028_auto_20170913_1503")] operations = [ migrations.AlterField( - model_name='port', - name='radius', - field=models.CharField(choices=[('NO', 'NO'), ('STRICT', 'STRICT'), ('BLOQ', 'BLOQ'), ('COMMON', 'COMMON'), ('3', '3'), ('7', '7'), ('8', '8'), ('13', '13'), ('20', '20'), ('42', '42'), ('69', '69')], default='NO', max_length=32), - ), + model_name="port", + name="radius", + field=models.CharField( + choices=[ + ("NO", "NO"), + ("STRICT", "STRICT"), + ("BLOQ", "BLOQ"), + ("COMMON", "COMMON"), + ("3", "3"), + ("7", "7"), + ("8", "8"), + ("13", "13"), + ("20", "20"), + ("42", "42"), + ("69", "69"), + ], + default="NO", + max_length=32, + ), + ) ] diff --git a/topologie/migrations/0030_auto_20171004_0235.py b/topologie/migrations/0030_auto_20171004_0235.py index ac3ceeba..8e0989b2 100644 --- a/topologie/migrations/0030_auto_20171004_0235.py +++ b/topologie/migrations/0030_auto_20171004_0235.py @@ -8,20 +8,31 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0029_auto_20171002_0334'), - ('machines', '0049_vlan'), - ] + dependencies = [("topologie", "0029_auto_20171002_0334"), ("machines", "0049_vlan")] operations = [ migrations.AddField( - model_name='port', - name='vlan_force', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='machines.Vlan'), + model_name="port", + name="vlan_force", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="machines.Vlan", + ), ), migrations.AlterField( - model_name='port', - name='radius', - field=models.CharField(choices=[('NO', 'NO'), ('STRICT', 'STRICT'), ('BLOQ', 'BLOQ'), ('COMMON', 'COMMON')], default='NO', max_length=32), + model_name="port", + name="radius", + field=models.CharField( + choices=[ + ("NO", "NO"), + ("STRICT", "STRICT"), + ("BLOQ", "BLOQ"), + ("COMMON", "COMMON"), + ], + default="NO", + max_length=32, + ), ), ] diff --git a/topologie/migrations/0031_auto_20171015_2033.py b/topologie/migrations/0031_auto_20171015_2033.py index 674af6c6..f222f272 100644 --- a/topologie/migrations/0031_auto_20171015_2033.py +++ b/topologie/migrations/0031_auto_20171015_2033.py @@ -7,34 +7,28 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0030_auto_20171004_0235'), - ] + dependencies = [("topologie", "0030_auto_20171004_0235")] operations = [ migrations.AlterField( - model_name='port', - name='port', + model_name="port", name="port", field=models.PositiveIntegerField() + ), + migrations.AlterField( + model_name="stack", + name="member_id_max", field=models.PositiveIntegerField(), ), migrations.AlterField( - model_name='stack', - name='member_id_max', + model_name="stack", + name="member_id_min", field=models.PositiveIntegerField(), ), migrations.AlterField( - model_name='stack', - name='member_id_min', - field=models.PositiveIntegerField(), + model_name="switch", name="number", field=models.PositiveIntegerField() ), migrations.AlterField( - model_name='switch', - name='number', - field=models.PositiveIntegerField(), - ), - migrations.AlterField( - model_name='switch', - name='stack_member_id', + model_name="switch", + name="stack_member_id", field=models.PositiveIntegerField(blank=True, null=True), ), ] diff --git a/topologie/migrations/0032_auto_20171026_0338.py b/topologie/migrations/0032_auto_20171026_0338.py index 37548306..cb0f6e88 100644 --- a/topologie/migrations/0032_auto_20171026_0338.py +++ b/topologie/migrations/0032_auto_20171026_0338.py @@ -8,29 +8,54 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0031_auto_20171015_2033'), - ] + dependencies = [("topologie", "0031_auto_20171015_2033")] operations = [ migrations.CreateModel( - name='ConstructorSwitch', + name="ConstructorSwitch", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)), ], ), migrations.CreateModel( - name='ModelSwitch', + name="ModelSwitch", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('reference', models.CharField(max_length=255)), - ('constructor', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='topologie.ConstructorSwitch')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("reference", models.CharField(max_length=255)), + ( + "constructor", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to="topologie.ConstructorSwitch", + ), + ), ], ), migrations.AddField( - model_name='switch', - name='model', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.ModelSwitch'), + model_name="switch", + name="model", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="topologie.ModelSwitch", + ), ), ] diff --git a/topologie/migrations/0033_auto_20171231_1743.py b/topologie/migrations/0033_auto_20171231_1743.py index c79412d7..9e36c82e 100644 --- a/topologie/migrations/0033_auto_20171231_1743.py +++ b/topologie/migrations/0033_auto_20171231_1743.py @@ -7,33 +7,40 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0032_auto_20171026_0338'), - ] + dependencies = [("topologie", "0032_auto_20171026_0338")] operations = [ migrations.AlterModelOptions( - name='constructorswitch', - options={'permissions': (('view_constructorswitch', 'Peut voir un objet constructorswitch'),)}, + name="constructorswitch", + options={ + "permissions": ( + ("view_constructorswitch", "Peut voir un objet constructorswitch"), + ) + }, ), migrations.AlterModelOptions( - name='modelswitch', - options={'permissions': (('view_modelswitch', 'Peut voir un objet modelswitch'),)}, + name="modelswitch", + options={ + "permissions": (("view_modelswitch", "Peut voir un objet modelswitch"),) + }, ), migrations.AlterModelOptions( - name='port', - options={'permissions': (('view_port', 'Peut voir un objet port'),)}, + name="port", + options={"permissions": (("view_port", "Peut voir un objet port"),)}, ), migrations.AlterModelOptions( - name='room', - options={'ordering': ['name'], 'permissions': (('view_room', 'Peut voir un objet chambre'),)}, + name="room", + options={ + "ordering": ["name"], + "permissions": (("view_room", "Peut voir un objet chambre"),), + }, ), migrations.AlterModelOptions( - name='stack', - options={'permissions': (('view_stack', 'Peut voir un objet stack'),)}, + name="stack", + options={"permissions": (("view_stack", "Peut voir un objet stack"),)}, ), migrations.AlterModelOptions( - name='switch', - options={'permissions': (('view_switch', 'Peut voir un objet switch'),)}, + name="switch", + options={"permissions": (("view_switch", "Peut voir un objet switch"),)}, ), ] diff --git a/topologie/migrations/0034_borne.py b/topologie/migrations/0034_borne.py index 6d827f3a..3365b451 100644 --- a/topologie/migrations/0034_borne.py +++ b/topologie/migrations/0034_borne.py @@ -9,20 +9,33 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('machines', '0076_auto_20180130_1623'), - ('topologie', '0033_auto_20171231_1743'), + ("machines", "0076_auto_20180130_1623"), + ("topologie", "0033_auto_20171231_1743"), ] operations = [ migrations.CreateModel( - name='Borne', + name="Borne", fields=[ - ('interface_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='machines.Interface')), - ('location', models.CharField(help_text="Détails sur la localisation de l'AP", max_length=255)), + ( + "interface_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="machines.Interface", + ), + ), + ( + "location", + models.CharField( + help_text="Détails sur la localisation de l'AP", max_length=255 + ), + ), ], - options={ - 'permissions': (('view_borne', 'Peut voir une borne'),), - }, - bases=('machines.interface',), - ), + options={"permissions": (("view_borne", "Peut voir une borne"),)}, + bases=("machines.interface",), + ) ] diff --git a/topologie/migrations/0035_auto_20180324_0023.py b/topologie/migrations/0035_auto_20180324_0023.py index 7fa69d01..419aa82f 100644 --- a/topologie/migrations/0035_auto_20180324_0023.py +++ b/topologie/migrations/0035_auto_20180324_0023.py @@ -7,14 +7,17 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0034_borne'), - ] + dependencies = [("topologie", "0034_borne")] operations = [ migrations.AlterField( - model_name='borne', - name='location', - field=models.CharField(blank=True, help_text="Détails sur la localisation de l'AP", max_length=255, null=True), - ), + model_name="borne", + name="location", + field=models.CharField( + blank=True, + help_text="Détails sur la localisation de l'AP", + max_length=255, + null=True, + ), + ) ] diff --git a/topologie/migrations/0036_transferborne.py b/topologie/migrations/0036_transferborne.py index 2b59e3b9..b3603c36 100644 --- a/topologie/migrations/0036_transferborne.py +++ b/topologie/migrations/0036_transferborne.py @@ -7,26 +7,26 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0035_auto_20180324_0023'), - ] + dependencies = [("topologie", "0035_auto_20180324_0023")] def transfer_bornes(apps, schema_editor): db_alias = schema_editor.connection.alias machinetype = apps.get_model("machines", "MachineType") borne = apps.get_model("topologie", "Borne") - interface = apps.get_model("machines", "Interface") - bornes_list = machinetype.objects.using(db_alias).filter(type__icontains='borne') + interface = apps.get_model("machines", "Interface") + bornes_list = machinetype.objects.using(db_alias).filter( + ip_type__name__icontains="borne" + ) if bornes_list: - for inter in interface.objects.using(db_alias).filter(type=bornes_list.first()): + for inter in interface.objects.using(db_alias).filter( + machine_type=bornes_list.first() + ): borne_object = borne() borne_object.interface_ptr_id = inter.pk - borne_object.__dict__.update(inter.__dict__) + borne_object.__dict__.update(inter.__dict__) borne_object.save() def untransfer_bornes(apps, schema_editor): return - operations = [ - migrations.RunPython(transfer_bornes, untransfer_bornes), - ] + operations = [migrations.RunPython(transfer_bornes, untransfer_bornes)] diff --git a/topologie/migrations/0037_auto_20180325_0127.py b/topologie/migrations/0037_auto_20180325_0127.py index 30b34e58..4d9edd4c 100644 --- a/topologie/migrations/0037_auto_20180325_0127.py +++ b/topologie/migrations/0037_auto_20180325_0127.py @@ -9,25 +9,50 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('machines', '0076_auto_20180130_1623'), - ('topologie', '0036_transferborne'), + ("machines", "0076_auto_20180130_1623"), + ("topologie", "0036_transferborne"), ] operations = [ migrations.CreateModel( - name='NewSwitch', + name="NewSwitch", fields=[ - ('interface_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='machines.Interface')), - ('location', models.CharField(max_length=255)), - ('number', models.PositiveIntegerField()), - ('stack_member_id', models.PositiveIntegerField(blank=True, null=True)), - ('model', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.ModelSwitch')), - ('stack', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.Stack')), + ( + "interface_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="machines.Interface", + ), + ), + ("location", models.CharField(max_length=255)), + ("number", models.PositiveIntegerField()), + ("stack_member_id", models.PositiveIntegerField(blank=True, null=True)), + ( + "model", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="topologie.ModelSwitch", + ), + ), + ( + "stack", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="topologie.Stack", + ), + ), ], - bases=('machines.interface',), + bases=("machines.interface",), ), migrations.AlterUniqueTogether( - name='newswitch', - unique_together=set([('stack', 'stack_member_id')]), + name="newswitch", unique_together=set([("stack", "stack_member_id")]) ), ] diff --git a/topologie/migrations/0038_transfersw.py b/topologie/migrations/0038_transfersw.py index 6f79825f..0b1853cd 100644 --- a/topologie/migrations/0038_transfersw.py +++ b/topologie/migrations/0038_transfersw.py @@ -7,15 +7,13 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0037_auto_20180325_0127'), - ] + dependencies = [("topologie", "0037_auto_20180325_0127")] def transfer_sw(apps, schema_editor): db_alias = schema_editor.connection.alias newswitch = apps.get_model("topologie", "NewSwitch") switch = apps.get_model("topologie", "Switch") - interface = apps.get_model("machines", "Interface") + interface = apps.get_model("machines", "Interface") sw_list = switch.objects.using(db_alias).all() for sw in sw_list: new_sw = newswitch() @@ -26,12 +24,10 @@ class Migration(migrations.Migration): new_sw.stack_member_id = sw.stack_member_id new_sw.model = sw.model new_sw.interface_ptr_id = sw.switch_interface.pk - new_sw.__dict__.update(sw.switch_interface.__dict__) + new_sw.__dict__.update(sw.switch_interface.__dict__) new_sw.save() def untransfer_sw(apps, schema_editor): return - operations = [ - migrations.RunPython(transfer_sw, untransfer_sw), - ] + operations = [migrations.RunPython(transfer_sw, untransfer_sw)] diff --git a/topologie/migrations/0039_port_new_switch.py b/topologie/migrations/0039_port_new_switch.py index 33dda2b4..c3dc70dc 100644 --- a/topologie/migrations/0039_port_new_switch.py +++ b/topologie/migrations/0039_port_new_switch.py @@ -8,14 +8,18 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0038_transfersw'), - ] + dependencies = [("topologie", "0038_transfersw")] operations = [ migrations.AddField( - model_name='port', - name='new_switch', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='ports', to='topologie.NewSwitch'), - ), + model_name="port", + name="new_switch", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="ports", + to="topologie.NewSwitch", + ), + ) ] diff --git a/topologie/migrations/0040_transferports.py b/topologie/migrations/0040_transferports.py index 7f72edc2..8e572082 100644 --- a/topologie/migrations/0040_transferports.py +++ b/topologie/migrations/0040_transferports.py @@ -7,9 +7,7 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0039_port_new_switch'), - ] + dependencies = [("topologie", "0039_port_new_switch")] def transfer_port(apps, schema_editor): db_alias = schema_editor.connection.alias @@ -17,12 +15,12 @@ class Migration(migrations.Migration): switch = apps.get_model("topologie", "NewSwitch") port_list = port.objects.using(db_alias).all() for p in port_list: - p.new_switch = switch.objects.filter(interface_ptr=p.switch.switch_interface).first() + p.new_switch = switch.objects.filter( + interface_ptr=p.switch.switch_interface + ).first() p.save() def untransfer_port(apps, schema_editor): return - operations = [ - migrations.RunPython(transfer_port, untransfer_port), - ] + operations = [migrations.RunPython(transfer_port, untransfer_port)] diff --git a/topologie/migrations/0041_transferportsw.py b/topologie/migrations/0041_transferportsw.py index 9e1d979a..a0cb4b0a 100644 --- a/topologie/migrations/0041_transferportsw.py +++ b/topologie/migrations/0041_transferportsw.py @@ -7,18 +7,10 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0040_transferports'), - ] + dependencies = [("topologie", "0040_transferports")] operations = [ - migrations.AlterUniqueTogether( - name='port', - unique_together=set([]), - ), - migrations.RemoveField( - model_name='port', - name='switch', - ), - migrations.RenameField('Port', 'new_switch', 'switch') + migrations.AlterUniqueTogether(name="port", unique_together=set([])), + migrations.RemoveField(model_name="port", name="switch"), + migrations.RenameField("Port", "new_switch", "switch"), ] diff --git a/topologie/migrations/0042_transferswitch.py b/topologie/migrations/0042_transferswitch.py index 96ad49dc..098ec7d4 100644 --- a/topologie/migrations/0042_transferswitch.py +++ b/topologie/migrations/0042_transferswitch.py @@ -7,12 +7,6 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0041_transferportsw'), - ] + dependencies = [("topologie", "0041_transferportsw")] - operations = [ - migrations.DeleteModel( - name='Switch', - ), - ] + operations = [migrations.DeleteModel(name="Switch")] diff --git a/topologie/migrations/0043_renamenewswitch.py b/topologie/migrations/0043_renamenewswitch.py index 3deeced4..90a9d776 100644 --- a/topologie/migrations/0043_renamenewswitch.py +++ b/topologie/migrations/0043_renamenewswitch.py @@ -7,10 +7,6 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0042_transferswitch'), - ] + dependencies = [("topologie", "0042_transferswitch")] - operations = [ - migrations.RenameModel(old_name='NewSwitch', new_name='Switch'), - ] + operations = [migrations.RenameModel(old_name="NewSwitch", new_name="Switch")] diff --git a/topologie/migrations/0044_auto_20180326_0002.py b/topologie/migrations/0044_auto_20180326_0002.py index f18b4ed9..b1af46d7 100644 --- a/topologie/migrations/0044_auto_20180326_0002.py +++ b/topologie/migrations/0044_auto_20180326_0002.py @@ -8,21 +8,16 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0043_renamenewswitch'), - ] + dependencies = [("topologie", "0043_renamenewswitch")] operations = [ - migrations.RenameModel( - old_name='Borne', - new_name='AccessPoint', + migrations.RenameModel(old_name="Borne", new_name="AccessPoint"), + migrations.AlterModelOptions( + name="accesspoint", + options={"permissions": (("view_ap", "Peut voir une borne"),)}, ), migrations.AlterModelOptions( - name='accesspoint', - options={'permissions': (('view_ap', 'Peut voir une borne'),)}, - ), - migrations.AlterModelOptions( - name='switch', - options={'permissions': (('view_switch', 'Peut voir un objet switch'),)}, + name="switch", + options={"permissions": (("view_switch", "Peut voir un objet switch"),)}, ), ] diff --git a/topologie/migrations/0045_auto_20180326_0123.py b/topologie/migrations/0045_auto_20180326_0123.py index cf621967..f2dd68d0 100644 --- a/topologie/migrations/0045_auto_20180326_0123.py +++ b/topologie/migrations/0045_auto_20180326_0123.py @@ -8,14 +8,16 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0044_auto_20180326_0002'), - ] + dependencies = [("topologie", "0044_auto_20180326_0002")] operations = [ migrations.AlterField( - model_name='port', - name='switch', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ports', to='topologie.Switch'), - ), + model_name="port", + name="switch", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="ports", + to="topologie.Switch", + ), + ) ] diff --git a/topologie/migrations/0046_auto_20180326_0129.py b/topologie/migrations/0046_auto_20180326_0129.py index dc5e849e..7715ad2f 100644 --- a/topologie/migrations/0046_auto_20180326_0129.py +++ b/topologie/migrations/0046_auto_20180326_0129.py @@ -7,13 +7,10 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0045_auto_20180326_0123'), - ] + dependencies = [("topologie", "0045_auto_20180326_0123")] operations = [ migrations.AlterUniqueTogether( - name='port', - unique_together=set([('switch', 'port')]), - ), + name="port", unique_together=set([("switch", "port")]) + ) ] diff --git a/topologie/migrations/0047_ap_machine.py b/topologie/migrations/0047_ap_machine.py index f09ca318..ad8774ed 100644 --- a/topologie/migrations/0047_ap_machine.py +++ b/topologie/migrations/0047_ap_machine.py @@ -8,19 +8,33 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0046_auto_20180326_0129'), - ] - - + dependencies = [("topologie", "0046_auto_20180326_0129")] operations = [ migrations.CreateModel( - name='NewAccessPoint', + name="NewAccessPoint", fields=[ - ('machine_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='machines.Machine')), - ('location', models.CharField(help_text="Détails sur la localisation de l'AP", max_length=255, null=True, blank=True)), + ( + "machine_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="machines.Machine", + ), + ), + ( + "location", + models.CharField( + help_text="Détails sur la localisation de l'AP", + max_length=255, + null=True, + blank=True, + ), + ), ], - bases=('machines.machine',), - ), + bases=("machines.machine",), + ) ] diff --git a/topologie/migrations/0048_ap_machine.py b/topologie/migrations/0048_ap_machine.py index ed1aa2fd..003f7d1e 100644 --- a/topologie/migrations/0048_ap_machine.py +++ b/topologie/migrations/0048_ap_machine.py @@ -8,9 +8,7 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0047_ap_machine'), - ] + dependencies = [("topologie", "0047_ap_machine")] def transfer_ap(apps, schema_editor): db_alias = schema_editor.connection.alias @@ -29,11 +27,6 @@ class Migration(migrations.Migration): operations = [ migrations.RunPython(transfer_ap, untransfer_ap), - migrations.DeleteModel( - name='AccessPoint', - ), - migrations.RenameModel( - old_name='NewAccessPoint', - new_name='AccessPoint', - ), + migrations.DeleteModel(name="AccessPoint"), + migrations.RenameModel(old_name="NewAccessPoint", new_name="AccessPoint"), ] diff --git a/topologie/migrations/0049_switchs_machine.py b/topologie/migrations/0049_switchs_machine.py index 040ab599..f28a2d8e 100644 --- a/topologie/migrations/0049_switchs_machine.py +++ b/topologie/migrations/0049_switchs_machine.py @@ -8,23 +8,45 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0048_ap_machine'), - ] - - + dependencies = [("topologie", "0048_ap_machine")] operations = [ migrations.CreateModel( - name='NewSw', + name="NewSw", fields=[ - ('machine_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='machines.Machine')), - ('location', models.CharField(max_length=255)), - ('number', models.PositiveIntegerField()), - ('stack_member_id', models.PositiveIntegerField(blank=True, null=True)), - ('model', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.ModelSwitch')), - ('stack', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.Stack')), + ( + "machine_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="machines.Machine", + ), + ), + ("location", models.CharField(max_length=255)), + ("number", models.PositiveIntegerField()), + ("stack_member_id", models.PositiveIntegerField(blank=True, null=True)), + ( + "model", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="topologie.ModelSwitch", + ), + ), + ( + "stack", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="topologie.Stack", + ), + ), ], - bases=('machines.machine',), - ), + bases=("machines.machine",), + ) ] diff --git a/topologie/migrations/0050_port_new_switch.py b/topologie/migrations/0050_port_new_switch.py index b03a8ee5..1efab4dd 100644 --- a/topologie/migrations/0050_port_new_switch.py +++ b/topologie/migrations/0050_port_new_switch.py @@ -8,14 +8,18 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0049_switchs_machine'), - ] + dependencies = [("topologie", "0049_switchs_machine")] operations = [ migrations.AddField( - model_name='port', - name='new_sw', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='ports', to='topologie.NewSw'), - ), + model_name="port", + name="new_sw", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="ports", + to="topologie.NewSw", + ), + ) ] diff --git a/topologie/migrations/0051_switchs_machine.py b/topologie/migrations/0051_switchs_machine.py index d7f1da86..3a5744e0 100644 --- a/topologie/migrations/0051_switchs_machine.py +++ b/topologie/migrations/0051_switchs_machine.py @@ -8,15 +8,13 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0050_port_new_switch'), - ] + dependencies = [("topologie", "0050_port_new_switch")] def transfer_sw(apps, schema_editor): db_alias = schema_editor.connection.alias newswitch = apps.get_model("topologie", "NewSw") switch = apps.get_model("topologie", "Switch") - machine = apps.get_model("machines", "Machine") + machine = apps.get_model("machines", "Machine") sw_list = switch.objects.using(db_alias).all() for sw in sw_list: new_sw = newswitch() @@ -27,13 +25,10 @@ class Migration(migrations.Migration): new_sw.stack_member_id = sw.stack_member_id new_sw.model = sw.model new_sw.machine_ptr_id = sw.interface_ptr.machine.pk - new_sw.__dict__.update(sw.interface_ptr.machine.__dict__) + new_sw.__dict__.update(sw.interface_ptr.machine.__dict__) new_sw.save() def untransfer_sw(apps, schema_editor): return - - operations = [ - migrations.RunPython(transfer_sw, untransfer_sw), - ] + operations = [migrations.RunPython(transfer_sw, untransfer_sw)] diff --git a/topologie/migrations/0052_transferports.py b/topologie/migrations/0052_transferports.py index 88badbe6..3f6c7776 100644 --- a/topologie/migrations/0052_transferports.py +++ b/topologie/migrations/0052_transferports.py @@ -7,9 +7,7 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0051_switchs_machine'), - ] + dependencies = [("topologie", "0051_switchs_machine")] def transfer_port(apps, schema_editor): db_alias = schema_editor.connection.alias @@ -24,14 +22,8 @@ class Migration(migrations.Migration): return operations = [ - migrations.AlterUniqueTogether( - name='port', - unique_together=set([]), - ), - migrations.RunPython(transfer_port, untransfer_port), - migrations.RemoveField( - model_name='port', - name='switch', - ), - migrations.RenameField('Port', 'new_sw', 'switch') + migrations.AlterUniqueTogether(name="port", unique_together=set([])), + migrations.RunPython(transfer_port, untransfer_port), + migrations.RemoveField(model_name="port", name="switch"), + migrations.RenameField("Port", "new_sw", "switch"), ] diff --git a/topologie/migrations/0053_finalsw.py b/topologie/migrations/0053_finalsw.py index 2ed7e2ac..be77f071 100644 --- a/topologie/migrations/0053_finalsw.py +++ b/topologie/migrations/0053_finalsw.py @@ -8,18 +8,9 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0052_transferports'), - ] - - + dependencies = [("topologie", "0052_transferports")] operations = [ - migrations.DeleteModel( - name='Switch', - ), - migrations.RenameModel( - old_name='NewSw', - new_name='Switch', - ), + migrations.DeleteModel(name="Switch"), + migrations.RenameModel(old_name="NewSw", new_name="Switch"), ] diff --git a/topologie/migrations/0054_auto_20180326_1742.py b/topologie/migrations/0054_auto_20180326_1742.py index cfaff01b..98567126 100644 --- a/topologie/migrations/0054_auto_20180326_1742.py +++ b/topologie/migrations/0054_auto_20180326_1742.py @@ -8,31 +8,31 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0053_finalsw'), - ] + dependencies = [("topologie", "0053_finalsw")] operations = [ migrations.AlterModelOptions( - name='accesspoint', - options={'permissions': (('view_ap', 'Peut voir une borne'),)}, + name="accesspoint", + options={"permissions": (("view_ap", "Peut voir une borne"),)}, ), migrations.AlterModelOptions( - name='switch', - options={'permissions': (('view_switch', 'Peut voir un objet switch'),)}, + name="switch", + options={"permissions": (("view_switch", "Peut voir un objet switch"),)}, ), migrations.AlterField( - model_name='port', - name='switch', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ports', to='topologie.Switch'), + model_name="port", + name="switch", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="ports", + to="topologie.Switch", + ), preserve_default=False, ), migrations.AlterUniqueTogether( - name='port', - unique_together=set([('switch', 'port')]), + name="port", unique_together=set([("switch", "port")]) ), migrations.AlterUniqueTogether( - name='switch', - unique_together=set([('stack', 'stack_member_id')]), + name="switch", unique_together=set([("stack", "stack_member_id")]) ), ] diff --git a/topologie/migrations/0055_auto_20180329_0431.py b/topologie/migrations/0055_auto_20180329_0431.py index f8633d84..a7b232bf 100644 --- a/topologie/migrations/0055_auto_20180329_0431.py +++ b/topologie/migrations/0055_auto_20180329_0431.py @@ -7,13 +7,11 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0054_auto_20180326_1742'), - ] + dependencies = [("topologie", "0054_auto_20180326_1742")] operations = [ migrations.AlterModelOptions( - name='accesspoint', - options={'permissions': (('view_accesspoint', 'Peut voir une borne'),)}, - ), + name="accesspoint", + options={"permissions": (("view_accesspoint", "Peut voir une borne"),)}, + ) ] diff --git a/topologie/migrations/0056_building_switchbay.py b/topologie/migrations/0056_building_switchbay.py index 3f705263..c166adc7 100644 --- a/topologie/migrations/0056_building_switchbay.py +++ b/topologie/migrations/0056_building_switchbay.py @@ -9,33 +9,68 @@ import re2o.mixins class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0055_auto_20180329_0431'), - ] + dependencies = [("topologie", "0055_auto_20180329_0431")] operations = [ migrations.CreateModel( - name='Building', + name="Building", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)), ], options={ - 'permissions': (('view_building', 'Peut voir un objet batiment'),), + "permissions": (("view_building", "Peut voir un objet batiment"),) }, bases=(re2o.mixins.AclMixin, re2o.mixins.RevMixin, models.Model), ), migrations.CreateModel( - name='SwitchBay', + name="SwitchBay", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('info', models.CharField(blank=True, help_text='Informations particulières', max_length=255, null=True)), - ('building', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='topologie.Building')), - ('members', models.ManyToManyField(blank=True, related_name='bay_switches', to='topologie.Switch')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)), + ( + "info", + models.CharField( + blank=True, + help_text="Informations particulières", + max_length=255, + null=True, + ), + ), + ( + "building", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to="topologie.Building", + ), + ), + ( + "members", + models.ManyToManyField( + blank=True, related_name="bay_switches", to="topologie.Switch" + ), + ), ], options={ - 'permissions': (('view_switchbay', 'Peut voir un objet baie de brassage'),), + "permissions": ( + ("view_switchbay", "Peut voir un objet baie de brassage"), + ) }, bases=(re2o.mixins.AclMixin, re2o.mixins.RevMixin, models.Model), ), diff --git a/topologie/migrations/0057_auto_20180408_0316.py b/topologie/migrations/0057_auto_20180408_0316.py index e4a0d09b..3f512be9 100644 --- a/topologie/migrations/0057_auto_20180408_0316.py +++ b/topologie/migrations/0057_auto_20180408_0316.py @@ -8,18 +8,18 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0056_building_switchbay'), - ] + dependencies = [("topologie", "0056_building_switchbay")] operations = [ - migrations.RemoveField( - model_name='switchbay', - name='members', - ), + migrations.RemoveField(model_name="switchbay", name="members"), migrations.AddField( - model_name='switch', - name='switchbay', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.SwitchBay'), + model_name="switch", + name="switchbay", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="topologie.SwitchBay", + ), ), ] diff --git a/topologie/migrations/0058_remove_switch_location.py b/topologie/migrations/0058_remove_switch_location.py index 0d75fc67..7308e993 100644 --- a/topologie/migrations/0058_remove_switch_location.py +++ b/topologie/migrations/0058_remove_switch_location.py @@ -7,13 +7,6 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0057_auto_20180408_0316'), - ] + dependencies = [("topologie", "0057_auto_20180408_0316")] - operations = [ - migrations.RemoveField( - model_name='switch', - name='location', - ), - ] + operations = [migrations.RemoveField(model_name="switch", name="location")] diff --git a/topologie/migrations/0059_auto_20180415_2249.py b/topologie/migrations/0059_auto_20180415_2249.py index a58b710d..211b5799 100644 --- a/topologie/migrations/0059_auto_20180415_2249.py +++ b/topologie/migrations/0059_auto_20180415_2249.py @@ -8,29 +8,41 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0058_remove_switch_location'), - ] + dependencies = [("topologie", "0058_remove_switch_location")] operations = [ migrations.AlterField( - model_name='switch', - name='model', - field=models.ForeignKey(blank=True, help_text='Modèle du switch', null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.ModelSwitch'), + model_name="switch", + name="model", + field=models.ForeignKey( + blank=True, + help_text="Modèle du switch", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="topologie.ModelSwitch", + ), ), migrations.AlterField( - model_name='switch', - name='number', - field=models.PositiveIntegerField(help_text='Nombre de ports'), + model_name="switch", + name="number", + field=models.PositiveIntegerField(help_text="Nombre de ports"), ), migrations.AlterField( - model_name='switch', - name='stack_member_id', - field=models.PositiveIntegerField(blank=True, help_text='Baie de brassage du switch', null=True), + model_name="switch", + name="stack_member_id", + field=models.PositiveIntegerField( + blank=True, help_text="Baie de brassage du switch", null=True + ), ), migrations.AlterField( - model_name='switch', - name='switchbay', - field=models.ForeignKey(blank=True, help_text='Baie de brassage du switch', null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.SwitchBay'), + model_name="switch", + name="switchbay", + field=models.ForeignKey( + blank=True, + help_text="Baie de brassage du switch", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="topologie.SwitchBay", + ), ), ] diff --git a/topologie/migrations/0060_server.py b/topologie/migrations/0060_server.py index af067e7b..7d757429 100644 --- a/topologie/migrations/0060_server.py +++ b/topologie/migrations/0060_server.py @@ -8,18 +8,15 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('machines', '0081_auto_20180521_1413'), - ('topologie', '0059_auto_20180415_2249'), + ("machines", "0081_auto_20180521_1413"), + ("topologie", "0059_auto_20180415_2249"), ] operations = [ migrations.CreateModel( - name='Server', - fields=[ - ], - options={ - 'proxy': True, - }, - bases=('machines.machine',), - ), + name="Server", + fields=[], + options={"proxy": True}, + bases=("machines.machine",), + ) ] diff --git a/topologie/migrations/0061_portprofile.py b/topologie/migrations/0061_portprofile.py index 88e2d9ab..dd26f988 100644 --- a/topologie/migrations/0061_portprofile.py +++ b/topologie/migrations/0061_portprofile.py @@ -13,88 +13,298 @@ def transfer_profil(apps, schema_editor): profil = apps.get_model("topologie", "PortProfile") vlan = apps.get_model("machines", "Vlan") port_list = port.objects.using(db_alias).all() - profil_nothing = profil.objects.using(db_alias).create(name='nothing', profil_default='nothing', radius_type='NO') - profil_uplink = profil.objects.using(db_alias).create(name='uplink', profil_default='uplink', radius_type='NO') - profil_machine = profil.objects.using(db_alias).create(name='asso_machine', profil_default='asso_machine', radius_type='NO') - profil_room = profil.objects.using(db_alias).create(name='room', profil_default='room', radius_type='NO') - profil_borne = profil.objects.using(db_alias).create(name='accesspoint', profil_default='accesspoint', radius_type='NO') + profil_nothing = profil.objects.using(db_alias).create( + name="nothing", profil_default="nothing", radius_type="NO" + ) + profil_uplink = profil.objects.using(db_alias).create( + name="uplink", profil_default="uplink", radius_type="NO" + ) + profil_machine = profil.objects.using(db_alias).create( + name="asso_machine", profil_default="asso_machine", radius_type="NO" + ) + profil_room = profil.objects.using(db_alias).create( + name="room", profil_default="room", radius_type="NO" + ) + profil_borne = profil.objects.using(db_alias).create( + name="accesspoint", profil_default="accesspoint", radius_type="NO" + ) for vlan_instance in vlan.objects.using(db_alias).all(): if port.objects.using(db_alias).filter(vlan_force=vlan_instance): - custom_profile = profil.objects.using(db_alias).create(name='vlan-force-' + str(vlan_instance.vlan_id), radius_type='NO', vlan_untagged=vlan_instance) - port.objects.using(db_alias).filter(vlan_force=vlan_instance).update(custom_profile=custom_profile) - if port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').count() and port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').count(): - profil_room.radius_type = 'MAC-radius' - profil_room.radius_mode = 'STRICT' - common_profil = profil.objects.using(db_alias).create(name='mac-radius-common', radius_type='MAC-radius', radius_mode='COMMON') - no_rad_profil = profil.objects.using(db_alias).create(name='no-radius', radius_type='NO') - port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').update(custom_profile=common_profil) - port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profile=no_rad_profil) - elif port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').count() and port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').count(): - profil_room.radius_type = 'MAC-radius' - profil_room.radius_mode = 'COMMON' - strict_profil = profil.objects.using(db_alias).create(name='mac-radius-strict', radius_type='MAC-radius', radius_mode='STRICT') - no_rad_profil = profil.objects.using(db_alias).create(name='no-radius', radius_type='NO') - port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').update(custom_profile=strict_profil) - port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profile=no_rad_profil) + custom_profile = profil.objects.using(db_alias).create( + name="vlan-force-" + str(vlan_instance.vlan_id), + radius_type="NO", + vlan_untagged=vlan_instance, + ) + port.objects.using(db_alias).filter(vlan_force=vlan_instance).update( + custom_profile=custom_profile + ) + if ( + port.objects.using(db_alias) + .filter(room__isnull=False) + .filter(radius="STRICT") + .count() + > port.objects.using(db_alias) + .filter(room__isnull=False) + .filter(radius="NO") + .count() + and port.objects.using(db_alias) + .filter(room__isnull=False) + .filter(radius="STRICT") + .count() + > port.objects.using(db_alias) + .filter(room__isnull=False) + .filter(radius="COMMON") + .count() + ): + profil_room.radius_type = "MAC-radius" + profil_room.radius_mode = "STRICT" + common_profil = profil.objects.using(db_alias).create( + name="mac-radius-common", radius_type="MAC-radius", radius_mode="COMMON" + ) + no_rad_profil = profil.objects.using(db_alias).create( + name="no-radius", radius_type="NO" + ) + port.objects.using(db_alias).filter(room__isnull=False).filter( + radius="COMMON" + ).update(custom_profile=common_profil) + port.objects.using(db_alias).filter(room__isnull=False).filter( + radius="NO" + ).update(custom_profile=no_rad_profil) + elif ( + port.objects.using(db_alias) + .filter(room__isnull=False) + .filter(radius="COMMON") + .count() + > port.objects.using(db_alias) + .filter(room__isnull=False) + .filter(radius="NO") + .count() + and port.objects.using(db_alias) + .filter(room__isnull=False) + .filter(radius="COMMON") + .count() + > port.objects.using(db_alias) + .filter(room__isnull=False) + .filter(radius="STRICT") + .count() + ): + profil_room.radius_type = "MAC-radius" + profil_room.radius_mode = "COMMON" + strict_profil = profil.objects.using(db_alias).create( + name="mac-radius-strict", radius_type="MAC-radius", radius_mode="STRICT" + ) + no_rad_profil = profil.objects.using(db_alias).create( + name="no-radius", radius_type="NO" + ) + port.objects.using(db_alias).filter(room__isnull=False).filter( + radius="STRICT" + ).update(custom_profile=strict_profil) + port.objects.using(db_alias).filter(room__isnull=False).filter( + radius="NO" + ).update(custom_profile=no_rad_profil) else: - strict_profil = profil.objects.using(db_alias).create(name='mac-radius-strict', radius_type='MAC-radius', radius_mode='STRICT') - common_profil = profil.objects.using(db_alias).create(name='mac-radius-common', radius_type='MAC-radius', radius_mode='COMMON') - port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').update(custom_profile=strict_profil) - port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profile=common_profil) + strict_profil = profil.objects.using(db_alias).create( + name="mac-radius-strict", radius_type="MAC-radius", radius_mode="STRICT" + ) + common_profil = profil.objects.using(db_alias).create( + name="mac-radius-common", radius_type="MAC-radius", radius_mode="COMMON" + ) + port.objects.using(db_alias).filter(room__isnull=False).filter( + radius="STRICT" + ).update(custom_profile=strict_profil) + port.objects.using(db_alias).filter(room__isnull=False).filter( + radius="NO" + ).update(custom_profile=common_profil) profil_room.save() class Migration(migrations.Migration): dependencies = [ - ('machines', '0082_auto_20180525_2209'), - ('topologie', '0060_server'), + ("machines", "0082_auto_20180525_2209"), + ("topologie", "0060_server"), ] operations = [ migrations.CreateModel( - name='PortProfile', + name="PortProfile", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='Name')), - ('profil_default', models.CharField(blank=True, choices=[('room', 'room'), ('accespoint', 'accesspoint'), ('uplink', 'uplink'), ('asso_machine', 'asso_machine'), ('nothing', 'nothing')], max_length=32, null=True, unique=True, verbose_name='profil default')), - ('radius_type', models.CharField(choices=[('NO', 'NO'), ('802.1X', '802.1X'), ('MAC-radius', 'MAC-radius')], help_text='Type of radius auth : inactive, mac-address or 802.1X', max_length=32, verbose_name='RADIUS type')), - ('radius_mode', models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], default='COMMON', help_text='In case of mac-auth : mode common or strict on this port', max_length=32, verbose_name='RADIUS mode')), - ('speed', models.CharField(choices=[('10-half', '10-half'), ('100-half', '100-half'), ('10-full', '10-full'), ('100-full', '100-full'), ('1000-full', '1000-full'), ('auto', 'auto'), ('auto-10', 'auto-10'), ('auto-100', 'auto-100')], default='auto', help_text='Port speed limit', max_length=32, verbose_name='Speed')), - ('mac_limit', models.IntegerField(blank=True, help_text='Limit of mac-address on this port', null=True, verbose_name='Mac limit')), - ('flow_control', models.BooleanField(default=False, help_text='Flow control', verbose_name='Flow control')), - ('dhcp_snooping', models.BooleanField(default=False, help_text='Protect against rogue dhcp', verbose_name='Dhcp snooping')), - ('dhcpv6_snooping', models.BooleanField(default=False, help_text='Protect against rogue dhcpv6', verbose_name='Dhcpv6 snooping')), - ('arp_protect', models.BooleanField(default=False, help_text='Check if ip is dhcp assigned', verbose_name='Arp protect')), - ('ra_guard', models.BooleanField(default=False, help_text='Protect against rogue ra', verbose_name='Ra guard')), - ('loop_protect', models.BooleanField(default=False, help_text='Protect again loop', verbose_name='Loop Protect')), - ('vlan_tagged', models.ManyToManyField(blank=True, related_name='vlan_tagged', to='machines.Vlan', verbose_name='VLAN(s) tagged')), - ('vlan_untagged', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vlan_untagged', to='machines.Vlan', verbose_name='VLAN untagged')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255, verbose_name="Name")), + ( + "profil_default", + models.CharField( + blank=True, + choices=[ + ("room", "room"), + ("accespoint", "accesspoint"), + ("uplink", "uplink"), + ("asso_machine", "asso_machine"), + ("nothing", "nothing"), + ], + max_length=32, + null=True, + unique=True, + verbose_name="profil default", + ), + ), + ( + "radius_type", + models.CharField( + choices=[ + ("NO", "NO"), + ("802.1X", "802.1X"), + ("MAC-radius", "MAC-radius"), + ], + help_text="Type of radius auth : inactive, mac-address or 802.1X", + max_length=32, + verbose_name="RADIUS type", + ), + ), + ( + "radius_mode", + models.CharField( + choices=[("STRICT", "STRICT"), ("COMMON", "COMMON")], + default="COMMON", + help_text="In case of mac-auth : mode common or strict on this port", + max_length=32, + verbose_name="RADIUS mode", + ), + ), + ( + "speed", + models.CharField( + choices=[ + ("10-half", "10-half"), + ("100-half", "100-half"), + ("10-full", "10-full"), + ("100-full", "100-full"), + ("1000-full", "1000-full"), + ("auto", "auto"), + ("auto-10", "auto-10"), + ("auto-100", "auto-100"), + ], + default="auto", + help_text="Port speed limit", + max_length=32, + verbose_name="Speed", + ), + ), + ( + "mac_limit", + models.IntegerField( + blank=True, + help_text="Limit of mac-address on this port", + null=True, + verbose_name="Mac limit", + ), + ), + ( + "flow_control", + models.BooleanField( + default=False, + help_text="Flow control", + verbose_name="Flow control", + ), + ), + ( + "dhcp_snooping", + models.BooleanField( + default=False, + help_text="Protect against rogue dhcp", + verbose_name="Dhcp snooping", + ), + ), + ( + "dhcpv6_snooping", + models.BooleanField( + default=False, + help_text="Protect against rogue dhcpv6", + verbose_name="Dhcpv6 snooping", + ), + ), + ( + "arp_protect", + models.BooleanField( + default=False, + help_text="Check if ip is dhcp assigned", + verbose_name="Arp protect", + ), + ), + ( + "ra_guard", + models.BooleanField( + default=False, + help_text="Protect against rogue ra", + verbose_name="Ra guard", + ), + ), + ( + "loop_protect", + models.BooleanField( + default=False, + help_text="Protect again loop", + verbose_name="Loop Protect", + ), + ), + ( + "vlan_tagged", + models.ManyToManyField( + blank=True, + related_name="vlan_tagged", + to="machines.Vlan", + verbose_name="VLAN(s) tagged", + ), + ), + ( + "vlan_untagged", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="vlan_untagged", + to="machines.Vlan", + verbose_name="VLAN untagged", + ), + ), ], options={ - 'verbose_name': 'Port profile', - 'permissions': (('view_port_profile', 'Can view a port profile object'),), - 'verbose_name_plural': 'Port profiles', + "verbose_name": "Port profile", + "permissions": ( + ("view_port_profile", "Can view a port profile object"), + ), + "verbose_name_plural": "Port profiles", }, bases=(re2o.mixins.AclMixin, re2o.mixins.RevMixin, models.Model), ), migrations.AddField( - model_name='port', - name='custom_profile', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='topologie.PortProfile'), + model_name="port", + name="custom_profile", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="topologie.PortProfile", + ), ), migrations.RunPython(transfer_profil), - migrations.RemoveField( - model_name='port', - name='radius', - ), - migrations.RemoveField( - model_name='port', - name='vlan_force', - ), + migrations.RemoveField(model_name="port", name="radius"), + migrations.RemoveField(model_name="port", name="vlan_force"), migrations.AddField( - model_name='port', - name='state', - field=models.BooleanField(default=True, help_text='Port state Active', verbose_name='Port State Active'), + model_name="port", + name="state", + field=models.BooleanField( + default=True, + help_text="Port state Active", + verbose_name="Port State Active", + ), ), ] diff --git a/topologie/migrations/0062_auto_20180815_1918.py b/topologie/migrations/0062_auto_20180815_1918.py index fc100258..a36f0ef0 100644 --- a/topologie/migrations/0062_auto_20180815_1918.py +++ b/topologie/migrations/0062_auto_20180815_1918.py @@ -8,139 +8,273 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0061_portprofile'), - ] + dependencies = [("topologie", "0061_portprofile")] operations = [ migrations.AlterModelOptions( - name='accesspoint', - options={'permissions': (('view_accesspoint', 'Can view an access point object'),), 'verbose_name': 'access point', 'verbose_name_plural': 'access points'}, + name="accesspoint", + options={ + "permissions": ( + ("view_accesspoint", "Can view an access point object"), + ), + "verbose_name": "access point", + "verbose_name_plural": "access points", + }, ), migrations.AlterModelOptions( - name='building', - options={'permissions': (('view_building', 'Can view a building object'),), 'verbose_name': 'building', 'verbose_name_plural': 'buildings'}, + name="building", + options={ + "permissions": (("view_building", "Can view a building object"),), + "verbose_name": "building", + "verbose_name_plural": "buildings", + }, ), migrations.AlterModelOptions( - name='constructorswitch', - options={'permissions': (('view_constructorswitch', 'Can view a switch constructor object'),), 'verbose_name': 'switch constructor', 'verbose_name_plural': 'switch constructors'}, + name="constructorswitch", + options={ + "permissions": ( + ("view_constructorswitch", "Can view a switch constructor object"), + ), + "verbose_name": "switch constructor", + "verbose_name_plural": "switch constructors", + }, ), migrations.AlterModelOptions( - name='modelswitch', - options={'permissions': (('view_modelswitch', 'Can view a switch model object'),), 'verbose_name': 'switch model', 'verbose_name_plural': 'switch models'}, + name="modelswitch", + options={ + "permissions": ( + ("view_modelswitch", "Can view a switch model object"), + ), + "verbose_name": "switch model", + "verbose_name_plural": "switch models", + }, ), migrations.AlterModelOptions( - name='port', - options={'permissions': (('view_port', 'Can view a port object'),), 'verbose_name': 'port', 'verbose_name_plural': 'ports'}, + name="port", + options={ + "permissions": (("view_port", "Can view a port object"),), + "verbose_name": "port", + "verbose_name_plural": "ports", + }, ), migrations.AlterModelOptions( - name='portprofile', - options={'permissions': (('view_port_profile', 'Can view a port profile object'),), 'verbose_name': 'port profile', 'verbose_name_plural': 'port profiles'}, + name="portprofile", + options={ + "permissions": ( + ("view_port_profile", "Can view a port profile object"), + ), + "verbose_name": "port profile", + "verbose_name_plural": "port profiles", + }, ), migrations.AlterModelOptions( - name='room', - options={'ordering': ['name'], 'permissions': (('view_room', 'Can view a room object'),), 'verbose_name': 'room', 'verbose_name_plural': 'rooms'}, + name="room", + options={ + "ordering": ["name"], + "permissions": (("view_room", "Can view a room object"),), + "verbose_name": "room", + "verbose_name_plural": "rooms", + }, ), migrations.AlterModelOptions( - name='stack', - options={'permissions': (('view_stack', 'Can view a stack object'),), 'verbose_name': 'switches stack', 'verbose_name_plural': 'switches stacks'}, + name="stack", + options={ + "permissions": (("view_stack", "Can view a stack object"),), + "verbose_name": "switches stack", + "verbose_name_plural": "switches stacks", + }, ), migrations.AlterModelOptions( - name='switch', - options={'permissions': (('view_switch', 'Can view a switch object'),), 'verbose_name': 'switch', 'verbose_name_plural': 'switches'}, + name="switch", + options={ + "permissions": (("view_switch", "Can view a switch object"),), + "verbose_name": "switch", + "verbose_name_plural": "switches", + }, ), migrations.AlterModelOptions( - name='switchbay', - options={'permissions': (('view_switchbay', 'Can view a switch bay object'),), 'verbose_name': 'switch bay', 'verbose_name_plural': 'switch bays'}, + name="switchbay", + options={ + "permissions": (("view_switchbay", "Can view a switch bay object"),), + "verbose_name": "switch bay", + "verbose_name_plural": "switch bays", + }, ), migrations.AlterField( - model_name='accesspoint', - name='location', - field=models.CharField(blank=True, help_text="Details about the AP's location", max_length=255, null=True), + model_name="accesspoint", + name="location", + field=models.CharField( + blank=True, + help_text="Details about the AP's location", + max_length=255, + null=True, + ), ), migrations.AlterField( - model_name='port', - name='state', - field=models.BooleanField(default=True, help_text='Port state Active', verbose_name='Port state Active'), + model_name="port", + name="state", + field=models.BooleanField( + default=True, + help_text="Port state Active", + verbose_name="Port state Active", + ), ), migrations.AlterField( - model_name='portprofile', - name='arp_protect', - field=models.BooleanField(default=False, help_text='Check if IP adress is DHCP assigned', verbose_name='ARP protection'), + model_name="portprofile", + name="arp_protect", + field=models.BooleanField( + default=False, + help_text="Check if IP adress is DHCP assigned", + verbose_name="ARP protection", + ), ), migrations.AlterField( - model_name='portprofile', - name='dhcp_snooping', - field=models.BooleanField(default=False, help_text='Protect against rogue DHCP', verbose_name='DHCP snooping'), + model_name="portprofile", + name="dhcp_snooping", + field=models.BooleanField( + default=False, + help_text="Protect against rogue DHCP", + verbose_name="DHCP snooping", + ), ), migrations.AlterField( - model_name='portprofile', - name='dhcpv6_snooping', - field=models.BooleanField(default=False, help_text='Protect against rogue DHCPv6', verbose_name='DHCPv6 snooping'), + model_name="portprofile", + name="dhcpv6_snooping", + field=models.BooleanField( + default=False, + help_text="Protect against rogue DHCPv6", + verbose_name="DHCPv6 snooping", + ), ), migrations.AlterField( - model_name='portprofile', - name='flow_control', - field=models.BooleanField(default=False, help_text='Flow control'), + model_name="portprofile", + name="flow_control", + field=models.BooleanField(default=False, help_text="Flow control"), ), migrations.AlterField( - model_name='portprofile', - name='loop_protect', - field=models.BooleanField(default=False, help_text='Protect against loop', verbose_name='Loop protection'), + model_name="portprofile", + name="loop_protect", + field=models.BooleanField( + default=False, + help_text="Protect against loop", + verbose_name="Loop protection", + ), ), migrations.AlterField( - model_name='portprofile', - name='mac_limit', - field=models.IntegerField(blank=True, help_text='Limit of MAC-address on this port', null=True, verbose_name='MAC limit'), + model_name="portprofile", + name="mac_limit", + field=models.IntegerField( + blank=True, + help_text="Limit of MAC-address on this port", + null=True, + verbose_name="MAC limit", + ), ), migrations.AlterField( - model_name='portprofile', - name='profil_default', - field=models.CharField(blank=True, choices=[('room', 'room'), ('accespoint', 'accesspoint'), ('uplink', 'uplink'), ('asso_machine', 'asso_machine'), ('nothing', 'nothing')], max_length=32, null=True, unique=True, verbose_name='Default profile'), + model_name="portprofile", + name="profil_default", + field=models.CharField( + blank=True, + choices=[ + ("room", "room"), + ("accespoint", "accesspoint"), + ("uplink", "uplink"), + ("asso_machine", "asso_machine"), + ("nothing", "nothing"), + ], + max_length=32, + null=True, + unique=True, + verbose_name="Default profile", + ), ), migrations.AlterField( - model_name='portprofile', - name='ra_guard', - field=models.BooleanField(default=False, help_text='Protect against rogue RA', verbose_name='RA guard'), + model_name="portprofile", + name="ra_guard", + field=models.BooleanField( + default=False, + help_text="Protect against rogue RA", + verbose_name="RA guard", + ), ), migrations.AlterField( - model_name='portprofile', - name='radius_mode', - field=models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], default='COMMON', help_text='In case of MAC-authentication : mode COMMON or STRICT on this port', max_length=32, verbose_name='RADIUS mode'), + model_name="portprofile", + name="radius_mode", + field=models.CharField( + choices=[("STRICT", "STRICT"), ("COMMON", "COMMON")], + default="COMMON", + help_text="In case of MAC-authentication : mode COMMON or STRICT on this port", + max_length=32, + verbose_name="RADIUS mode", + ), ), migrations.AlterField( - model_name='portprofile', - name='radius_type', - field=models.CharField(choices=[('NO', 'NO'), ('802.1X', '802.1X'), ('MAC-radius', 'MAC-radius')], help_text='Type of RADIUS authentication : inactive, MAC-address or 802.1X', max_length=32, verbose_name='RADIUS type'), + model_name="portprofile", + name="radius_type", + field=models.CharField( + choices=[ + ("NO", "NO"), + ("802.1X", "802.1X"), + ("MAC-radius", "MAC-radius"), + ], + help_text="Type of RADIUS authentication : inactive, MAC-address or 802.1X", + max_length=32, + verbose_name="RADIUS type", + ), ), migrations.AlterField( - model_name='portprofile', - name='speed', - field=models.CharField(choices=[('10-half', '10-half'), ('100-half', '100-half'), ('10-full', '10-full'), ('100-full', '100-full'), ('1000-full', '1000-full'), ('auto', 'auto'), ('auto-10', 'auto-10'), ('auto-100', 'auto-100')], default='auto', help_text='Port speed limit', max_length=32), + model_name="portprofile", + name="speed", + field=models.CharField( + choices=[ + ("10-half", "10-half"), + ("100-half", "100-half"), + ("10-full", "10-full"), + ("100-full", "100-full"), + ("1000-full", "1000-full"), + ("auto", "auto"), + ("auto-10", "auto-10"), + ("auto-100", "auto-100"), + ], + default="auto", + help_text="Port speed limit", + max_length=32, + ), ), migrations.AlterField( - model_name='switch', - name='model', - field=models.ForeignKey(blank=True, help_text='Switch model', null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.ModelSwitch'), + model_name="switch", + name="model", + field=models.ForeignKey( + blank=True, + help_text="Switch model", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="topologie.ModelSwitch", + ), ), migrations.AlterField( - model_name='switch', - name='number', - field=models.PositiveIntegerField(help_text='Number of ports'), + model_name="switch", + name="number", + field=models.PositiveIntegerField(help_text="Number of ports"), ), migrations.AlterField( - model_name='switch', - name='stack_member_id', + model_name="switch", + name="stack_member_id", field=models.PositiveIntegerField(blank=True, null=True), ), migrations.AlterField( - model_name='switch', - name='switchbay', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.SwitchBay'), + model_name="switch", + name="switchbay", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="topologie.SwitchBay", + ), ), migrations.AlterField( - model_name='switchbay', - name='info', + model_name="switchbay", + name="info", field=models.CharField(blank=True, max_length=255, null=True), ), ] diff --git a/topologie/migrations/0063_auto_20180919_2225.py b/topologie/migrations/0063_auto_20180919_2225.py index 45228340..1312f405 100644 --- a/topologie/migrations/0063_auto_20180919_2225.py +++ b/topologie/migrations/0063_auto_20180919_2225.py @@ -9,24 +9,36 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('preferences', '0051_auto_20180919_2225'), - ('topologie', '0062_auto_20180815_1918'), + ("preferences", "0051_auto_20180919_2225"), + ("topologie", "0062_auto_20180815_1918"), ] operations = [ migrations.AddField( - model_name='modelswitch', - name='firmware', + model_name="modelswitch", + name="firmware", field=models.CharField(blank=True, max_length=255, null=True), ), migrations.AddField( - model_name='switch', - name='management_creds', - field=models.ForeignKey(blank=True, help_text='Identifiant de management de ce switch', null=True, on_delete=django.db.models.deletion.PROTECT, to='preferences.SwitchManagementCred'), + model_name="switch", + name="management_creds", + field=models.ForeignKey( + blank=True, + help_text="Identifiant de management de ce switch", + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="preferences.SwitchManagementCred", + ), ), migrations.AddField( - model_name='switch', - name='radius_key', - field=models.ForeignKey(blank=True, help_text='Clef radius du switch', null=True, on_delete=django.db.models.deletion.PROTECT, to='preferences.RadiusKey'), + model_name="switch", + name="radius_key", + field=models.ForeignKey( + blank=True, + help_text="Clef radius du switch", + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="preferences.RadiusKey", + ), ), ] diff --git a/topologie/migrations/0064_switch_automatic_provision.py b/topologie/migrations/0064_switch_automatic_provision.py index d8d280cf..99b68d29 100644 --- a/topologie/migrations/0064_switch_automatic_provision.py +++ b/topologie/migrations/0064_switch_automatic_provision.py @@ -7,14 +7,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0063_auto_20180919_2225'), - ] + dependencies = [("topologie", "0063_auto_20180919_2225")] operations = [ migrations.AddField( - model_name='switch', - name='automatic_provision', - field=models.BooleanField(default=False, help_text='Provision automatique de ce switch'), - ), + model_name="switch", + name="automatic_provision", + field=models.BooleanField( + default=False, help_text="Provision automatique de ce switch" + ), + ) ] diff --git a/topologie/migrations/0065_auto_20180927_1836.py b/topologie/migrations/0065_auto_20180927_1836.py index 80f3eb2d..2a5b9ed0 100644 --- a/topologie/migrations/0065_auto_20180927_1836.py +++ b/topologie/migrations/0065_auto_20180927_1836.py @@ -7,14 +7,25 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0064_switch_automatic_provision'), - ] + dependencies = [("topologie", "0064_switch_automatic_provision")] operations = [ migrations.AlterField( - model_name='portprofile', - name='profil_default', - field=models.CharField(blank=True, choices=[('room', 'room'), ('access_point', 'access_point'), ('uplink', 'uplink'), ('asso_machine', 'asso_machine'), ('nothing', 'nothing')], max_length=32, null=True, unique=True, verbose_name='Default profile'), - ), + model_name="portprofile", + name="profil_default", + field=models.CharField( + blank=True, + choices=[ + ("room", "room"), + ("access_point", "access_point"), + ("uplink", "uplink"), + ("asso_machine", "asso_machine"), + ("nothing", "nothing"), + ], + max_length=32, + null=True, + unique=True, + verbose_name="Default profile", + ), + ) ] diff --git a/topologie/migrations/0066_modelswitch_commercial_name.py b/topologie/migrations/0066_modelswitch_commercial_name.py index 9a01a4f0..2646c62b 100644 --- a/topologie/migrations/0066_modelswitch_commercial_name.py +++ b/topologie/migrations/0066_modelswitch_commercial_name.py @@ -7,14 +7,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0065_auto_20180927_1836'), - ] + dependencies = [("topologie", "0065_auto_20180927_1836")] operations = [ migrations.AddField( - model_name='modelswitch', - name='commercial_name', + model_name="modelswitch", + name="commercial_name", field=models.CharField(blank=True, max_length=255, null=True), - ), + ) ] diff --git a/topologie/migrations/0067_auto_20181230_1819.py b/topologie/migrations/0067_auto_20181230_1819.py index 57f268ea..f9f56f09 100644 --- a/topologie/migrations/0067_auto_20181230_1819.py +++ b/topologie/migrations/0067_auto_20181230_1819.py @@ -9,58 +9,103 @@ import re2o.mixins class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0066_modelswitch_commercial_name'), - ] + dependencies = [("topologie", "0066_modelswitch_commercial_name")] operations = [ migrations.CreateModel( - name='ModuleOnSwitch', + name="ModuleOnSwitch", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('slot', models.CharField(help_text='Slot on switch', max_length=15, verbose_name='Slot')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "slot", + models.CharField( + help_text="Slot on switch", max_length=15, verbose_name="Slot" + ), + ), ], options={ - 'verbose_name': 'link between switchs and modules', - 'permissions': (('view_moduleonswitch', 'Can view a moduleonswitch object'),), + "verbose_name": "link between switchs and modules", + "permissions": ( + ("view_moduleonswitch", "Can view a moduleonswitch object"), + ), }, bases=(re2o.mixins.AclMixin, re2o.mixins.RevMixin, models.Model), ), migrations.CreateModel( - name='ModuleSwitch', + name="ModuleSwitch", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('reference', models.CharField(help_text='Reference of a module', max_length=255, verbose_name='Module reference')), - ('comment', models.CharField(blank=True, help_text='Comment', max_length=255, null=True, verbose_name='Comment')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "reference", + models.CharField( + help_text="Reference of a module", + max_length=255, + verbose_name="Module reference", + ), + ), + ( + "comment", + models.CharField( + blank=True, + help_text="Comment", + max_length=255, + null=True, + verbose_name="Comment", + ), + ), ], options={ - 'verbose_name': 'Module of a switch', - 'permissions': (('view_moduleswitch', 'Can view a module object'),), + "verbose_name": "Module of a switch", + "permissions": (("view_moduleswitch", "Can view a module object"),), }, bases=(re2o.mixins.AclMixin, re2o.mixins.RevMixin, models.Model), ), migrations.AddField( - model_name='modelswitch', - name='is_itself_module', - field=models.BooleanField(default=False, help_text='Does the switch, itself, considered as a module'), + model_name="modelswitch", + name="is_itself_module", + field=models.BooleanField( + default=False, + help_text="Does the switch, itself, considered as a module", + ), ), migrations.AddField( - model_name='modelswitch', - name='is_modular', - field=models.BooleanField(default=False, help_text='Is this switch model modular'), + model_name="modelswitch", + name="is_modular", + field=models.BooleanField( + default=False, help_text="Is this switch model modular" + ), ), migrations.AddField( - model_name='moduleonswitch', - name='module', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='topologie.ModuleSwitch'), + model_name="moduleonswitch", + name="module", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="topologie.ModuleSwitch" + ), ), migrations.AddField( - model_name='moduleonswitch', - name='switch', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='topologie.Switch'), + model_name="moduleonswitch", + name="switch", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="topologie.Switch" + ), ), migrations.AlterUniqueTogether( - name='moduleonswitch', - unique_together=set([('slot', 'switch')]), + name="moduleonswitch", unique_together=set([("slot", "switch")]) ), ] diff --git a/topologie/migrations/0068_auto_20190102_1758.py b/topologie/migrations/0068_auto_20190102_1758.py index b03e7ae5..41342c90 100644 --- a/topologie/migrations/0068_auto_20190102_1758.py +++ b/topologie/migrations/0068_auto_20190102_1758.py @@ -7,14 +7,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0067_auto_20181230_1819'), - ] + dependencies = [("topologie", "0067_auto_20181230_1819")] operations = [ migrations.AlterField( - model_name='modelswitch', - name='is_itself_module', - field=models.BooleanField(default=False, help_text='Is the switch, itself, considered as a module'), - ), + model_name="modelswitch", + name="is_itself_module", + field=models.BooleanField( + default=False, help_text="Is the switch, itself, considered as a module" + ), + ) ] diff --git a/topologie/migrations/0069_auto_20190108_1439.py b/topologie/migrations/0069_auto_20190108_1439.py index ba13942b..9a26746e 100644 --- a/topologie/migrations/0069_auto_20190108_1439.py +++ b/topologie/migrations/0069_auto_20190108_1439.py @@ -8,52 +8,105 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('topologie', '0068_auto_20190102_1758'), - ] + dependencies = [("topologie", "0068_auto_20190102_1758")] operations = [ migrations.AlterModelOptions( - name='moduleonswitch', - options={'permissions': (('view_moduleonswitch', 'Can view a link between switch and module object'),), 'verbose_name': 'link between switch and module', 'verbose_name_plural': 'links between switch and module'}, + name="moduleonswitch", + options={ + "permissions": ( + ( + "view_moduleonswitch", + "Can view a link between switch and module object", + ), + ), + "verbose_name": "link between switch and module", + "verbose_name_plural": "links between switch and module", + }, ), migrations.AlterModelOptions( - name='moduleswitch', - options={'permissions': (('view_moduleswitch', 'Can view a switch module object'),), 'verbose_name': 'switch module', 'verbose_name_plural': 'switch modules'}, + name="moduleswitch", + options={ + "permissions": ( + ("view_moduleswitch", "Can view a switch module object"), + ), + "verbose_name": "switch module", + "verbose_name_plural": "switch modules", + }, ), migrations.AlterField( - model_name='modelswitch', - name='is_itself_module', - field=models.BooleanField(default=False, help_text='The switch is considered as a module.'), + model_name="modelswitch", + name="is_itself_module", + field=models.BooleanField( + default=False, help_text="The switch is considered as a module." + ), ), migrations.AlterField( - model_name='modelswitch', - name='is_modular', - field=models.BooleanField(default=False, help_text='The switch model is modular.'), + model_name="modelswitch", + name="is_modular", + field=models.BooleanField( + default=False, help_text="The switch model is modular." + ), ), migrations.AlterField( - model_name='portprofile', - name='profil_default', - field=models.CharField(blank=True, choices=[('room', 'Room'), ('access_point', 'Access point'), ('uplink', 'Uplink'), ('asso_machine', 'Organisation machine'), ('nothing', 'Nothing')], max_length=32, null=True, unique=True, verbose_name='Default profile'), + model_name="portprofile", + name="profil_default", + field=models.CharField( + blank=True, + choices=[ + ("room", "Room"), + ("access_point", "Access point"), + ("uplink", "Uplink"), + ("asso_machine", "Organisation machine"), + ("nothing", "Nothing"), + ], + max_length=32, + null=True, + unique=True, + verbose_name="Default profile", + ), ), migrations.AlterField( - model_name='portprofile', - name='radius_type', - field=models.CharField(choices=[('NO', 'NO'), ('802.1X', '802.1X'), ('MAC-radius', 'MAC-RADIUS')], help_text='Type of RADIUS authentication : inactive, MAC-address or 802.1X', max_length=32, verbose_name='RADIUS type'), + model_name="portprofile", + name="radius_type", + field=models.CharField( + choices=[ + ("NO", "NO"), + ("802.1X", "802.1X"), + ("MAC-radius", "MAC-RADIUS"), + ], + help_text="Type of RADIUS authentication : inactive, MAC-address or 802.1X", + max_length=32, + verbose_name="RADIUS type", + ), ), migrations.AlterField( - model_name='switch', - name='automatic_provision', - field=models.BooleanField(default=False, help_text='Automatic provision for the switch'), + model_name="switch", + name="automatic_provision", + field=models.BooleanField( + default=False, help_text="Automatic provision for the switch" + ), ), migrations.AlterField( - model_name='switch', - name='management_creds', - field=models.ForeignKey(blank=True, help_text='Management credentials for the switch', null=True, on_delete=django.db.models.deletion.PROTECT, to='preferences.SwitchManagementCred'), + model_name="switch", + name="management_creds", + field=models.ForeignKey( + blank=True, + help_text="Management credentials for the switch", + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="preferences.SwitchManagementCred", + ), ), migrations.AlterField( - model_name='switch', - name='radius_key', - field=models.ForeignKey(blank=True, help_text='RADIUS key of the switch', null=True, on_delete=django.db.models.deletion.PROTECT, to='preferences.RadiusKey'), + model_name="switch", + name="radius_key", + field=models.ForeignKey( + blank=True, + help_text="RADIUS key of the switch", + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="preferences.RadiusKey", + ), ), ] diff --git a/topologie/migrations/0070_auto_20190218_1743.py b/topologie/migrations/0070_auto_20190218_1743.py new file mode 100644 index 00000000..df16dfca --- /dev/null +++ b/topologie/migrations/0070_auto_20190218_1743.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2019-02-18 16:43 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import re2o.mixins + + +class Migration(migrations.Migration): + + dependencies = [("topologie", "0069_auto_20190108_1439")] + + def create_dormitory(apps, schema_editor): + db_alias = schema_editor.connection.alias + dormitory = apps.get_model("topologie", "Dormitory") + building = apps.get_model("topologie", "Building") + dorm = dormitory.objects.using(db_alias).create(name="Residence") + building.objects.using(db_alias).update(dormitory=dorm) + + def delete_dormitory(apps, schema_editor): + pass + + operations = [ + migrations.CreateModel( + name="Dormitory", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)), + ], + options={ + "verbose_name": "dormitory", + "permissions": (("view_dormitory", "Can view a dormitory object"),), + "verbose_name_plural": "dormitories", + }, + bases=(re2o.mixins.AclMixin, re2o.mixins.RevMixin, models.Model), + ), + migrations.AddField( + model_name="building", + name="dormitory", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="topologie.Dormitory", + ), + preserve_default=False, + ), + migrations.RunPython(create_dormitory, delete_dormitory), + ] diff --git a/topologie/migrations/0071_auto_20190218_1936.py b/topologie/migrations/0071_auto_20190218_1936.py new file mode 100644 index 00000000..39db3e31 --- /dev/null +++ b/topologie/migrations/0071_auto_20190218_1936.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2019-02-18 18:36 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [("topologie", "0070_auto_20190218_1743")] + + def transfer_room(apps, schema_editor): + db_alias = schema_editor.connection.alias + room_obj = apps.get_model("topologie", "Room") + building_obj = apps.get_model("topologie", "Building") + dorm_obj = apps.get_model("topologie", "Dormitory") + dorm = dorm_obj.objects.using(db_alias).first() + for room in room_obj.objects.using(db_alias).all(): + building, created = building_obj.objects.using(db_alias).get_or_create( + name=room.name[0].upper(), dormitory=dorm + ) + room.building = building + room.name = room.name[1:] + room.save() + + def untransfer_room(apps, schema_editor): + pass + + operations = [ + migrations.AlterField( + model_name="room", name="name", field=models.CharField(max_length=255) + ), + migrations.AddField( + model_name="room", + name="building", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="topologie.Building", + ), + ), + migrations.AlterUniqueTogether( + name="room", unique_together=set([("name", "building")]) + ), + migrations.AlterField( + model_name="building", + name="dormitory", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, to="topologie.Dormitory" + ), + ), + migrations.RunPython(transfer_room, untransfer_room), + migrations.AlterField( + model_name="room", + name="building", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, to="topologie.Building" + ), + ), + ] diff --git a/topologie/migrations/0072_auto_20190720_2318.py b/topologie/migrations/0072_auto_20190720_2318.py new file mode 100644 index 00000000..7e276c3e --- /dev/null +++ b/topologie/migrations/0072_auto_20190720_2318.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2019-07-20 21:18 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [("topologie", "0071_auto_20190218_1936")] + + operations = [ + migrations.AlterModelOptions( + name="room", + options={ + "ordering": ["building__name"], + "permissions": (("view_room", "Can view a room object"),), + "verbose_name": "room", + "verbose_name_plural": "rooms", + }, + ), + migrations.AddField( + model_name="portprofile", + name="on_dormitory", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="dormitory_ofprofil", + to="topologie.Dormitory", + verbose_name="Profil on dormitory", + ), + ), + migrations.AlterField( + model_name="portprofile", + name="profil_default", + field=models.CharField( + blank=True, + choices=[ + ("room", "Room"), + ("access_point", "Access point"), + ("uplink", "Uplink"), + ("asso_machine", "Organisation machine"), + ("nothing", "Nothing"), + ], + max_length=32, + null=True, + verbose_name="Default profile", + ), + ), + migrations.AlterUniqueTogether( + name="portprofile", + unique_together=set([("on_dormitory", "profil_default")]), + ), + ] diff --git a/topologie/migrations/0073_auto_20191120_0159.py b/topologie/migrations/0073_auto_20191120_0159.py new file mode 100644 index 00000000..482c21c8 --- /dev/null +++ b/topologie/migrations/0073_auto_20191120_0159.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.23 on 2019-11-20 00:59 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0072_auto_20190720_2318'), + ] + + operations = [ + migrations.AlterField( + model_name='accesspoint', + name='location', + field=models.CharField(blank=True, help_text="Details about the AP's location.", max_length=255, null=True), + ), + migrations.AlterField( + model_name='moduleonswitch', + name='slot', + field=models.CharField(help_text='Slot on switch.', max_length=15, verbose_name='slot'), + ), + migrations.AlterField( + model_name='moduleswitch', + name='comment', + field=models.CharField(blank=True, help_text='Comment.', max_length=255, null=True, verbose_name='comment'), + ), + migrations.AlterField( + model_name='moduleswitch', + name='reference', + field=models.CharField(help_text='Reference of a module.', max_length=255, verbose_name='module reference'), + ), + migrations.AlterField( + model_name='port', + name='state', + field=models.BooleanField(default=True, help_text='Port state Active.', verbose_name='port state Active'), + ), + migrations.AlterField( + model_name='portprofile', + name='arp_protect', + field=models.BooleanField(default=False, help_text='Check if IP address is DHCP assigned.', verbose_name='ARP protection'), + ), + migrations.AlterField( + model_name='portprofile', + name='dhcp_snooping', + field=models.BooleanField(default=False, help_text='Protect against rogue DHCP.', verbose_name='DHCP snooping'), + ), + migrations.AlterField( + model_name='portprofile', + name='dhcpv6_snooping', + field=models.BooleanField(default=False, help_text='Protect against rogue DHCPv6.', verbose_name='DHCPv6 snooping'), + ), + migrations.AlterField( + model_name='portprofile', + name='flow_control', + field=models.BooleanField(default=False, help_text='Flow control.'), + ), + migrations.AlterField( + model_name='portprofile', + name='loop_protect', + field=models.BooleanField(default=False, help_text='Protect against loop.', verbose_name='loop protection'), + ), + migrations.AlterField( + model_name='portprofile', + name='mac_limit', + field=models.IntegerField(blank=True, help_text='Limit of MAC-address on this port.', null=True, verbose_name='MAC limit'), + ), + migrations.AlterField( + model_name='portprofile', + name='name', + field=models.CharField(max_length=255, verbose_name='name'), + ), + migrations.AlterField( + model_name='portprofile', + name='on_dormitory', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='dormitory_ofprofil', to='topologie.Dormitory', verbose_name='profile on dormitory'), + ), + migrations.AlterField( + model_name='portprofile', + name='profil_default', + field=models.CharField(blank=True, choices=[('room', 'Room'), ('access_point', 'Access point'), ('uplink', 'Uplink'), ('asso_machine', 'Organisation machine'), ('nothing', 'Nothing')], max_length=32, null=True, verbose_name='default profile'), + ), + migrations.AlterField( + model_name='portprofile', + name='ra_guard', + field=models.BooleanField(default=False, help_text='Protect against rogue RA.', verbose_name='RA guard'), + ), + migrations.AlterField( + model_name='portprofile', + name='radius_mode', + field=models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], default='COMMON', help_text='In case of MAC-authentication: mode COMMON or STRICT on this port.', max_length=32, verbose_name='RADIUS mode'), + ), + migrations.AlterField( + model_name='portprofile', + name='radius_type', + field=models.CharField(choices=[('NO', 'NO'), ('802.1X', '802.1X'), ('MAC-radius', 'MAC-RADIUS')], help_text='Type of RADIUS authentication: inactive, MAC-address or 802.1X.', max_length=32, verbose_name='RADIUS type'), + ), + migrations.AlterField( + model_name='portprofile', + name='speed', + field=models.CharField(choices=[('10-half', '10-half'), ('100-half', '100-half'), ('10-full', '10-full'), ('100-full', '100-full'), ('1000-full', '1000-full'), ('auto', 'auto'), ('auto-10', 'auto-10'), ('auto-100', 'auto-100')], default='auto', help_text='Port speed limit.', max_length=32), + ), + migrations.AlterField( + model_name='switch', + name='automatic_provision', + field=models.BooleanField(default=False, help_text='Automatic provision for the switch.'), + ), + migrations.AlterField( + model_name='switch', + name='management_creds', + field=models.ForeignKey(blank=True, help_text='Management credentials for the switch.', null=True, on_delete=django.db.models.deletion.PROTECT, to='preferences.SwitchManagementCred'), + ), + migrations.AlterField( + model_name='switch', + name='model', + field=models.ForeignKey(blank=True, help_text='Switch model.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.ModelSwitch'), + ), + migrations.AlterField( + model_name='switch', + name='number', + field=models.PositiveIntegerField(help_text='Number of ports.'), + ), + migrations.AlterField( + model_name='switch', + name='radius_key', + field=models.ForeignKey(blank=True, help_text='RADIUS key of the switch.', null=True, on_delete=django.db.models.deletion.PROTECT, to='preferences.RadiusKey'), + ), + ] diff --git a/topologie/migrations/__init__.py b/topologie/migrations/__init__.py index 20abb0d2..b409e525 100644 --- a/topologie/migrations/__init__.py +++ b/topologie/migrations/__init__.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -19,4 +19,3 @@ # 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. - diff --git a/topologie/models.py b/topologie/models.py index 3e093b40..ab6a22dd 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -42,6 +42,7 @@ import itertools from django.db import models from django.db.models.signals import post_save, post_delete from django.utils.functional import cached_property +from django.core.cache import cache from django.dispatch import receiver from django.core.exceptions import ValidationError from django.db import IntegrityError @@ -49,12 +50,8 @@ from django.db import transaction from django.utils.translation import ugettext_lazy as _ from reversion import revisions as reversion -from preferences.models import ( - OptionalTopologie, - RadiusKey, - SwitchManagementCred -) -from machines.models import Machine, regen +from preferences.models import OptionalTopologie, RadiusKey, SwitchManagementCred +from machines.models import Machine, regen, Role, MachineType, Ipv6List from re2o.mixins import AclMixin, RevMixin @@ -70,9 +67,7 @@ class Stack(AclMixin, RevMixin, models.Model): member_id_max = models.PositiveIntegerField() class Meta: - permissions = ( - ("view_stack", _("Can view a stack object")), - ) + permissions = (("view_stack", _("Can view a stack object")),) verbose_name = _("switches stack") verbose_name_plural = _("switches stacks") @@ -89,8 +84,7 @@ class Stack(AclMixin, RevMixin, models.Model): """ Verification que l'id_max < id_min""" if self.member_id_max < self.member_id_min: raise ValidationError( - {'member_id_max': _("The maximum ID is less than the" - " minimum ID.")} + {"member_id_max": _("The maximum ID is less than the minimum ID.")} ) @@ -102,38 +96,30 @@ class AccessPoint(AclMixin, Machine): location = models.CharField( max_length=255, - help_text=_("Details about the AP's location"), + help_text=_("Details about the AP's location."), blank=True, - null=True + null=True, ) class Meta: - permissions = ( - ("view_accesspoint", _("Can view an access point object")), - ) + permissions = (("view_accesspoint", _("Can view an access point object")),) verbose_name = _("access point") verbose_name_plural = _("access points") def port(self): """Return the queryset of ports for this device""" - return Port.objects.filter( - machine_interface__machine=self - ) + return Port.objects.filter(machine_interface__machine=self) def switch(self): """Return the switch where this is plugged""" - return Switch.objects.filter( - ports__machine_interface__machine=self - ) + return Switch.objects.filter(ports__machine_interface__machine=self) def building(self): """ Return the building of the AP/Server (building of the switchs connected to...) """ - return Building.objects.filter( - switchbay__switch=self.switch() - ) + return Building.objects.filter(switchbay__switch=self.switch()) @cached_property def short_name(self): @@ -160,24 +146,18 @@ class Server(Machine): def port(self): """Return the queryset of ports for this device""" - return Port.objects.filter( - machine_interface__machine=self - ) + return Port.objects.filter(machine_interface__machine=self) def switch(self): """Return the switch where this is plugged""" - return Switch.objects.filter( - ports__machine_interface__machine=self - ) + return Switch.objects.filter(ports__machine_interface__machine=self) def building(self): """ Return the building of the AP/Server (building of the switchs connected to...) """ - return Building.objects.filter( - switchbay__switch=self.switch() - ) + return Building.objects.filter(switchbay__switch=self.switch()) @cached_property def short_name(self): @@ -207,57 +187,42 @@ class Switch(AclMixin, Machine): Validation au save que l'id du stack est bien dans le range id_min id_max de la stack parente""" - number = models.PositiveIntegerField( - help_text=_("Number of ports") - ) + number = models.PositiveIntegerField(help_text=_("Number of ports.")) stack = models.ForeignKey( - 'topologie.Stack', - blank=True, - null=True, - on_delete=models.SET_NULL - ) - stack_member_id = models.PositiveIntegerField( - blank=True, - null=True + "topologie.Stack", blank=True, null=True, on_delete=models.SET_NULL ) + stack_member_id = models.PositiveIntegerField(blank=True, null=True) model = models.ForeignKey( - 'topologie.ModelSwitch', + "topologie.ModelSwitch", blank=True, null=True, on_delete=models.SET_NULL, - help_text=_("Switch model") + help_text=_("Switch model."), ) switchbay = models.ForeignKey( - 'topologie.SwitchBay', - blank=True, - null=True, - on_delete=models.SET_NULL, + "topologie.SwitchBay", blank=True, null=True, on_delete=models.SET_NULL ) radius_key = models.ForeignKey( - 'preferences.RadiusKey', + "preferences.RadiusKey", blank=True, null=True, on_delete=models.PROTECT, - help_text=_("RADIUS key of the switch") + help_text=_("RADIUS key of the switch."), ) management_creds = models.ForeignKey( - 'preferences.SwitchManagementCred', + "preferences.SwitchManagementCred", blank=True, null=True, on_delete=models.PROTECT, - help_text=_("Management credentials for the switch") + help_text=_("Management credentials for the switch."), ) automatic_provision = models.BooleanField( - default=False, - help_text=_("Automatic provision for the switch") + default=False, help_text=_("Automatic provision for the switch.") ) - class Meta: - unique_together = ('stack', 'stack_member_id') - permissions = ( - ("view_switch", _("Can view a switch object")), - ) + unique_together = ("stack", "stack_member_id") + permissions = (("view_switch", _("Can view a switch object")),) verbose_name = _("switch") verbose_name_plural = _("switches") @@ -267,45 +232,58 @@ class Switch(AclMixin, Machine): super(Switch, self).clean() if self.stack is not None: if self.stack_member_id is not None: - if (self.stack_member_id > self.stack.member_id_max) or\ - (self.stack_member_id < self.stack.member_id_min): + if (self.stack_member_id > self.stack.member_id_max) or ( + self.stack_member_id < self.stack.member_id_min + ): raise ValidationError( - {'stack_member_id': _("The switch ID exceeds the" - " limits allowed by the stack.")} - ) + { + "stack_member_id": _( + "The switch ID exceeds the" + " limits allowed by the stack." + ) + } + ) else: raise ValidationError( - {'stack_member_id': _("The stack member ID can't be" - " void.")} + {"stack_member_id": _("The stack member ID can't be void.")} ) def create_ports(self, begin, end): """ Crée les ports de begin à end si les valeurs données sont cohérentes. """ if end < begin: - raise ValidationError(_("The end port is less than the start" - " port.")) + raise ValidationError(_("The end port is less than the start port.")) ports_to_create = range(begin, end + 1) - existing_ports = Port.objects.filter(switch=self.switch).values_list('port', flat=True) + existing_ports = Port.objects.filter(switch=self.switch).values_list( + "port", flat=True + ) non_existing_ports = list(set(ports_to_create) - set(existing_ports)) if len(non_existing_ports) + existing_ports.count() > self.number: raise ValidationError(_("This switch can't have that many ports.")) with transaction.atomic(), reversion.create_revision(): - reversion.set_comment(_("Creation")) - Port.objects.bulk_create([Port(switch=self.switch, port=port_id) for port_id in non_existing_ports]) + reversion.set_comment("Creation") + Port.objects.bulk_create( + [ + Port(switch=self.switch, port=port_id) + for port_id in non_existing_ports + ] + ) def main_interface(self): """ Returns the 'main' interface of the switch It must the the management interface for that device""" - switch_iptype = OptionalTopologie.get_cached_value('switchs_ip_type') + switch_iptype = OptionalTopologie.get_cached_value("switchs_ip_type") if switch_iptype: - return self.interface_set.filter(type__ip_type=switch_iptype).first() + return ( + self.interface_set.filter(machine_type__ip_type=switch_iptype).first() + or self.interface_set.first() + ) return self.interface_set.first() @cached_property def get_name(self): - return self.name or getattr(self.main_interface(), 'domain', 'Unknown') + return self.name or getattr(self.main_interface(), "domain", "Unknown") @cached_property def get_radius_key(self): @@ -320,27 +298,58 @@ class Switch(AclMixin, Machine): else: return None + @cached_property + def get_radius_servers_objects(self): + return Role.all_interfaces_for_roletype("radius-server").filter( + machine_type__in=MachineType.objects.filter( + interface__in=self.interface_set.all() + ) + ) + + @cached_property + def get_radius_servers(self): + def return_ips_dict(interfaces): + return { + "ipv4": [str(interface.ipv4) for interface in interfaces], + "ipv6": Ipv6List.objects.filter(interface__in=interfaces).values_list( + "ipv6", flat=True + ), + } + + return return_ips_dict(self.get_radius_servers_objects) + @cached_property def get_management_cred(self): """Retourne l'objet des creds de managament de ce switch""" - return self.management_creds or SwitchManagementCred.objects.filter(default_switch=True).first() + return ( + self.management_creds + or SwitchManagementCred.objects.filter(default_switch=True).first() + ) @cached_property def get_management_cred_value(self): """Retourne un dict des creds de management du switch""" if self.get_management_cred: - return {'id': self.get_management_cred.management_id, 'pass': self.get_management_cred.management_pass} + return { + "id": self.get_management_cred.management_id, + "pass": self.get_management_cred.management_pass, + } else: return None @cached_property def rest_enabled(self): - return OptionalTopologie.get_cached_value('switchs_rest_management') or self.automatic_provision + return ( + OptionalTopologie.get_cached_value("switchs_rest_management") + or self.automatic_provision + ) @cached_property def web_management_enabled(self): - sw_management = OptionalTopologie.get_cached_value('switchs_web_management') - sw_management_ssl = OptionalTopologie.get_cached_value('switchs_web_management_ssl') + sw_management = OptionalTopologie.get_cached_value("switchs_web_management") + sw_management_ssl = OptionalTopologie.get_cached_value( + "switchs_web_management_ssl" + ) if sw_management_ssl: return "ssl" elif sw_management: @@ -361,24 +370,90 @@ class Switch(AclMixin, Machine): @cached_property def interfaces_subnet(self): """Return dict ip:subnet for all ip of the switch""" - return dict((str(interface.ipv4), interface.type.ip_type.ip_set_full_info) for interface in self.interface_set.all()) + return dict( + ( + str(interface.ipv4), + interface.machine_type.ip_type.ip_net_full_info + or interface.machine_type.ip_type.ip_set_full_info[0], + ) + for interface in self.interface_set.all() + ) @cached_property def interfaces6_subnet(self): """Return dict ip6:subnet for all ipv6 of the switch""" - return dict((str(interface.ipv6().first()), interface.type.ip_type.ip6_set_full_info) for interface in self.interface_set.all()) + return dict( + ( + str(interface.ipv6().first()), + interface.machine_type.ip_type.ip6_set_full_info, + ) + for interface in self.interface_set.all() + ) @cached_property def list_modules(self): """Return modules of that switch, list of dict (rank, reference)""" modules = [] - if getattr(self.model, 'is_modular', None): + if getattr(self.model, "is_modular", None): if self.model.is_itself_module: modules.append((1, self.model.reference)) for module_of_self in self.moduleonswitch_set.all(): modules.append((module_of_self.slot, module_of_self.module.reference)) return modules + @cached_property + def get_dormitory(self): + """Returns the dormitory of that switch""" + if self.switchbay: + return self.switchbay.building.dormitory + else: + return None + + @classmethod + def nothing_profile(cls): + """Return default nothing port profile""" + nothing_profile, _created = PortProfile.objects.get_or_create( + profil_default="nothing", name="nothing", radius_type="NO" + ) + return nothing_profile + + def profile_type_or_nothing(self, profile_type): + """Return the profile for a profile_type of this switch + + If exists, returns the defined default profile for a profile type on the dormitory which + the switch belongs + + Otherwise, returns the nothing profile""" + profile_queryset = PortProfile.objects.filter(profil_default=profile_type) + if self.get_dormitory: + port_profile = ( + profile_queryset.filter(on_dormitory=self.get_dormitory).first() + or profile_queryset.first() + ) + else: + port_profile = profile_queryset.first() + return port_profile or Switch.nothing_profile() + + @cached_property + def default_uplink_profile(self): + """Default uplink profile for that switch -- in cache""" + return self.profile_type_or_nothing("uplink") + + @cached_property + def default_access_point_profile(self): + """Default ap profile for that switch -- in cache""" + return self.profile_type_or_nothing("access_point") + + @cached_property + def default_room_profile(self): + """Default room profile for that switch -- in cache""" + return self.profile_type_or_nothing("room") + + @cached_property + def default_asso_machine_profile(self): + """Default asso machine profile for that switch -- in cache""" + return self.profile_type_or_nothing("asso_machine") + def __str__(self): return str(self.get_name) @@ -387,91 +462,77 @@ class ModelSwitch(AclMixin, RevMixin, models.Model): """Un modèle (au sens constructeur) de switch""" reference = models.CharField(max_length=255) - commercial_name = models.CharField( - max_length=255, - null=True, - blank=True - ) + commercial_name = models.CharField(max_length=255, null=True, blank=True) constructor = models.ForeignKey( - 'topologie.ConstructorSwitch', - on_delete=models.PROTECT - ) - firmware = models.CharField( - max_length=255, - null=True, - blank=True + "topologie.ConstructorSwitch", on_delete=models.PROTECT ) + firmware = models.CharField(max_length=255, null=True, blank=True) is_modular = models.BooleanField( - default=False, - help_text=_("The switch model is modular."), + default=False, help_text=_("The switch model is modular.") ) is_itself_module = models.BooleanField( - default=False, - help_text=_("The switch is considered as a module."), + default=False, help_text=_("The switch is considered as a module.") ) class Meta: - permissions = ( - ("view_modelswitch", _("Can view a switch model object")), - ) + permissions = (("view_modelswitch", _("Can view a switch model object")),) verbose_name = _("switch model") verbose_name_plural = _("switch models") def __str__(self): if self.commercial_name: - return str(self.constructor) + ' ' + str(self.commercial_name) + return str(self.constructor) + " " + str(self.commercial_name) else: - return str(self.constructor) + ' ' + self.reference + return str(self.constructor) + " " + self.reference class ModuleSwitch(AclMixin, RevMixin, models.Model): """A module of a switch""" + reference = models.CharField( max_length=255, - help_text=_("Reference of a module"), - verbose_name=_("Module reference") + help_text=_("Reference of a module."), + verbose_name=_("module reference"), ) comment = models.CharField( max_length=255, null=True, blank=True, - help_text=_("Comment"), - verbose_name=_("Comment") + help_text=_("Comment."), + verbose_name=_("comment"), ) class Meta: - permissions = ( - ("view_moduleswitch", _("Can view a switch module object")), - ) + permissions = (("view_moduleswitch", _("Can view a switch module object")),) verbose_name = _("switch module") verbose_name_plural = _("switch modules") - def __str__(self): return str(self.reference) class ModuleOnSwitch(AclMixin, RevMixin, models.Model): """Link beetween module and switch""" - module = models.ForeignKey('ModuleSwitch', on_delete=models.CASCADE) - switch = models.ForeignKey('Switch', on_delete=models.CASCADE) + + module = models.ForeignKey("ModuleSwitch", on_delete=models.CASCADE) + switch = models.ForeignKey("Switch", on_delete=models.CASCADE) slot = models.CharField( - max_length=15, - help_text=_("Slot on switch"), - verbose_name=_("Slot") + max_length=15, help_text=_("Slot on switch."), verbose_name=_("slot") ) class Meta: permissions = ( - ("view_moduleonswitch", _("Can view a link between switch and" - " module object")), + ( + "view_moduleonswitch", + _("Can view a link between switch and module object"), + ), ) verbose_name = _("link between switch and module") verbose_name_plural = _("links between switch and module") - unique_together = ['slot', 'switch'] + unique_together = ["slot", "switch"] def __str__(self): - return _("On slot ") + str(self.slot) + _(" of ") + str(self.switch) + return _("On slot %(slot)s of %(switch)s").format(slot=str(self.slot), switch=str(self.switch)) class ConstructorSwitch(AclMixin, RevMixin, models.Model): @@ -481,11 +542,10 @@ class ConstructorSwitch(AclMixin, RevMixin, models.Model): class Meta: permissions = ( - ("view_constructorswitch", _("Can view a switch constructor" - " object")), + ("view_constructorswitch", _("Can view a switch constructor object")), ) verbose_name = _("switch constructor") - verbose_name_plural = ("switch constructors") + verbose_name_plural = _("switch constructors") def __str__(self): return self.name @@ -495,20 +555,11 @@ class SwitchBay(AclMixin, RevMixin, models.Model): """Une baie de brassage""" name = models.CharField(max_length=255) - building = models.ForeignKey( - 'Building', - on_delete=models.PROTECT - ) - info = models.CharField( - max_length=255, - blank=True, - null=True - ) + building = models.ForeignKey("Building", on_delete=models.PROTECT) + info = models.CharField(max_length=255, blank=True, null=True) class Meta: - permissions = ( - ("view_switchbay", _("Can view a switch bay object")), - ) + permissions = (("view_switchbay", _("Can view a switch bay object")),) verbose_name = _("switch bay") verbose_name_plural = _("switch bays") @@ -516,15 +567,42 @@ class SwitchBay(AclMixin, RevMixin, models.Model): return self.name -class Building(AclMixin, RevMixin, models.Model): - """Un batiment""" +class Dormitory(AclMixin, RevMixin, models.Model): + """A student accomodation/dormitory + Une résidence universitaire""" name = models.CharField(max_length=255) class Meta: - permissions = ( - ("view_building", _("Can view a building object")), - ) + permissions = (("view_dormitory", _("Can view a dormitory object")),) + verbose_name = _("dormitory") + verbose_name_plural = _("dormitories") + + def all_ap_in(self): + """Returns all ap of the dorms""" + return AccessPoint.all_ap_in(self.building_set.all()) + + @classmethod + def is_multiple_dorms(cls): + multiple_dorms = cache.get("multiple_dorms") + if multiple_dorms: + return multiple_dorms + else: + return cache.get_or_set("multiple_dorms", cls.objects.count() > 1) + + def __str__(self): + return self.name + + +class Building(AclMixin, RevMixin, models.Model): + """A building of a dormitory + Un batiment""" + + name = models.CharField(max_length=255) + dormitory = models.ForeignKey("Dormitory", on_delete=models.PROTECT) + + class Meta: + permissions = (("view_building", _("Can view a building object")),) verbose_name = _("building") verbose_name_plural = _("buildings") @@ -532,8 +610,18 @@ class Building(AclMixin, RevMixin, models.Model): """Returns all ap of the building""" return AccessPoint.all_ap_in(self) + def get_name(self): + if Dormitory.is_multiple_dorms(): + return self.dormitory.name + " : " + self.name + else: + return self.name + + @cached_property + def cached_name(self): + return self.get_name() + def __str__(self): - return self.name + return self.cached_name class Port(AclMixin, RevMixin, models.Model): @@ -553,48 +641,28 @@ class Port(AclMixin, RevMixin, models.Model): de forcer un port sur un vlan particulier. S'additionne à la politique RADIUS""" - switch = models.ForeignKey( - 'Switch', - related_name="ports", - on_delete=models.CASCADE - ) + switch = models.ForeignKey("Switch", related_name="ports", on_delete=models.CASCADE) port = models.PositiveIntegerField() - room = models.ForeignKey( - 'Room', - on_delete=models.PROTECT, - blank=True, - null=True - ) + room = models.ForeignKey("Room", on_delete=models.PROTECT, blank=True, null=True) machine_interface = models.ForeignKey( - 'machines.Interface', - on_delete=models.SET_NULL, - blank=True, - null=True + "machines.Interface", on_delete=models.SET_NULL, blank=True, null=True ) related = models.OneToOneField( - 'self', - null=True, - blank=True, - related_name='related_port' + "self", null=True, blank=True, related_name="related_port" ) custom_profile = models.ForeignKey( - 'PortProfile', - on_delete=models.PROTECT, - blank=True, - null=True + "PortProfile", on_delete=models.PROTECT, blank=True, null=True ) state = models.BooleanField( default=True, - help_text=_("Port state Active"), - verbose_name=_("Port state Active") + help_text=_("Port state Active."), + verbose_name=_("port state Active"), ) details = models.CharField(max_length=255, blank=True) class Meta: - unique_together = ('switch', 'port') - permissions = ( - ("view_port", _("Can view a port object")), - ) + unique_together = ("switch", "port") + permissions = (("view_port", _("Can view a port object")),) verbose_name = _("port") verbose_name_plural = _("ports") @@ -613,43 +681,37 @@ class Port(AclMixin, RevMixin, models.Model): @cached_property def get_port_profile(self): """Return the config profil for this port - :returns: the profile of self (port)""" - def profile_or_nothing(profile): - port_profile = PortProfile.objects.filter( - profil_default=profile).first() - if port_profile: - return port_profile - else: - nothing_profile, _created = PortProfile.objects.get_or_create( - profil_default='nothing', - name='nothing', - radius_type='NO' - ) - return nothing_profile + :returns: the profile of self (port) + + If is defined a custom profile, returns it + elIf a default profile is defined for its dormitory, returns it + Else, returns the global default profil + If not exists, create a nothing profile""" if self.custom_profile: return self.custom_profile elif self.related: - return profile_or_nothing('uplink') + return self.switch.default_uplink_profile elif self.machine_interface: - if hasattr(self.machine_interface.machine, 'accesspoint'): - return profile_or_nothing('access_point') + if hasattr(self.machine_interface.machine, "accesspoint"): + return self.switch.default_access_point_profile else: - return profile_or_nothing('asso_machine') + return self.switch.default_asso_machine_profile elif self.room: - return profile_or_nothing('room') + return self.switch.default_room_profile else: - return profile_or_nothing('nothing') + return Switch.nothing_profile() @classmethod def get_instance(cls, portid, *_args, **kwargs): - return (cls.objects - .select_related('machine_interface__domain__extension') - .select_related('machine_interface__machine__switch') - .select_related('room') - .select_related('related') - .prefetch_related('switch__interface_set__domain__extension') - .get(pk=portid)) + return ( + cls.objects.select_related("machine_interface__domain__extension") + .select_related("machine_interface__machine__switch") + .select_related("room") + .select_related("related") + .prefetch_related("switch__interface_set__domain__extension") + .get(pk=portid) + ) def make_port_related(self): """ Synchronise le port distant sur self""" @@ -672,14 +734,19 @@ class Port(AclMixin, RevMixin, models.Model): A priori pas d'autre solution que de faire ça à la main. A priori tout cela est dans un bloc transaction, donc pas de problème de cohérence""" - if hasattr(self, 'switch'): + if hasattr(self, "switch"): if self.port > self.switch.number: raise ValidationError( _("The port can't exist, its number is too great.") ) - if (self.room and self.machine_interface or - self.room and self.related or - self.machine_interface and self.related): + if ( + self.room + and self.machine_interface + or self.room + and self.related + or self.machine_interface + and self.related + ): raise ValidationError( _("Room, interface and related port are mutually exclusive.") ) @@ -688,12 +755,14 @@ class Port(AclMixin, RevMixin, models.Model): if self.related and not self.related.related: if self.related.machine_interface or self.related.room: raise ValidationError( - _("The related port is already used, please clear it" - " before creating the relation.") + _( + "The related port is already used, please clear it" + " before creating the relation." + ) ) else: self.make_port_related() - elif hasattr(self, 'related_port'): + elif hasattr(self, "related_port"): self.clean_port_related() def __str__(self): @@ -703,143 +772,139 @@ class Port(AclMixin, RevMixin, models.Model): class Room(AclMixin, RevMixin, models.Model): """Une chambre/local contenant une prise murale""" - name = models.CharField(max_length=255, unique=True) + name = models.CharField(max_length=255) details = models.CharField(max_length=255, blank=True) + building = models.ForeignKey("Building", on_delete=models.PROTECT) class Meta: - ordering = ['name'] - permissions = ( - ("view_room", _("Can view a room object")), - ) + ordering = ["building__name"] + permissions = (("view_room", _("Can view a room object")),) verbose_name = _("room") verbose_name_plural = _("rooms") + unique_together = ("name", "building") def __str__(self): - return self.name + return self.building.cached_name + " " + self.name class PortProfile(AclMixin, RevMixin, models.Model): """Contains the information of the ports' configuration for a switch""" - TYPES = ( - ('NO', 'NO'), - ('802.1X', '802.1X'), - ('MAC-radius', _("MAC-RADIUS")), - ) - MODES = ( - ('STRICT', 'STRICT'), - ('COMMON', 'COMMON'), - ) + + TYPES = (("NO", "NO"), ("802.1X", "802.1X"), ("MAC-radius", _("MAC-RADIUS"))) + MODES = (("STRICT", "STRICT"), ("COMMON", "COMMON")) SPEED = ( - ('10-half', '10-half'), - ('100-half', '100-half'), - ('10-full', '10-full'), - ('100-full', '100-full'), - ('1000-full', '1000-full'), - ('auto', 'auto'), - ('auto-10', 'auto-10'), - ('auto-100', 'auto-100'), + ("10-half", "10-half"), + ("100-half", "100-half"), + ("10-full", "10-full"), + ("100-full", "100-full"), + ("1000-full", "1000-full"), + ("auto", "auto"), + ("auto-10", "auto-10"), + ("auto-100", "auto-100"), ) PROFIL_DEFAULT = ( - ('room', _("Room")), - ('access_point', _("Access point")), - ('uplink', _("Uplink")), - ('asso_machine', _("Organisation machine")), - ('nothing', _("Nothing")), + ("room", _("Room")), + ("access_point", _("Access point")), + ("uplink", _("Uplink")), + ("asso_machine", _("Organisation machine")), + ("nothing", _("Nothing")), ) - name = models.CharField(max_length=255, verbose_name=_("Name")) + name = models.CharField(max_length=255, verbose_name=_("name")) profil_default = models.CharField( max_length=32, choices=PROFIL_DEFAULT, blank=True, null=True, - unique=True, - verbose_name=_("Default profile") + verbose_name=_("default profile"), ) - vlan_untagged = models.ForeignKey( - 'machines.Vlan', - related_name='vlan_untagged', + on_dormitory = models.ForeignKey( + "topologie.Dormitory", + related_name="dormitory_ofprofil", on_delete=models.SET_NULL, blank=True, null=True, - verbose_name=_("VLAN untagged") + verbose_name=_("profile on dormitory"), + ) + vlan_untagged = models.ForeignKey( + "machines.Vlan", + related_name="vlan_untagged", + on_delete=models.SET_NULL, + blank=True, + null=True, + verbose_name=_("VLAN untagged"), ) vlan_tagged = models.ManyToManyField( - 'machines.Vlan', - related_name='vlan_tagged', + "machines.Vlan", + related_name="vlan_tagged", blank=True, - verbose_name=_("VLAN(s) tagged") + verbose_name=_("VLAN(s) tagged"), ) radius_type = models.CharField( max_length=32, choices=TYPES, - help_text=_("Type of RADIUS authentication : inactive, MAC-address or" - " 802.1X"), - verbose_name=_("RADIUS type") + help_text=_( + "Type of RADIUS authentication: inactive, MAC-address or 802.1X." + ), + verbose_name=_("RADIUS type"), ) radius_mode = models.CharField( max_length=32, choices=MODES, - default='COMMON', - help_text=_("In case of MAC-authentication : mode COMMON or STRICT on" - " this port"), - verbose_name=_("RADIUS mode") + default="COMMON", + help_text=_( + "In case of MAC-authentication: mode COMMON or STRICT on this port." + ), + verbose_name=_("RADIUS mode"), ) speed = models.CharField( - max_length=32, - choices=SPEED, - default='auto', - help_text=_("Port speed limit"), + max_length=32, choices=SPEED, default="auto", help_text=_("Port speed limit.") ) mac_limit = models.IntegerField( null=True, blank=True, - help_text=_("Limit of MAC-address on this port"), - verbose_name=_("MAC limit") - ) - flow_control = models.BooleanField( - default=False, - help_text=_("Flow control"), + help_text=_("Limit of MAC-address on this port."), + verbose_name=_("MAC limit"), ) + flow_control = models.BooleanField(default=False, help_text=_("Flow control.")) dhcp_snooping = models.BooleanField( default=False, - help_text=_("Protect against rogue DHCP"), - verbose_name=_("DHCP snooping") + help_text=_("Protect against rogue DHCP."), + verbose_name=_("DHCP snooping"), ) dhcpv6_snooping = models.BooleanField( default=False, - help_text=_("Protect against rogue DHCPv6"), - verbose_name=_("DHCPv6 snooping") + help_text=_("Protect against rogue DHCPv6."), + verbose_name=_("DHCPv6 snooping"), ) arp_protect = models.BooleanField( default=False, - help_text=_("Check if IP adress is DHCP assigned"), - verbose_name=_("ARP protection") + help_text=_("Check if IP address is DHCP assigned."), + verbose_name=_("ARP protection"), ) ra_guard = models.BooleanField( default=False, - help_text=_("Protect against rogue RA"), - verbose_name=_("RA guard") + help_text=_("Protect against rogue RA."), + verbose_name=_("RA guard"), ) loop_protect = models.BooleanField( default=False, - help_text=_("Protect against loop"), - verbose_name=_("Loop protection") + help_text=_("Protect against loop."), + verbose_name=_("loop protection"), ) class Meta: - permissions = ( - ("view_port_profile", _("Can view a port profile object")), - ) + permissions = (("view_port_profile", _("Can view a port profile object")),) verbose_name = _("port profile") verbose_name_plural = _("port profiles") + unique_together = ["on_dormitory", "profil_default"] security_parameters_fields = [ - 'loop_protect', - 'ra_guard', - 'arp_protect', - 'dhcpv6_snooping', - 'dhcp_snooping', - 'flow_control' + "loop_protect", + "ra_guard", + "arp_protect", + "dhcpv6_snooping", + "dhcp_snooping", + "flow_control", ] @cached_property @@ -852,7 +917,26 @@ class PortProfile(AclMixin, RevMixin, models.Model): @cached_property def security_parameters_as_str(self): - return ','.join(self.security_parameters_enabled) + return ",".join(self.security_parameters_enabled) + + def clean(self): + """ Check that there is only one generic profil default""" + super(PortProfile, self).clean() + if ( + self.profil_default + and not self.on_dormitory + and PortProfile.objects.exclude(id=self.id) + .filter(profil_default=self.profil_default) + .exclude(on_dormitory__isnull=False) + .exists() + ): + raise ValidationError( + { + "profil_default": _( + "A default profile for all dormitories of that type already exists." + ) + } + ) def __str__(self): return self.name @@ -861,14 +945,14 @@ class PortProfile(AclMixin, RevMixin, models.Model): @receiver(post_save, sender=AccessPoint) def ap_post_save(**_kwargs): """Regeneration des noms des bornes vers le controleur""" - regen('unifi-ap-names') + regen("unifi-ap-names") regen("graph_topo") @receiver(post_delete, sender=AccessPoint) def ap_post_delete(**_kwargs): """Regeneration des noms des bornes vers le controleur""" - regen('unifi-ap-names') + regen("unifi-ap-names") regen("graph_topo") @@ -916,4 +1000,3 @@ def switch_post_save(**_kwargs): @receiver(post_delete, sender=Switch) def switch_post_delete(**_kwargs): regen("graph_topo") - diff --git a/topologie/templates/topologie/aff_ap.html b/topologie/templates/topologie/aff_ap.html index 1acd54bb..afbf00ca 100644 --- a/topologie/templates/topologie/aff_ap.html +++ b/topologie/templates/topologie/aff_ap.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/topologie/templates/topologie/aff_building.html b/topologie/templates/topologie/aff_building.html index 9885f1c5..cf288b53 100644 --- a/topologie/templates/topologie/aff_building.html +++ b/topologie/templates/topologie/aff_building.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -35,13 +35,13 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Building" as tr_building %} {% include 'buttons/sort.html' with prefix='building' col='name' text=tr_building %} - {% trans "Access points" %} + {% trans "Access points" %} {% for building in building_list %} - {{ building.name }} + {{ building.cached_name }} {% for ap in building.all_ap_in %} {{ ap.short_name }} {% endfor %} {% can_edit building %} diff --git a/topologie/templates/topologie/aff_chambres.html b/topologie/templates/topologie/aff_chambres.html index 6e2b181f..d03f7b19 100644 --- a/topologie/templates/topologie/aff_chambres.html +++ b/topologie/templates/topologie/aff_chambres.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -34,6 +34,8 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Room" as tr_room %} + {% trans "Building" as tr_building %} + {% include 'buttons/sort.html' with prefix='building' col='name' text=tr_building %} {% include 'buttons/sort.html' with prefix='room' col='name' text=tr_room %} {% trans "Details" %} @@ -41,6 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% for room in room_list %} + {{ room.building }} {{ room.name }} {{ room.details }} diff --git a/topologie/templates/topologie/aff_constructor_switch.html b/topologie/templates/topologie/aff_constructor_switch.html index eceeb682..9e4dcb42 100644 --- a/topologie/templates/topologie/aff_constructor_switch.html +++ b/topologie/templates/topologie/aff_constructor_switch.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/topologie/templates/topologie/aff_dormitory.html b/topologie/templates/topologie/aff_dormitory.html new file mode 100644 index 00000000..b4311b8a --- /dev/null +++ b/topologie/templates/topologie/aff_dormitory.html @@ -0,0 +1,62 @@ +{% 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 Lara 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 %} +{% load logs_extra %} +{% load i18n %} + +{% if dormitory_list.paginator %} + {% include 'pagination.html' with list=dormitory_list %} +{% endif %} + + + + + {% trans "Dormitory" as tr_dormitory %} + + + + + + {% for dormitory in dormitory_list %} + + + + + + {% endfor %} +
    {% include 'buttons/sort.html' with prefix='dormitory' col='name' text=tr_dormitory %}{% trans "Buildings" %}
    {{ dormitory.name }}{% for building in dormitory.building_set.all %} {{ building.name }} {% endfor %} + {% can_edit dormitory %} + {% include 'buttons/edit.html' with href='topologie:edit-dormitory' id=dormitory.id %} + {% acl_end %} + {% history_button dormitory %} + {% can_delete dormitory %} + {% include 'buttons/suppr.html' with href='topologie:del-dormitory' id=dormitory.id %} + {% acl_end %} +
    + +{% if dormitory_list.paginator %} + {% include 'pagination.html' with list=dormitory_list %} +{% endif %} + diff --git a/topologie/templates/topologie/aff_model_switch.html b/topologie/templates/topologie/aff_model_switch.html index 426b308a..51f98617 100644 --- a/topologie/templates/topologie/aff_model_switch.html +++ b/topologie/templates/topologie/aff_model_switch.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -36,7 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Reference" as tr_ref %} {% include 'buttons/sort.html' with prefix='model-switch' col='reference' text=tr_ref %} {% trans "Commercial name" %} - Firmware + {% trans "Firmware" %} {% trans "Switch constructor" as tr_constructor %} {% include 'buttons/sort.html' with prefix='model-switch' col='constructor' text=tr_constructor %} {% trans "Switches" %} diff --git a/topologie/templates/topologie/aff_modules.html b/topologie/templates/topologie/aff_modules.html index 021457f2..ac09ec13 100644 --- a/topologie/templates/topologie/aff_modules.html +++ b/topologie/templates/topologie/aff_modules.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/topologie/templates/topologie/aff_port.html b/topologie/templates/topologie/aff_port.html index 6a02b484..91742147 100644 --- a/topologie/templates/topologie/aff_port.html +++ b/topologie/templates/topologie/aff_port.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -88,7 +88,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endif %} - {% if not port.custom_profil %} + {% if not port.custom_profile %} {% trans "Default: " %} {% endif %} {{ port.get_port_profile }} diff --git a/topologie/templates/topologie/aff_port_profile.html b/topologie/templates/topologie/aff_port_profile.html index b335904e..009c0b0f 100644 --- a/topologie/templates/topologie/aff_port_profile.html +++ b/topologie/templates/topologie/aff_port_profile.html @@ -47,7 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% for port_profile in port_profile_list %} {{ port_profile.name }} - {{ port_profile.profil_default }} + {{ port_profile.profil_default }} {% if port_profile.profil_default%} - {% if port_profile.on_dormitory %}{% blocktrans with dorm=port_profile.on_dormitory %} on {{ dorm }}{% endblocktrans %}{% else %}{% trans "Everywhere" %}{% endif %}{% endif %} {% if port_profile.vlan_untagged %} {% trans "Untagged: " %}{{ port_profile.vlan_untagged }} diff --git a/topologie/templates/topologie/aff_repr_switch.html b/topologie/templates/topologie/aff_repr_switch.html index c482da7a..f0924124 100644 --- a/topologie/templates/topologie/aff_repr_switch.html +++ b/topologie/templates/topologie/aff_repr_switch.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/topologie/templates/topologie/aff_stacks.html b/topologie/templates/topologie/aff_stacks.html index c1032ee6..1ca259eb 100644 --- a/topologie/templates/topologie/aff_stacks.html +++ b/topologie/templates/topologie/aff_stacks.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/topologie/templates/topologie/aff_switch.html b/topologie/templates/topologie/aff_switch.html index 93c05530..e2866aba 100644 --- a/topologie/templates/topologie/aff_switch.html +++ b/topologie/templates/topologie/aff_switch.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -44,7 +44,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% include 'buttons/sort.html' with prefix='switch' col='ports' text=tr_ports %} {% trans "Stack" as tr_stack %} {% include 'buttons/sort.html' with prefix='switch' col='stack' text=tr_stack %} - {% trans "Stack ID" %} + {% trans "Stack member ID" %} {% trans "Switch model" %} {% trans "Details" %} diff --git a/topologie/templates/topologie/aff_switch_bay.html b/topologie/templates/topologie/aff_switch_bay.html index 0aa81449..9335f0a8 100644 --- a/topologie/templates/topologie/aff_switch_bay.html +++ b/topologie/templates/topologie/aff_switch_bay.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/topologie/templates/topologie/aff_vlanoptions.html b/topologie/templates/topologie/aff_vlanoptions.html index 9b2dc913..65c80e34 100644 --- a/topologie/templates/topologie/aff_vlanoptions.html +++ b/topologie/templates/topologie/aff_vlanoptions.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/topologie/templates/topologie/delete.html b/topologie/templates/topologie/delete.html index 24e6edad..e5dea3c4 100644 --- a/topologie/templates/topologie/delete.html +++ b/topologie/templates/topologie/delete.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/topologie/templates/topologie/edit_stack_sw.html b/topologie/templates/topologie/edit_stack_sw.html index bf094f07..6e1cf848 100644 --- a/topologie/templates/topologie/edit_stack_sw.html +++ b/topologie/templates/topologie/edit_stack_sw.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/topologie/templates/topologie/index.html b/topologie/templates/topologie/index.html index be3667ca..4d021779 100644 --- a/topologie/templates/topologie/index.html +++ b/topologie/templates/topologie/index.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -65,7 +65,7 @@ function toggle_graph() {

    {% trans "Switches" %}

    {% can_create Switch %} -{% trans " Add a switch" %} + {% trans "Add a switch" %}
    {% acl_end %} {% include 'topologie/aff_switch.html' with switch_list=switch_list %} diff --git a/topologie/templates/topologie/index_ap.html b/topologie/templates/topologie/index_ap.html index 5fbd3d94..b1521937 100644 --- a/topologie/templates/topologie/index_ap.html +++ b/topologie/templates/topologie/index_ap.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block content %}

    {% trans "Access points" %}

    {% can_create AccessPoint %} -{% trans " Add an access point" %} + {% trans "Add an access point" %}
    {% acl_end %} {% include 'topologie/aff_ap.html' with ap_list=ap_list %} diff --git a/topologie/templates/topologie/index_model_switch.html b/topologie/templates/topologie/index_model_switch.html index e7ad725f..da82adb7 100644 --- a/topologie/templates/topologie/index_model_switch.html +++ b/topologie/templates/topologie/index_model_switch.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    {% trans "Switch models" %}

    {% can_create ModelSwitch %} - {% trans " Add a switch model" %} + {% trans "Add a switch model" %}
    {% acl_end %} @@ -42,7 +42,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    {% trans "Switch constructors" %}

    {% can_create ConstructorSwitch %} - {% trans " Add a switch constructor" %} + {% trans "Add a switch constructor" %}
    {% acl_end %} diff --git a/topologie/templates/topologie/index_module.html b/topologie/templates/topologie/index_module.html index a7fee190..fae9b282 100644 --- a/topologie/templates/topologie/index_module.html +++ b/topologie/templates/topologie/index_module.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block content %}

    {% trans "Switch modules" %}

    {% can_create ModuleSwitch %} -{% trans " Add a module" %} + {% trans "Add a module" %}
    {% acl_end %} {% include 'topologie/aff_modules.html' with module_list=module_list modular_switchs=modular_switchs %} diff --git a/topologie/templates/topologie/index_p.html b/topologie/templates/topologie/index_p.html index 04a516ec..462bd146 100644 --- a/topologie/templates/topologie/index_p.html +++ b/topologie/templates/topologie/index_p.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -48,8 +48,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    {% include 'buttons/edit.html' with href='topologie:edit-switch' id=id_switch %} {% can_create Port %} -{% include 'buttons/add.html' with href='topologie:new-port' id=id_switch %} -{% include 'buttons/add.html' with href='topologie:create-ports' id=id_switch %} + {% trans "Add a port" %} + {% trans "Add ports to the port list" %} {% acl_end %}
    {% include 'topologie/aff_repr_switch.html' with port_list=port_list %} diff --git a/topologie/templates/topologie/index_physical_grouping.html b/topologie/templates/topologie/index_physical_grouping.html index d2defa0c..cea4fb75 100644 --- a/topologie/templates/topologie/index_physical_grouping.html +++ b/topologie/templates/topologie/index_physical_grouping.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    {% trans "Stacks" %}

    {% can_create Stack %} - {% trans " Add a stack" %} + {% trans "Add a stack" %} {% acl_end %} {% include 'topologie/aff_stacks.html' with stack_list=stack_list %} @@ -41,7 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    {% trans "Switch bays" %}

    {% can_create SwitchBay %} - {% trans " Add a switch bay" %} + {% trans "Add a switch bay" %}
    {% acl_end %} @@ -50,10 +50,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    {% trans "Buildings" %}

    {% can_create Building %} - {% trans " Add a building" %} + {% trans "Add a building" %}
    {% acl_end %} {% include 'topologie/aff_building.html' with building_list=building_list %} + + +

    {% trans "Dormitories" %}

    + {% can_create Dormitory %} + + {% trans "Add a dormitory" %} + +
    + {% acl_end %} + {% include 'topologie/aff_dormitory.html' with dormitory_list=dormitory_list %} {% endblock %} diff --git a/topologie/templates/topologie/index_portprofile.html b/topologie/templates/topologie/index_portprofile.html index a528b992..dd04687d 100644 --- a/topologie/templates/topologie/index_portprofile.html +++ b/topologie/templates/topologie/index_portprofile.html @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    {% trans "Port profiles" %}

    {% can_create PortProfile %} -{% trans " Add a port profile" %} + {% trans "Add a port profile" %}
    {% acl_end %} {% include 'topologie/aff_port_profile.html' with port_profile_list=port_profile_list %} diff --git a/topologie/templates/topologie/index_room.html b/topologie/templates/topologie/index_room.html index 248ce05d..17658e0c 100644 --- a/topologie/templates/topologie/index_room.html +++ b/topologie/templates/topologie/index_room.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block content %}

    {% trans "Rooms" %}

    {% can_create Room %} -{% trans " Add a room" %} + {% trans "Add a room" %}
    {% acl_end %} {% include 'topologie/aff_chambres.html' with room_list=room_list %} diff --git a/topologie/templates/topologie/sidebar.html b/topologie/templates/topologie/sidebar.html index 249c4308..f90e5d55 100644 --- a/topologie/templates/topologie/sidebar.html +++ b/topologie/templates/topologie/sidebar.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/topologie/templates/topologie/switch.html b/topologie/templates/topologie/switch.html index 5bb738f6..bdc0dae0 100644 --- a/topologie/templates/topologie/switch.html +++ b/topologie/templates/topologie/switch.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -36,15 +36,15 @@ with this program; if not, write to the Free Software Foundation, Inc., -{% bootstrap_icon "list" %}{% trans " Go to the ports list" %} +{% bootstrap_icon "list" %} {% trans "Go to the ports list" %}
    {% csrf_token %} {% if topoform %}

    {% trans "Specific settings for the switch" %}

    {% massive_bootstrap_form topoform 'switch_interface' %} {% endif %} - {% trans "Create" as tr_create %} - {% bootstrap_button tr_create button_type="submit" icon='ok' button_class='btn-success' %} + {% trans "Confirm" as tr_confirm %} + {% bootstrap_button tr_confirm button_type="submit" icon='ok' button_class='btn-success' %}


    diff --git a/topologie/templates/topologie/topo.html b/topologie/templates/topologie/topo.html index 2f6449e5..409b65ec 100644 --- a/topologie/templates/topologie/topo.html +++ b/topologie/templates/topologie/topo.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% bootstrap_form_errors topoform %} {% if id_switch %} -{% bootstrap_icon "list" %}{% trans " Go to the ports list" %} +{% bootstrap_icon "list" %} {% trans "Go to the ports list" %} {% endif %}
    {% csrf_token %} diff --git a/topologie/templates/topologie/topo_more.html b/topologie/templates/topologie/topo_more.html index 89f6404d..617870d7 100644 --- a/topologie/templates/topologie/topo_more.html +++ b/topologie/templates/topologie/topo_more.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -56,8 +56,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    {% trans "DNS name" %}

    {% bootstrap_form domainform %} {% endif %} - {% trans "Create or edit" as tr_create_or_edit %} - {% bootstrap_button tr_create_or_edit button_type="submit" icon='ok' button_class='btn-success' %} + {% trans "Confirm" as tr_confirm %} + {% bootstrap_button tr_confirm button_type="submit" icon='ok' button_class='btn-success' %}


    diff --git a/topologie/tests.py b/topologie/tests.py index dfe72a14..42d5ddfd 100644 --- a/topologie/tests.py +++ b/topologie/tests.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify diff --git a/topologie/urls.py b/topologie/urls.py index 70eae8e4..c204cb39 100644 --- a/topologie/urls.py +++ b/topologie/urls.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -33,105 +33,128 @@ from django.conf.urls import url from . import views urlpatterns = [ - url(r'^$', views.index, name='index'), - url(r'^index_ap/$', views.index_ap, name='index-ap'), - url(r'^new_ap/$', views.new_ap, name='new-ap'), - url(r'^edit_ap/(?P[0-9]+)$', - views.edit_ap, - name='edit-ap'), - url(r'^create_ports/(?P[0-9]+)$', - views.create_ports, - name='create-ports'), - url(r'^index_room/$', views.index_room, name='index-room'), - url(r'^new_room/$', views.new_room, name='new-room'), - url(r'^edit_room/(?P[0-9]+)$', views.edit_room, name='edit-room'), - url(r'^del_room/(?P[0-9]+)$', views.del_room, name='del-room'), - url(r'^new_switch/$', views.new_switch, name='new-switch'), - url(r'^switch/(?P[0-9]+)$', - views.index_port, - name='index-port'), - url(r'^edit_port/(?P[0-9]+)$', views.edit_port, name='edit-port'), - url(r'^new_port/(?P[0-9]+)$', views.new_port, name='new-port'), - url(r'^del_port/(?P[0-9]+)$', views.del_port, name='del-port'), - url(r'^edit_switch/(?P[0-9]+)$', - views.edit_switch, - name='edit-switch'), - url(r'^new_stack/$', views.new_stack, name='new-stack'), - url(r'^index_physical_grouping/$', + url(r"^$", views.index, name="index"), + url(r"^index_ap/$", views.index_ap, name="index-ap"), + url(r"^new_ap/$", views.new_ap, name="new-ap"), + url(r"^edit_ap/(?P[0-9]+)$", views.edit_ap, name="edit-ap"), + url( + r"^create_ports/(?P[0-9]+)$", views.create_ports, name="create-ports" + ), + url(r"^index_room/$", views.index_room, name="index-room"), + url(r"^new_room/$", views.new_room, name="new-room"), + url(r"^edit_room/(?P[0-9]+)$", views.edit_room, name="edit-room"), + url(r"^del_room/(?P[0-9]+)$", views.del_room, name="del-room"), + url(r"^new_switch/$", views.new_switch, name="new-switch"), + url(r"^switch/(?P[0-9]+)$", views.index_port, name="index-port"), + url(r"^edit_port/(?P[0-9]+)$", views.edit_port, name="edit-port"), + url(r"^new_port/(?P[0-9]+)$", views.new_port, name="new-port"), + url(r"^del_port/(?P[0-9]+)$", views.del_port, name="del-port"), + url(r"^edit_switch/(?P[0-9]+)$", views.edit_switch, name="edit-switch"), + url(r"^new_stack/$", views.new_stack, name="new-stack"), + url( + r"^index_physical_grouping/$", views.index_physical_grouping, - name='index-physical-grouping'), - url(r'^edit_stack/(?P[0-9]+)$', - views.edit_stack, - name='edit-stack'), - url(r'^del_stack/(?P[0-9]+)$', - views.del_stack, - name='del-stack'), - url(r'^index_model_switch/$', - views.index_model_switch, - name='index-model-switch'), - url(r'^index_model_switch/$', - views.index_model_switch, - name='index-model-switch'), - url(r'^new_model_switch/$', - views.new_model_switch, - name='new-model-switch'), - url(r'^edit_model_switch/(?P[0-9]+)$', + name="index-physical-grouping", + ), + url(r"^edit_stack/(?P[0-9]+)$", views.edit_stack, name="edit-stack"), + url(r"^del_stack/(?P[0-9]+)$", views.del_stack, name="del-stack"), + url(r"^index_model_switch/$", views.index_model_switch, name="index-model-switch"), + url(r"^index_model_switch/$", views.index_model_switch, name="index-model-switch"), + url(r"^new_model_switch/$", views.new_model_switch, name="new-model-switch"), + url( + r"^edit_model_switch/(?P[0-9]+)$", views.edit_model_switch, - name='edit-model-switch'), - url(r'^del_model_switch/(?P[0-9]+)$', + name="edit-model-switch", + ), + url( + r"^del_model_switch/(?P[0-9]+)$", views.del_model_switch, - name='del-model-switch'), - url(r'^new_constructor_switch/$', + name="del-model-switch", + ), + url( + r"^new_constructor_switch/$", views.new_constructor_switch, - name='new-constructor-switch'), - url(r'^edit_constructor_switch/(?P[0-9]+)$', + name="new-constructor-switch", + ), + url( + r"^edit_constructor_switch/(?P[0-9]+)$", views.edit_constructor_switch, - name='edit-constructor-switch'), - url(r'^del_constructor_switch/(?P[0-9]+)$', + name="edit-constructor-switch", + ), + url( + r"^del_constructor_switch/(?P[0-9]+)$", views.del_constructor_switch, - name='del-constructor-switch'), - url(r'^new_switch_bay/$', - views.new_switch_bay, - name='new-switch-bay'), - url(r'^edit_switch_bay/(?P[0-9]+)$', + name="del-constructor-switch", + ), + url(r"^new_switch_bay/$", views.new_switch_bay, name="new-switch-bay"), + url( + r"^edit_switch_bay/(?P[0-9]+)$", views.edit_switch_bay, - name='edit-switch-bay'), - url(r'^del_switch_bay/(?P[0-9]+)$', + name="edit-switch-bay", + ), + url( + r"^del_switch_bay/(?P[0-9]+)$", views.del_switch_bay, - name='del-switch-bay'), - url(r'^new_building/$', - views.new_building, - name='new-building'), - url(r'^edit_building/(?P[0-9]+)$', + name="del-switch-bay", + ), + url(r"^new_building/$", views.new_building, name="new-building"), + url( + r"^edit_building/(?P[0-9]+)$", views.edit_building, - name='edit-building'), - url(r'^del_building/(?P[0-9]+)$', + name="edit-building", + ), + url( + r"^del_building/(?P[0-9]+)$", views.del_building, - name='del-building'), - url(r'^index_port_profile/$', - views.index_port_profile, - name='index-port-profile'), - url(r'^new_port_profile/$', - views.new_port_profile, - name='new-port-profile'), - url(r'^edit_port_profile/(?P[0-9]+)$', + name="del-building", + ), + url(r"^new_dormitory/$", views.new_dormitory, name="new-dormitory"), + url( + r"^edit_dormitory/(?P[0-9]+)$", + views.edit_dormitory, + name="edit-dormitory", + ), + url( + r"^del_dormitory/(?P[0-9]+)$", + views.del_dormitory, + name="del-dormitory", + ), + url(r"^index_port_profile/$", views.index_port_profile, name="index-port-profile"), + url(r"^new_port_profile/$", views.new_port_profile, name="new-port-profile"), + url( + r"^edit_port_profile/(?P[0-9]+)$", views.edit_port_profile, - name='edit-port-profile'), - url(r'^del_port_profile/(?P[0-9]+)$', + name="edit-port-profile", + ), + url( + r"^del_port_profile/(?P[0-9]+)$", views.del_port_profile, - name='del-port-profile'), - url(r'^edit_vlanoptions/(?P[0-9]+)$', + name="del-port-profile", + ), + url( + r"^edit_vlanoptions/(?P[0-9]+)$", views.edit_vlanoptions, - name='edit-vlanoptions'), - url(r'^add_module/$', views.add_module, name='add-module'), - url(r'^edit_module/(?P[0-9]+)$', + name="edit-vlanoptions", + ), + url(r"^add_module/$", views.add_module, name="add-module"), + url( + r"^edit_module/(?P[0-9]+)$", views.edit_module, - name='edit-module'), - url(r'^del_module/(?P[0-9]+)$', views.del_module, name='del-module'), - url(r'^index_module/$', views.index_module, name='index-module'), - url(r'^add_module_on/$', views.add_module_on, name='add-module-on'), - url(r'^edit_module_on/(?P[0-9]+)$', + name="edit-module", + ), + url( + r"^del_module/(?P[0-9]+)$", views.del_module, name="del-module" + ), + url(r"^index_module/$", views.index_module, name="index-module"), + url(r"^add_module_on/$", views.add_module_on, name="add-module-on"), + url( + r"^edit_module_on/(?P[0-9]+)$", views.edit_module_on, - name='edit-module-on'), - url(r'^del_module_on/(?P[0-9]+)$', views.del_module_on, name='del-module-on'), + name="edit-module-on", + ), + url( + r"^del_module_on/(?P[0-9]+)$", + views.del_module_on, + name="del-module-on", + ), ] diff --git a/topologie/views.py b/topologie/views.py index 2eeb3d55..3d3f742a 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -48,30 +48,17 @@ from django.utils.translation import ugettext as _ import tempfile from users.views import form -from re2o.base import ( - re2o_paginator, - SortTable, -) -from re2o.acl import ( - can_create, - can_edit, - can_delete, - can_view, - can_view_all, -) +from re2o.base import re2o_paginator, SortTable +from re2o.acl import can_create, can_edit, can_delete, can_view, can_view_all from re2o.settings import MEDIA_ROOT from machines.forms import ( DomainForm, EditInterfaceForm, AddInterfaceForm, - EditOptionVlanForm + EditOptionVlanForm, ) from machines.views import generate_ipv4_mbf_param -from machines.models import ( - Interface, - Service_link, - Vlan -) +from machines.models import Interface, Service_link, Vlan from preferences.models import AssoOption, GeneralOption from .models import ( @@ -84,6 +71,7 @@ from .models import ( AccessPoint, SwitchBay, Building, + Dormitory, Server, PortProfile, ModuleSwitch, @@ -103,15 +91,13 @@ from .forms import ( EditAccessPointForm, EditSwitchBayForm, EditBuildingForm, + EditDormitoryForm, EditPortProfileForm, EditModuleForm, EditSwitchModuleForm, ) -from subprocess import ( - Popen, - PIPE -) +from subprocess import Popen, PIPE from os.path import isfile @@ -120,56 +106,64 @@ from os.path import isfile @can_view_all(Switch) def index(request): """ Vue d'affichage de tous les swicthes""" - switch_list = (Switch.objects - .prefetch_related(Prefetch( - 'interface_set', - queryset=(Interface.objects - .select_related('ipv4__ip_type__extension') - .select_related('domain__extension')) - )) - .select_related('stack')) + switch_list = ( + Switch.objects.prefetch_related( + Prefetch( + "interface_set", + queryset=( + Interface.objects.select_related( + "ipv4__ip_type__extension" + ).select_related("domain__extension") + ), + ) + ) + .select_related("stack") + .select_related("switchbay__building__dormitory") + .select_related("model__constructor") + ) switch_list = SortTable.sort( switch_list, - request.GET.get('col'), - request.GET.get('order'), - SortTable.TOPOLOGIE_INDEX + request.GET.get("col"), + request.GET.get("order"), + SortTable.TOPOLOGIE_INDEX, ) - pagination_number = GeneralOption.get_cached_value('pagination_number') + pagination_number = GeneralOption.get_cached_value("pagination_number") switch_list = re2o_paginator(request, switch_list, pagination_number) if any( service_link.need_regen for service_link in Service_link.objects.filter( - service__service_type='graph_topo') + service__service_type="graph_topo" + ) ): make_machine_graph() for service_link in Service_link.objects.filter( - service__service_type='graph_topo'): + service__service_type="graph_topo" + ): service_link.done_regen() if not isfile("/var/www/re2o/media/images/switchs.png"): make_machine_graph() - return render( - request, - 'topologie/index.html', - {'switch_list': switch_list} - ) + return render(request, "topologie/index.html", {"switch_list": switch_list}) @login_required @can_view_all(PortProfile) def index_port_profile(request): - pagination_number = GeneralOption.get_cached_value('pagination_number') - port_profile_list = PortProfile.objects.all().select_related( - 'vlan_untagged') - port_profile_list = re2o_paginator( - request, port_profile_list, pagination_number) - vlan_list = Vlan.objects.all().order_by('vlan_id') + pagination_number = GeneralOption.get_cached_value("pagination_number") + port_profile_list = ( + PortProfile.objects.all() + .select_related("vlan_untagged") + .select_related("on_dormitory") + .prefetch_related("vlan_tagged") + ) + port_profile_list = re2o_paginator(request, port_profile_list, pagination_number) + vlan_list = Vlan.objects.all().order_by("vlan_id") return render( request, - 'topologie/index_portprofile.html', - {'port_profile_list': port_profile_list, 'vlan_list': vlan_list} + "topologie/index_portprofile.html", + {"port_profile_list": port_profile_list, "vlan_list": vlan_list}, ) @@ -178,32 +172,32 @@ def index_port_profile(request): @can_view(Switch) def index_port(request, switch, switchid): """ Affichage de l'ensemble des ports reliés à un switch particulier""" - port_list = (Port.objects - .filter(switch=switch) - .select_related('room') - .select_related('machine_interface__domain__extension') - .select_related('machine_interface__machine__user') - .select_related('related__switch') - .prefetch_related(Prefetch( - 'related__switch__interface_set', - queryset=(Interface.objects - .select_related('domain__extension')) - )) - .select_related('switch')) + port_list = ( + Port.objects.filter(switch=switch) + .select_related("room__building__dormitory") + .select_related("machine_interface__domain__extension") + .select_related("machine_interface__machine__user") + .select_related("machine_interface__machine__accesspoint") + .select_related("related__switch__switchbay__building__dormitory") + .prefetch_related( + Prefetch( + "related__switch__interface_set", + queryset=(Interface.objects.select_related("domain__extension")), + ) + ) + .select_related("switch__switchbay__building__dormitory") + .select_related("switch__model__constructor") + ) port_list = SortTable.sort( port_list, - request.GET.get('col'), - request.GET.get('order'), - SortTable.TOPOLOGIE_INDEX_PORT + request.GET.get("col"), + request.GET.get("order"), + SortTable.TOPOLOGIE_INDEX_PORT, ) return render( request, - 'topologie/index_p.html', - { - 'port_list': port_list, - 'id_switch': switchid, - 'switch': switch - } + "topologie/index_p.html", + {"port_list": port_list, "id_switch": switchid, "switch": switch}, ) @@ -211,84 +205,88 @@ def index_port(request, switch, switchid): @can_view_all(Room) def index_room(request): """ Affichage de l'ensemble des chambres""" - room_list = Room.objects + room_list = Room.objects.select_related("building__dormitory") room_list = SortTable.sort( room_list, - request.GET.get('col'), - request.GET.get('order'), - SortTable.TOPOLOGIE_INDEX_ROOM + request.GET.get("col"), + request.GET.get("order"), + SortTable.TOPOLOGIE_INDEX_ROOM, ) - pagination_number = GeneralOption.get_cached_value('pagination_number') + pagination_number = GeneralOption.get_cached_value("pagination_number") room_list = re2o_paginator(request, room_list, pagination_number) - return render( - request, - 'topologie/index_room.html', - {'room_list': room_list} - ) + return render(request, "topologie/index_room.html", {"room_list": room_list}) @login_required @can_view_all(AccessPoint) def index_ap(request): """ Affichage de l'ensemble des bornes""" - ap_list = (AccessPoint.objects - .prefetch_related(Prefetch( - 'interface_set', - queryset=(Interface.objects - .select_related('ipv4__ip_type__extension') - .select_related('domain__extension')) - )).distinct()) + ap_list = AccessPoint.objects.prefetch_related( + Prefetch( + "interface_set", + queryset=( + Interface.objects.select_related( + "ipv4__ip_type__extension" + ).select_related("domain__extension") + ), + ) + ).distinct() ap_list = SortTable.sort( ap_list, - request.GET.get('col'), - request.GET.get('order'), - SortTable.TOPOLOGIE_INDEX_BORNE + request.GET.get("col"), + request.GET.get("order"), + SortTable.TOPOLOGIE_INDEX_BORNE, ) - pagination_number = GeneralOption.get_cached_value('pagination_number') + pagination_number = GeneralOption.get_cached_value("pagination_number") ap_list = re2o_paginator(request, ap_list, pagination_number) - return render( - request, - 'topologie/index_ap.html', - {'ap_list': ap_list} - ) + return render(request, "topologie/index_ap.html", {"ap_list": ap_list}) @login_required -@can_view_all(Stack, Building, SwitchBay) +@can_view_all(Stack, Building, Dormitory, SwitchBay) def index_physical_grouping(request): """Affichage de la liste des stacks (affiche l'ensemble des switches)""" - stack_list = (Stack.objects - .prefetch_related( - 'switch_set__interface_set__domain__extension' - )) - building_list = Building.objects.all() - switch_bay_list = SwitchBay.objects.select_related('building') + stack_list = Stack.objects.prefetch_related( + "switch_set__interface_set__domain__extension" + ) + building_list = Building.objects.all().select_related("dormitory") + dormitory_list = Dormitory.objects.all().prefetch_related("building_set") + switch_bay_list = SwitchBay.objects.select_related( + "building__dormitory" + ).prefetch_related("switch_set__interface_set__domain") stack_list = SortTable.sort( stack_list, - request.GET.get('col'), - request.GET.get('order'), - SortTable.TOPOLOGIE_INDEX_STACK + request.GET.get("col"), + request.GET.get("order"), + SortTable.TOPOLOGIE_INDEX_STACK, ) building_list = SortTable.sort( building_list, - request.GET.get('col'), - request.GET.get('order'), - SortTable.TOPOLOGIE_INDEX_BUILDING + request.GET.get("col"), + request.GET.get("order"), + SortTable.TOPOLOGIE_INDEX_BUILDING, + ) + dormitory_list = SortTable.sort( + dormitory_list, + request.GET.get("col"), + request.GET.get("order"), + SortTable.TOPOLOGIE_INDEX_DORMITORY, ) switch_bay_list = SortTable.sort( switch_bay_list, - request.GET.get('col'), - request.GET.get('order'), - SortTable.TOPOLOGIE_INDEX_SWITCH_BAY + request.GET.get("col"), + request.GET.get("order"), + SortTable.TOPOLOGIE_INDEX_SWITCH_BAY, ) return render( request, - 'topologie/index_physical_grouping.html', + "topologie/index_physical_grouping.html", { - 'stack_list': stack_list, - 'switch_bay_list': switch_bay_list, - 'building_list': building_list, - } + "stack_list": stack_list, + "switch_bay_list": switch_bay_list, + "building_list": building_list, + "dormitory_list": dormitory_list, + }, ) @@ -296,27 +294,29 @@ def index_physical_grouping(request): @can_view_all(ModelSwitch, ConstructorSwitch) def index_model_switch(request): """ Affichage de l'ensemble des modèles de switches""" - model_switch_list = ModelSwitch.objects.select_related('constructor') + model_switch_list = ModelSwitch.objects.select_related( + "constructor" + ).prefetch_related("switch_set__interface_set__domain") constructor_switch_list = ConstructorSwitch.objects model_switch_list = SortTable.sort( model_switch_list, - request.GET.get('col'), - request.GET.get('order'), - SortTable.TOPOLOGIE_INDEX_MODEL_SWITCH + request.GET.get("col"), + request.GET.get("order"), + SortTable.TOPOLOGIE_INDEX_MODEL_SWITCH, ) constructor_switch_list = SortTable.sort( constructor_switch_list, - request.GET.get('col'), - request.GET.get('order'), - SortTable.TOPOLOGIE_INDEX_CONSTRUCTOR_SWITCH + request.GET.get("col"), + request.GET.get("order"), + SortTable.TOPOLOGIE_INDEX_CONSTRUCTOR_SWITCH, ) return render( request, - 'topologie/index_model_switch.html', + "topologie/index_model_switch.html", { - 'model_switch_list': model_switch_list, - 'constructor_switch_list': constructor_switch_list, - } + "model_switch_list": model_switch_list, + "constructor_switch_list": constructor_switch_list, + }, ) @@ -325,14 +325,17 @@ def index_model_switch(request): def index_module(request): """Display all modules of switchs""" module_list = ModuleSwitch.objects.all() - modular_switchs = Switch.objects.filter(model__is_modular=True) - pagination_number = GeneralOption.get_cached_value('pagination_number') + modular_switchs = ( + Switch.objects.filter(model__is_modular=True) + .select_related("model") + .prefetch_related("moduleonswitch_set__module") + ) + pagination_number = GeneralOption.get_cached_value("pagination_number") module_list = re2o_paginator(request, module_list, pagination_number) return render( request, - 'topologie/index_module.html', - {'module_list': module_list, - 'modular_switchs': modular_switchs} + "topologie/index_module.html", + {"module_list": module_list, "modular_switchs": modular_switchs}, ) @@ -345,11 +348,9 @@ def edit_vlanoptions(request, vlan_instance, **_kwargs): if vlan.changed_data: vlan.save() messages.success(request, _("The VLAN was edited.")) - return redirect(reverse('topologie:index-port-profile')) + return redirect(reverse("topologie:index-port-profile")) return form( - {'vlanform': vlan, 'action_name': _("Edit")}, - 'machines/machine.html', - request + {"vlanform": vlan, "action_name": _("Edit")}, "machines/machine.html", request ) @@ -361,7 +362,7 @@ def new_port(request, switchid): switch = Switch.objects.get(pk=switchid) except Switch.DoesNotExist: messages.error(request, _("Nonexistent switch.")) - return redirect(reverse('topologie:index')) + return redirect(reverse("topologie:index")) port = AddPortForm(request.POST or None) if port.is_valid(): port = port.save(commit=False) @@ -371,14 +372,12 @@ def new_port(request, switchid): messages.success(request, _("The port was added.")) except IntegrityError: messages.error(request, _("The port already exists.")) - return redirect(reverse( - 'topologie:index-port', - kwargs={'switchid': switchid} - )) + return redirect(reverse("topologie:index-port", kwargs={"switchid": switchid})) return form( - {'id_switch': switchid, 'topoform': port, 'action_name': _("Add")}, - 'topologie/topo.html', - request) + {"id_switch": switchid, "topoform": port, "action_name": _("Add")}, + "topologie/topo.html", + request, + ) @login_required @@ -392,18 +391,19 @@ def edit_port(request, port_object, **_kwargs): if port.changed_data: port.save() messages.success(request, _("The port was edited.")) - return redirect(reverse( - 'topologie:index-port', - kwargs={'switchid': str(port_object.switch.id)} - )) + return redirect( + reverse( + "topologie:index-port", kwargs={"switchid": str(port_object.switch.id)} + ) + ) return form( { - 'id_switch': str(port_object.switch.id), - 'topoform': port, - 'action_name': _("Edit") + "id_switch": str(port_object.switch.id), + "topoform": port, + "action_name": _("Edit"), }, - 'topologie/topo.html', - request + "topologie/topo.html", + request, ) @@ -418,14 +418,18 @@ def del_port(request, port, **_kwargs): except ProtectedError: messages.error( request, - (_("The port %s is used by another object, impossible to" - " delete it.") % port) + ( + _( + "The port %s is used by another object, impossible to" + " delete it." + ) + % port + ), ) - return redirect(reverse( - 'topologie:index-port', - kwargs={'switchid': str(port.switch.id)} - )) - return form({'objet': port}, 'topologie/delete.html', request) + return redirect( + reverse("topologie:index-port", kwargs={"switchid": str(port.switch.id)}) + ) + return form({"objet": port}, "topologie/delete.html", request) @login_required @@ -436,11 +440,9 @@ def new_stack(request): if stack.is_valid(): stack.save() messages.success(request, _("The stack was created.")) - return redirect(reverse('topologie:index-physical-grouping')) + return redirect(reverse("topologie:index-physical-grouping")) return form( - {'topoform': stack, 'action_name': _("Create")}, - 'topologie/topo.html', - request + {"topoform": stack, "action_name": _("Add")}, "topologie/topo.html", request ) @@ -452,11 +454,10 @@ def edit_stack(request, stack, **_kwargs): if stack.is_valid(): if stack.changed_data: stack.save() - return redirect(reverse('topologie:index-physical-grouping')) + messages.success(request, _("The stack was edited.")) + return redirect(reverse("topologie:index-physical-grouping")) return form( - {'topoform': stack, 'action_name': _("Edit")}, - 'topologie/topo.html', - request + {"topoform": stack, "action_name": _("Edit")}, "topologie/topo.html", request ) @@ -471,11 +472,16 @@ def del_stack(request, stack, **_kwargs): except ProtectedError: messages.error( request, - (_("The stack %s is used by another object, impossible to" - " deleted it.") % stack) + ( + _( + "The stack %s is used by another object, impossible to" + " deleted it." + ) + % stack + ), ) - return redirect(reverse('topologie:index-physical-grouping')) - return form({'objet': stack}, 'topologie/delete.html', request) + return redirect(reverse("topologie:index-physical-grouping")) + return form({"objet": stack}, "topologie/delete.html", request) @login_required @@ -486,9 +492,9 @@ def edit_switchs_stack(request, stack, **_kwargs): if request.method == "POST": pass else: - context = {'stack': stack} - context['switchs_stack'] = stack.switchs_set.all() - context['switchs_autres'] = Switch.object.filter(stack=None) + context = {"stack": stack} + context["switchs_stack"] = stack.switchs_set.all() + context["switchs_autres"] = Switch.object.filter(stack=None) @login_required @@ -497,26 +503,22 @@ def new_switch(request): """ Creation d'un switch. Cree en meme temps l'interface et la machine associée. Vue complexe. Appelle successivement les 4 models forms adaptés : machine, interface, domain et switch""" - switch = NewSwitchForm( - request.POST or None, - user=request.user - ) - interface = AddInterfaceForm( - request.POST or None, - user=request.user - ) - domain = DomainForm( - request.POST or None, - ) + switch = NewSwitchForm(request.POST or None, user=request.user) + interface = AddInterfaceForm(request.POST or None, user=request.user) + domain = DomainForm(request.POST or None, user=request.user) if switch.is_valid() and interface.is_valid(): - user = AssoOption.get_cached_value('utilisateur_asso') + user = AssoOption.get_cached_value("utilisateur_asso") if not user: messages.error( request, - (_("The organisation's user doesn't exist yet, please create" - " or link it in the preferences.")) + ( + _( + "The organisation's user doesn't exist yet, please create" + " or link it in the preferences." + ) + ), ) - return redirect(reverse('topologie:index')) + return redirect(reverse("topologie:index")) new_switch_obj = switch.save(commit=False) new_switch_obj.user = user new_interface_obj = interface.save(commit=False) @@ -529,18 +531,18 @@ def new_switch(request): new_domain_obj.interface_parent = new_interface_obj new_domain_obj.save() messages.success(request, _("The switch was created.")) - return redirect(reverse('topologie:index')) + return redirect(reverse("topologie:index")) i_mbf_param = generate_ipv4_mbf_param(interface, False) return form( { - 'topoform': interface, - 'machineform': switch, - 'domainform': domain, - 'i_mbf_param': i_mbf_param, - 'device': 'switch', + "topoform": interface, + "machineform": switch, + "domainform": domain, + "i_mbf_param": i_mbf_param, + "device": _("switch"), }, - 'topologie/topo_more.html', - request + "topologie/topo_more.html", + request, ) @@ -552,30 +554,24 @@ def create_ports(request, switchid): switch = Switch.objects.get(pk=switchid) except Switch.DoesNotExist: messages.error(request, _("Nonexistent switch.")) - return redirect(reverse('topologie:index')) - - first_port = getattr(switch.ports.order_by('port').first(), 'port', 1) + return redirect(reverse("topologie:index")) + + first_port = getattr(switch.ports.order_by("port").first(), "port", 1) last_port = switch.number + first_port - 1 port_form = CreatePortsForm( - request.POST or None, - initial={'begin': first_port, 'end': last_port} + request.POST or None, initial={"begin": first_port, "end": last_port} ) if port_form.is_valid(): - begin = port_form.cleaned_data['begin'] - end = port_form.cleaned_data['end'] + begin = port_form.cleaned_data["begin"] + end = port_form.cleaned_data["end"] try: switch.create_ports(begin, end) messages.success(request, _("The ports were created.")) except ValidationError as e: - messages.error(request, ''.join(e)) - return redirect(reverse( - 'topologie:index-port', - kwargs={'switchid': switchid} - )) + messages.error(request, "".join(e)) + return redirect(reverse("topologie:index-port", kwargs={"switchid": switchid})) return form( - {'id_switch': switchid, 'topoform': port_form}, - 'topologie/switch.html', - request + {"id_switch": switchid, "topoform": port_form}, "topologie/switch.html", request ) @@ -586,18 +582,15 @@ def edit_switch(request, switch, switchid): place dans le stack, interface et machine associée""" switch_form = EditSwitchForm( - request.POST or None, - instance=switch, - user=request.user + request.POST or None, instance=switch, user=request.user ) interface_form = EditInterfaceForm( - request.POST or None, - instance=switch.interface_set.first(), - user=request.user + request.POST or None, instance=switch.interface_set.first(), user=request.user ) domain_form = DomainForm( request.POST or None, - instance=switch.interface_set.first().domain + instance=switch.interface_set.first().domain, + user=request.user, ) if switch_form.is_valid() and interface_form.is_valid(): new_switch_obj = switch_form.save(commit=False) @@ -610,19 +603,19 @@ def edit_switch(request, switch, switchid): if domain_form.changed_data: new_domain_obj.save() messages.success(request, _("The switch was edited.")) - return redirect(reverse('topologie:index')) + return redirect(reverse("topologie:index")) i_mbf_param = generate_ipv4_mbf_param(interface_form, False) return form( { - 'id_switch': switchid, - 'topoform': interface_form, - 'machineform': switch_form, - 'domainform': domain_form, - 'i_mbf_param': i_mbf_param, - 'device': 'switch', + "id_switch": switchid, + "topoform": interface_form, + "machineform": switch_form, + "domainform": domain_form, + "i_mbf_param": i_mbf_param, + "device": _("switch"), }, - 'topologie/topo_more.html', - request + "topologie/topo_more.html", + request, ) @@ -632,26 +625,22 @@ def new_ap(request): """ Creation d'une ap. Cree en meme temps l'interface et la machine associée. Vue complexe. Appelle successivement les 3 models forms adaptés : machine, interface, domain et switch""" - ap = AddAccessPointForm( - request.POST or None, - user=request.user - ) - interface = AddInterfaceForm( - request.POST or None, - user=request.user - ) - domain = DomainForm( - request.POST or None, - ) + ap = AddAccessPointForm(request.POST or None, user=request.user) + interface = AddInterfaceForm(request.POST or None, user=request.user) + domain = DomainForm(request.POST or None, user=request.user) if ap.is_valid() and interface.is_valid(): - user = AssoOption.get_cached_value('utilisateur_asso') + user = AssoOption.get_cached_value("utilisateur_asso") if not user: messages.error( request, - (_("The organisation's user doesn't exist yet, please create" - " or link it in the preferences.")) + ( + _( + "The organisation's user doesn't exist yet, please create" + " or link it in the preferences." + ) + ), ) - return redirect(reverse('topologie:index')) + return redirect(reverse("topologie:index")) new_ap_obj = ap.save(commit=False) new_ap_obj.user = user new_interface_obj = interface.save(commit=False) @@ -664,18 +653,18 @@ def new_ap(request): new_domain_obj.interface_parent = new_interface_obj new_domain_obj.save() messages.success(request, _("The access point was created.")) - return redirect(reverse('topologie:index-ap')) + return redirect(reverse("topologie:index-ap")) i_mbf_param = generate_ipv4_mbf_param(interface, False) return form( { - 'topoform': interface, - 'machineform': ap, - 'domainform': domain, - 'i_mbf_param': i_mbf_param, - 'device': 'wifi ap', + "topoform": interface, + "machineform": ap, + "domainform": domain, + "i_mbf_param": i_mbf_param, + "device": _("access point"), }, - 'topologie/topo_more.html', - request + "topologie/topo_more.html", + request, ) @@ -685,28 +674,27 @@ def edit_ap(request, ap, **_kwargs): """ Edition d'un switch. Permet de chambre nombre de ports, place dans le stack, interface et machine associée""" interface_form = EditInterfaceForm( - request.POST or None, - user=request.user, - instance=ap.interface_set.first() - ) - ap_form = EditAccessPointForm( - request.POST or None, - user=request.user, - instance=ap + request.POST or None, user=request.user, instance=ap.interface_set.first() ) + ap_form = EditAccessPointForm(request.POST or None, user=request.user, instance=ap) domain_form = DomainForm( request.POST or None, - instance=ap.interface_set.first().domain + instance=ap.interface_set.first().domain, + user=request.user, ) if ap_form.is_valid() and interface_form.is_valid(): - user = AssoOption.get_cached_value('utilisateur_asso') + user = AssoOption.get_cached_value("utilisateur_asso") if not user: messages.error( request, - (_("The organisation's user doesn't exist yet, please create" - " or link it in the preferences.")) + ( + _( + "The organisation's user doesn't exist yet, please create" + " or link it in the preferences." + ) + ), ) - return redirect(reverse('topologie:index-ap')) + return redirect(reverse("topologie:index-ap")) new_ap_obj = ap_form.save(commit=False) new_interface_obj = interface_form.save(commit=False) new_domain_obj = domain_form.save(commit=False) @@ -717,18 +705,18 @@ def edit_ap(request, ap, **_kwargs): if domain_form.changed_data: new_domain_obj.save() messages.success(request, _("The access point was edited.")) - return redirect(reverse('topologie:index-ap')) + return redirect(reverse("topologie:index-ap")) i_mbf_param = generate_ipv4_mbf_param(interface_form, False) return form( { - 'topoform': interface_form, - 'machineform': ap_form, - 'domainform': domain_form, - 'i_mbf_param': i_mbf_param, - 'device': 'wifi ap', + "topoform": interface_form, + "machineform": ap_form, + "domainform": domain_form, + "i_mbf_param": i_mbf_param, + "device": _("access point"), }, - 'topologie/topo_more.html', - request + "topologie/topo_more.html", + request, ) @@ -740,11 +728,9 @@ def new_room(request): if room.is_valid(): room.save() messages.success(request, _("The room was created.")) - return redirect(reverse('topologie:index-room')) + return redirect(reverse("topologie:index-room")) return form( - {'topoform': room, 'action_name': _("Create")}, - 'topologie/topo.html', - request + {"topoform": room, "action_name": _("Add")}, "topologie/topo.html", request ) @@ -757,11 +743,9 @@ def edit_room(request, room, **_kwargs): if room.changed_data: room.save() messages.success(request, _("The room was edited.")) - return redirect(reverse('topologie:index-room')) + return redirect(reverse("topologie:index-room")) return form( - {'topoform': room, 'action_name': _("Edit")}, - 'topologie/topo.html', - request + {"topoform": room, "action_name": _("Edit")}, "topologie/topo.html", request ) @@ -776,14 +760,17 @@ def del_room(request, room, **_kwargs): except ProtectedError: messages.error( request, - (_("The room %s is used by another object, impossible to" - " deleted it.") % room) + ( + _( + "The room %s is used by another object, impossible to" + " deleted it." + ) + % room + ), ) - return redirect(reverse('topologie:index-room')) + return redirect(reverse("topologie:index-room")) return form( - {'objet': room, 'objet_name': _("Room")}, - 'topologie/delete.html', - request + {"objet": room, "objet_name": _("room")}, "topologie/delete.html", request ) @@ -795,11 +782,11 @@ def new_model_switch(request): if model_switch.is_valid(): model_switch.save() messages.success(request, _("The switch model was created.")) - return redirect(reverse('topologie:index-model-switch')) + return redirect(reverse("topologie:index-model-switch")) return form( - {'topoform': model_switch, 'action_name': _("Create")}, - 'topologie/topo.html', - request + {"topoform": model_switch, "action_name": _("Add")}, + "topologie/topo.html", + request, ) @@ -808,19 +795,16 @@ def new_model_switch(request): def edit_model_switch(request, model_switch, **_kwargs): """ Edition d'un modèle de switch""" - model_switch = EditModelSwitchForm( - request.POST or None, - instance=model_switch - ) + model_switch = EditModelSwitchForm(request.POST or None, instance=model_switch) if model_switch.is_valid(): if model_switch.changed_data: model_switch.save() messages.success(request, _("The switch model was edited.")) - return redirect(reverse('topologie:index-model-switch')) + return redirect(reverse("topologie:index-model-switch")) return form( - {'topoform': model_switch, 'action_name': _("Edit")}, - 'topologie/topo.html', - request + {"topoform": model_switch, "action_name": _("Edit")}, + "topologie/topo.html", + request, ) @@ -835,14 +819,19 @@ def del_model_switch(request, model_switch, **_kwargs): except ProtectedError: messages.error( request, - (_("The switch model %s is used by another object," - " impossible to delete it.") % model_switch) + ( + _( + "The switch model %s is used by another object," + " impossible to delete it." + ) + % model_switch + ), ) - return redirect(reverse('topologie:index-model-switch')) + return redirect(reverse("topologie:index-model-switch")) return form( - {'objet': model_switch, 'objet_name': _("Switch model")}, - 'topologie/delete.html', - request + {"objet": model_switch, "objet_name": _("switch model")}, + "topologie/delete.html", + request, ) @@ -854,11 +843,11 @@ def new_switch_bay(request): if switch_bay.is_valid(): switch_bay.save() messages.success(request, _("The switch bay was created.")) - return redirect(reverse('topologie:index-physical-grouping')) + return redirect(reverse("topologie:index-physical-grouping")) return form( - {'topoform': switch_bay, 'action_name': _("Create")}, - 'topologie/topo.html', - request + {"topoform": switch_bay, "action_name": _("Add")}, + "topologie/topo.html", + request, ) @@ -871,11 +860,11 @@ def edit_switch_bay(request, switch_bay, **_kwargs): if switch_bay.changed_data: switch_bay.save() messages.success(request, _("The switch bay was edited.")) - return redirect(reverse('topologie:index-physical-grouping')) + return redirect(reverse("topologie:index-physical-grouping")) return form( - {'topoform': switch_bay, 'action_name': _("Edit")}, - 'topologie/topo.html', - request + {"topoform": switch_bay, "action_name": _("Edit")}, + "topologie/topo.html", + request, ) @@ -890,54 +879,60 @@ def del_switch_bay(request, switch_bay, **_kwargs): except ProtectedError: messages.error( request, - (_("The switch bay %s is used by another object," - " impossible to delete it.") % switch_bay) + ( + _( + "The switch bay %s is used by another object," + " impossible to delete it." + ) + % switch_bay + ), ) - return redirect(reverse('topologie:index-physical-grouping')) + return redirect(reverse("topologie:index-physical-grouping")) return form( - {'objet': switch_bay, 'objet_name': _("Switch bay")}, - 'topologie/delete.html', - request + {"objet": switch_bay, "objet_name": _("switch bay")}, + "topologie/delete.html", + request, ) @login_required @can_create(Building) def new_building(request): - """Nouveau batiment""" + """New Building of a dorm + Nouveau batiment""" building = EditBuildingForm(request.POST or None) if building.is_valid(): building.save() messages.success(request, _("The building was created.")) - return redirect(reverse('topologie:index-physical-grouping')) + return redirect(reverse("topologie:index-physical-grouping")) return form( - {'topoform': building, 'action_name': _("Create")}, - 'topologie/topo.html', - request + {"topoform": building, "action_name": _("Add")}, + "topologie/topo.html", + request, ) @login_required @can_edit(Building) def edit_building(request, building, **_kwargs): - """ Edition d'un batiment""" + """Edit a building + Edition d'un batiment""" building = EditBuildingForm(request.POST or None, instance=building) if building.is_valid(): if building.changed_data: building.save() messages.success(request, _("The building was edited.")) - return redirect(reverse('topologie:index-physical-grouping')) + return redirect(reverse("topologie:index-physical-grouping")) return form( - {'topoform': building, 'action_name': _("Edit")}, - 'topologie/topo.html', - request + {"topoform": building, "action_name": _("Edit")}, "topologie/topo.html", request ) @login_required @can_delete(Building) def del_building(request, building, **_kwargs): - """ Suppression d'un batiment""" + """Delete a building + Suppression d'un batiment""" if request.method == "POST": try: building.delete() @@ -945,14 +940,82 @@ def del_building(request, building, **_kwargs): except ProtectedError: messages.error( request, - (_("The building %s is used by another object, impossible" - " to delete it.") % building) + ( + _( + "The building %s is used by another object, impossible" + " to delete it." + ) + % building + ), ) - return redirect(reverse('topologie:index-physical-grouping')) + return redirect(reverse("topologie:index-physical-grouping")) return form( - {'objet': building, 'objet_name': _("Building")}, - 'topologie/delete.html', - request + {"objet": building, "objet_name": _("building")}, + "topologie/delete.html", + request, + ) + + +@login_required +@can_create(Dormitory) +def new_dormitory(request): + """A new dormitory + Nouvelle residence""" + dormitory = EditDormitoryForm(request.POST or None) + if dormitory.is_valid(): + dormitory.save() + messages.success(request, _("The dormitory was created.")) + return redirect(reverse("topologie:index-physical-grouping")) + return form( + {"topoform": dormitory, "action_name": _("Add")}, + "topologie/topo.html", + request, + ) + + +@login_required +@can_edit(Dormitory) +def edit_dormitory(request, dormitory, **_kwargs): + """Edit a dormitory + Edition d'une residence""" + dormitory = EditDormitoryForm(request.POST or None, instance=dormitory) + if dormitory.is_valid(): + if dormitory.changed_data: + dormitory.save() + messages.success(request, _("The dormitory was edited.")) + return redirect(reverse("topologie:index-physical-grouping")) + return form( + {"topoform": dormitory, "action_name": _("Edit")}, + "topologie/topo.html", + request, + ) + + +@login_required +@can_delete(Dormitory) +def del_dormitory(request, dormitory, **_kwargs): + """Delete a dormitory + Suppression d'une residence""" + if request.method == "POST": + try: + dormitory.delete() + messages.success(request, _("The dormitory was deleted.")) + except ProtectedError: + messages.error( + request, + ( + _( + "The dormitory %s is used by another object, impossible" + " to delete it." + ) + % dormitory + ), + ) + return redirect(reverse("topologie:index-physical-grouping")) + return form( + {"objet": dormitory, "objet_name": _("dormitory")}, + "topologie/delete.html", + request, ) @@ -964,11 +1027,11 @@ def new_constructor_switch(request): if constructor_switch.is_valid(): constructor_switch.save() messages.success(request, _("The switch constructor was created.")) - return redirect(reverse('topologie:index-model-switch')) + return redirect(reverse("topologie:index-model-switch")) return form( - {'topoform': constructor_switch, 'action_name': _("Create")}, - 'topologie/topo.html', - request + {"topoform": constructor_switch, "action_name": _("Add")}, + "topologie/topo.html", + request, ) @@ -978,18 +1041,17 @@ def edit_constructor_switch(request, constructor_switch, **_kwargs): """ Edition d'un constructeur de switch""" constructor_switch = EditConstructorSwitchForm( - request.POST or None, - instance=constructor_switch + request.POST or None, instance=constructor_switch ) if constructor_switch.is_valid(): if constructor_switch.changed_data: constructor_switch.save() messages.success(request, _("The switch constructor was edited.")) - return redirect(reverse('topologie:index-model-switch')) + return redirect(reverse("topologie:index-model-switch")) return form( - {'topoform': constructor_switch, 'action_name': _("Edit")}, - 'topologie/topo.html', - request + {"topoform": constructor_switch, "action_name": _("Edit")}, + "topologie/topo.html", + request, ) @@ -1004,14 +1066,20 @@ def del_constructor_switch(request, constructor_switch, **_kwargs): except ProtectedError: messages.error( request, - (_("The switch constructor %s is used by another object," - " impossible to delete it.") % constructor_switch) + ( + _( + "The switch constructor %s is used by another object," + " impossible to delete it." + ) + % constructor_switch + ), ) - return redirect(reverse('topologie:index-model-switch')) - return form({ - 'objet': constructor_switch, - 'objet_name': _("Switch constructor") - }, 'topologie/delete.html', request) + return redirect(reverse("topologie:index-model-switch")) + return form( + {"objet": constructor_switch, "objet_name": _("switch constructor")}, + "topologie/delete.html", + request, + ) @login_required @@ -1022,11 +1090,11 @@ def new_port_profile(request): if port_profile.is_valid(): port_profile.save() messages.success(request, _("The port profile was created.")) - return redirect(reverse('topologie:index-port-profile')) + return redirect(reverse("topologie:index-port-profile")) return form( - {'topoform': port_profile, 'action_name': _("Create")}, - 'topologie/topo.html', - request + {"topoform": port_profile, "action_name": _("Add")}, + "topologie/topo.html", + request, ) @@ -1034,17 +1102,16 @@ def new_port_profile(request): @can_edit(PortProfile) def edit_port_profile(request, port_profile, **_kwargs): """Edit a port profile""" - port_profile = EditPortProfileForm( - request.POST or None, instance=port_profile) + port_profile = EditPortProfileForm(request.POST or None, instance=port_profile) if port_profile.is_valid(): if port_profile.changed_data: port_profile.save() messages.success(request, _("The port profile was edited.")) - return redirect(reverse('topologie:index-port-profile')) + return redirect(reverse("topologie:index-port-profile")) return form( - {'topoform': port_profile, 'action_name': _("Edit")}, - 'topologie/topo.html', - request + {"topoform": port_profile, "action_name": _("Edit")}, + "topologie/topo.html", + request, ) @@ -1052,19 +1119,17 @@ def edit_port_profile(request, port_profile, **_kwargs): @can_delete(PortProfile) def del_port_profile(request, port_profile, **_kwargs): """Delete a port profile""" - if request.method == 'POST': + if request.method == "POST": try: port_profile.delete() - messages.success(request, - _("The port profile was deleted.")) + messages.success(request, _("The port profile was deleted.")) except ProtectedError: - messages.success(request, - _("Impossible to delete the port profile.")) - return redirect(reverse('topologie:index-port-profile')) + messages.success(request, _("Impossible to delete the port profile.")) + return redirect(reverse("topologie:index-port-profile")) return form( - {'objet': port_profile, 'objet_name': _("Port profile")}, - 'topologie/delete.html', - request + {"objet": port_profile, "objet_name": _("port profile")}, + "topologie/delete.html", + request, ) @@ -1076,11 +1141,9 @@ def add_module(request): if module.is_valid(): module.save() messages.success(request, _("The module was created.")) - return redirect(reverse('topologie:index-module')) + return redirect(reverse("topologie:index-module")) return form( - {'topoform': module, 'action_name': _("Create")}, - 'topologie/topo.html', - request + {"topoform": module, "action_name": _("Add")}, "topologie/topo.html", request ) @@ -1093,11 +1156,9 @@ def edit_module(request, module_instance, **_kwargs): if module.changed_data: module.save() messages.success(request, _("The module was edited.")) - return redirect(reverse('topologie:index-module')) + return redirect(reverse("topologie:index-module")) return form( - {'topoform': module, 'action_name': _("Edit")}, - 'topologie/topo.html', - request + {"topoform": module, "action_name": _("Edit")}, "topologie/topo.html", request ) @@ -1112,16 +1173,20 @@ def del_module(request, module, **_kwargs): except ProtectedError: messages.error( request, - (_("The module %s is used by another object, impossible to" - " deleted it.") % module) + ( + _( + "The module %s is used by another object, impossible to" + " deleted it." + ) + % module + ), ) - return redirect(reverse('topologie:index-module')) + return redirect(reverse("topologie:index-module")) return form( - {'objet': module, 'objet_name': _("Module")}, - 'topologie/delete.html', - request + {"objet": module, "objet_name": _("module")}, "topologie/delete.html", request ) + @login_required @can_create(ModuleOnSwitch) def add_module_on(request): @@ -1130,11 +1195,11 @@ def add_module_on(request): if module_switch.is_valid(): module_switch.save() messages.success(request, _("The module was added.")) - return redirect(reverse('topologie:index-module')) + return redirect(reverse("topologie:index-module")) return form( - {'topoform': module_switch, 'action_name': _("Add")}, - 'topologie/topo.html', - request + {"topoform": module_switch, "action_name": _("Add")}, + "topologie/topo.html", + request, ) @@ -1147,11 +1212,9 @@ def edit_module_on(request, module_instance, **_kwargs): if module.changed_data: module.save() messages.success(request, _("The module was edited.")) - return redirect(reverse('topologie:index-module')) + return redirect(reverse("topologie:index-module")) return form( - {'topoform': module, 'action_name': _("Edit")}, - 'topologie/topo.html', - request + {"topoform": module, "action_name": _("Edit")}, "topologie/topo.html", request ) @@ -1166,14 +1229,17 @@ def del_module_on(request, module, **_kwargs): except ProtectedError: messages.error( request, - (_("The module %s is used by another object, impossible to" - " deleted it.") % module) + ( + _( + "The module %s is used by another object, impossible to" + " deleted it." + ) + % module + ), ) - return redirect(reverse('topologie:index-module')) + return redirect(reverse("topologie:index-module")) return form( - {'objet': module, 'objet_name': _("Module")}, - 'topologie/delete.html', - request + {"objet": module, "objet_name": _("module")}, "topologie/delete.html", request ) @@ -1182,72 +1248,123 @@ def make_machine_graph(): Create the graph of switchs, machines and access points. """ dico = { - 'subs': [], - 'links': [], - 'alone': [], - 'colors': { - 'head': "#7f0505", # Color parameters for the graph - 'back': "#b5adad", - 'texte': "#563d01", - 'border_bornes': "#02078e", - 'head_bornes': "#25771c", - 'head_server': "#1c3777" - } + "subs": [], + "links": [], + "alone": [], + "colors": { + "head": "#7f0505", # Color parameters for the graph + "back": "#b5adad", + "texte": "#563d01", + "border_bornes": "#02078e", + "head_bornes": "#25771c", + "head_server": "#1c3777", + }, } - missing = list(Switch.objects.all()) + missing = list( + Switch.objects.prefetch_related( + Prefetch( + "interface_set", + queryset=( + Interface.objects.select_related( + "ipv4__ip_type__extension" + ).select_related("domain__extension") + ), + ) + ) + ) detected = [] for building in Building.objects.all(): # Visit all buildings - dico['subs'].append( + dico["subs"].append( { - 'bat_id': building.id, - 'bat_name': building, - 'switchs': [], - 'bornes': [], - 'machines': [] + "bat_id": building.id, + "bat_name": building, + "switchs": [], + "bornes": [], + "machines": [], } ) # Visit all switchs in this building - for switch in Switch.objects.filter(switchbay__building=building): - dico['subs'][-1]['switchs'].append({ - 'name': switch.main_interface().domain.name, - 'nombre': switch.number, - 'model': switch.model, - 'id': switch.id, - 'batiment': building, - 'ports': [] - }) + for switch in ( + Switch.objects.filter(switchbay__building=building) + .prefetch_related( + Prefetch( + "interface_set", + queryset=( + Interface.objects.select_related( + "ipv4__ip_type__extension" + ).select_related("domain__extension") + ), + ) + ) + .select_related("switchbay__building") + .select_related("switchbay__building__dormitory") + .select_related("model__constructor") + ): + dico["subs"][-1]["switchs"].append( + { + "name": switch.get_name, + "nombre": switch.number, + "model": switch.model, + "id": switch.id, + "batiment": building, + "ports": [], + } + ) # visit all ports of this switch and add the switchs linked to it - for port in switch.ports.filter(related__isnull=False): - dico['subs'][-1]['switchs'][-1]['ports'].append({ - 'numero': port.port, - 'related': port.related.switch.main_interface().domain.name - }) + for port in switch.ports.filter(related__isnull=False).select_related( + "related__switch" + ): + dico["subs"][-1]["switchs"][-1]["ports"].append( + {"numero": port.port, "related": port.related.switch.get_name} + ) - for ap in AccessPoint.all_ap_in(building): - dico['subs'][-1]['bornes'].append({ - 'name': ap.short_name, - 'switch': ap.switch()[0].main_interface().domain.name, - 'port': ap.switch()[0].ports.filter( - machine_interface__machine=ap - )[0].port - }) - for server in Server.all_server_in(building): - dico['subs'][-1]['machines'].append({ - 'name': server.short_name, - 'switch': server.switch()[0].main_interface().domain.name, - 'port': Port.objects.filter( - machine_interface__machine=server - )[0].port - }) + for ap in AccessPoint.all_ap_in(building).prefetch_related( + Prefetch( + "interface_set", + queryset=( + Interface.objects.select_related( + "ipv4__ip_type__extension" + ).select_related("domain__extension") + ), + ) + ): + switch = ap.switch().first() + dico["subs"][-1]["bornes"].append( + { + "name": ap.short_name, + "switch": switch.get_name, + "port": switch.ports.filter(machine_interface__machine=ap) + .first() + .port, + } + ) + for server in Server.all_server_in(building).prefetch_related( + Prefetch( + "interface_set", + queryset=( + Interface.objects.select_related( + "ipv4__ip_type__extension" + ).select_related("domain__extension") + ), + ) + ): + dico["subs"][-1]["machines"].append( + { + "name": server.short_name, + "switch": server.switch().first().get_name, + "port": Port.objects.filter(machine_interface__machine=server) + .first() + .port, + } + ) # While the list of forgotten ones is not empty while missing: if missing[0].ports.count(): # The switch is not empty - links, new_detected = recursive_switchs( - missing[0], None, [missing[0]]) + links, new_detected = recursive_switchs(missing[0], None, [missing[0]]) for link in links: - dico['links'].append(link) + dico["links"].append(link) # Update the lists of missings and already detected switchs missing = [i for i in missing if i not in new_detected] detected += new_detected @@ -1255,28 +1372,25 @@ def make_machine_graph(): else: del missing[0] # Switchs that are not connected or not in a building - for switch in Switch.objects.filter( - switchbay__isnull=True).exclude(ports__related__isnull=False): - dico['alone'].append({ - 'id': switch.id, - 'name': switch.main_interface().domain.name - }) + for switch in Switch.objects.filter(switchbay__isnull=True).exclude( + ports__related__isnull=False + ): + dico["alone"].append({"id": switch.id, "name": switch.get_name}) # generate the dot file - dot_data = generate_dot(dico, 'topologie/graph_switch.dot') + dot_data = generate_dot(dico, "topologie/graph_switch.dot") # Create a temporary file to store the dot data - f = tempfile.NamedTemporaryFile(mode='w+', encoding='utf-8', delete=False) + f = tempfile.NamedTemporaryFile(mode="w+", encoding="utf-8", delete=False) with f: f.write(dot_data) unflatten = Popen( # unflatten the graph to make it look better - ["unflatten", "-l", "3", f.name], - stdout=PIPE + ["unflatten", "-l", "3", f.name], stdout=PIPE ) Popen( # pipe the result of the first command into the second ["dot", "-Tpng", "-o", MEDIA_ROOT + "/images/switchs.png"], stdin=unflatten.stdout, - stdout=PIPE + stdout=PIPE, ) @@ -1286,13 +1400,18 @@ def generate_dot(data, template): :param template: path to the dot template :return: all the lines of the dot file""" t = loader.get_template(template) - if not isinstance(t, Template) and \ - not (hasattr(t, 'template') and isinstance(t.template, Template)): - raise Exception(_("The default Django template isn't used. This can" - " lead to rendering errors. Check the parameters.")) + if not isinstance(t, Template) and not ( + hasattr(t, "template") and isinstance(t.template, Template) + ): + raise Exception( + _( + "The default Django template isn't used. This can" + " lead to rendering errors. Check the parameters." + ) + ) c = Context(data).flatten() dot = t.render(c) - return(dot) + return dot def recursive_switchs(switch_start, switch_before, detected): @@ -1310,12 +1429,14 @@ def recursive_switchs(switch_start, switch_before, detected): # create links to every switchs below for port in switch_start.ports.filter(related__isnull=False): # Not the switch that we come from, not the current switch - if port.related.switch != switch_before \ - and port.related.switch != port.switch \ - and port.related.switch not in detected: + if ( + port.related.switch != switch_before + and port.related.switch != port.switch + and port.related.switch not in detected + ): links = { # Dictionary of a link - 'depart': switch_start.id, - 'arrive': port.related.switch.id + "depart": switch_start.id, + "arrive": port.related.switch.id, } links_return.append(links) # Add current and below levels links @@ -1325,10 +1446,10 @@ def recursive_switchs(switch_start, switch_before, detected): if port.related.switch not in detected: # explore it and get the results links_down, detected = recursive_switchs( - port.related.switch, switch_start, detected) + port.related.switch, switch_start, detected + ) # Add the non empty links to the current list for link in links_down: if link: links_return.append(link) return (links_return, detected) - diff --git a/users/__init__.py b/users/__init__.py index b661a850..b75d67cb 100644 --- a/users/__init__.py +++ b/users/__init__.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify diff --git a/users/acl.py b/users/acl.py index cb3a16db..d66b3d2c 100644 --- a/users/acl.py +++ b/users/acl.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -27,6 +27,7 @@ Here are defined some functions to check acl on the application. """ from django.utils.translation import ugettext as _ + def can_view(user): """Check if an user can view the application. @@ -37,7 +38,9 @@ def can_view(user): 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('users') - return can, None if can else _("You don't have the right to view this" - " application.") - + can = user.has_module_perms("users") + return ( + can, + None if can else _("You don't have the right to view this application."), + ("users",), + ) diff --git a/users/admin.py b/users/admin.py index e7dd3240..f3ec04f4 100644 --- a/users/admin.py +++ b/users/admin.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -47,80 +47,92 @@ from .models import ( LdapUser, LdapServiceUser, LdapServiceUserGroup, - LdapUserGroup + LdapUserGroup, ) from .forms import ( UserChangeForm, UserCreationForm, ServiceUserChangeForm, - ServiceUserCreationForm + ServiceUserCreationForm, ) class LdapUserAdmin(admin.ModelAdmin): """Administration du ldapuser""" - list_display = ('name', 'uidNumber', 'login_shell') - exclude = ('user_password', 'sambat_nt_password') - search_fields = ('name',) + + list_display = ("name", "uidNumber", "login_shell") + exclude = ("user_password", "sambat_nt_password") + search_fields = ("name",) class LdapServiceUserAdmin(admin.ModelAdmin): """Administration du ldapserviceuser""" - list_display = ('name',) - exclude = ('user_password',) - search_fields = ('name',) + + list_display = ("name",) + exclude = ("user_password",) + search_fields = ("name",) class LdapUserGroupAdmin(admin.ModelAdmin): """Administration du ldapusergroupe""" - list_display = ('name', 'members', 'gid') - search_fields = ('name',) + + list_display = ("name", "members", "gid") + search_fields = ("name",) class LdapServiceUserGroupAdmin(admin.ModelAdmin): """Administration du ldap serviceusergroup""" - list_display = ('name',) - search_fields = ('name',) + + list_display = ("name",) + search_fields = ("name",) class SchoolAdmin(VersionAdmin): """Administration, gestion des écoles""" + pass class ListRightAdmin(VersionAdmin): """Gestion de la liste des droits existants Ne permet pas l'edition du gid (primarykey pour ldap)""" - list_display = ('unix_name',) + + list_display = ("unix_name",) class ListShellAdmin(VersionAdmin): """Gestion de la liste des shells coté admin""" + pass class RequestAdmin(admin.ModelAdmin): """Gestion des request objet, ticket pour lien de reinit mot de passe""" - list_display = ('user', 'type', 'created_at', 'expires_at') + + list_display = ("user", "type", "created_at", "expires_at") class BanAdmin(VersionAdmin): """Gestion des bannissements""" + pass class EMailAddressAdmin(VersionAdmin): """Gestion des alias mail""" + pass class WhitelistAdmin(VersionAdmin): """Gestion des whitelist""" + pass class UserAdmin(VersionAdmin, BaseUserAdmin): """Gestion d'un user : modification des champs perso, mot de passe, etc""" + # The forms to add and change user instances form = UserChangeForm add_form = UserCreationForm @@ -129,28 +141,25 @@ class UserAdmin(VersionAdmin, BaseUserAdmin): # These override the definitions on the base UserAdmin # that reference specific fields on auth.User. list_display = ( - 'pseudo', - 'surname', - 'email', - 'local_email_redirect', - 'local_email_enabled', - 'school', - 'is_admin', - 'shell' + "pseudo", + "surname", + "email", + "local_email_redirect", + "local_email_enabled", + "school", + "is_admin", + "shell", ) # Need to reset the settings from BaseUserAdmin # They are using fields we don't use like 'is_staff' list_filter = () fieldsets = ( - (None, {'fields': ('pseudo', 'password')}), + (None, {"fields": ("pseudo", "password")}), ( - 'Personal info', - { - 'fields': - ('surname', 'email', 'school', 'shell', 'uid_number') - } + "Personal info", + {"fields": ("surname", "email", "school", "shell", "uid_number")}, ), - ('Permissions', {'fields': ('is_admin', )}), + ("Permissions", {"fields": ("is_admin",)}), ) # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin # overrides get_fieldsets to use this attribute when creating a user. @@ -158,27 +167,28 @@ class UserAdmin(VersionAdmin, BaseUserAdmin): ( None, { - 'classes': ('wide',), - 'fields': ( - 'pseudo', - 'surname', - 'email', - 'school', - 'is_admin', - 'password1', - 'password2' - ) - } + "classes": ("wide",), + "fields": ( + "pseudo", + "surname", + "email", + "school", + "is_admin", + "password1", + "password2", + ), + }, ), ) - search_fields = ('pseudo', 'surname') - ordering = ('pseudo',) + search_fields = ("pseudo", "surname") + ordering = ("pseudo",) filter_horizontal = () class ServiceUserAdmin(VersionAdmin, BaseUserAdmin): """Gestion d'un service user admin : champs personnels, mot de passe; etc""" + # The forms to add and change user instances form = ServiceUserChangeForm add_form = ServiceUserCreationForm @@ -186,24 +196,16 @@ class ServiceUserAdmin(VersionAdmin, BaseUserAdmin): # The fields to be used in displaying the User model. # These override the definitions on the base UserAdmin # that reference specific fields on auth.User. - list_display = ('pseudo', 'access_group') + list_display = ("pseudo", "access_group") list_filter = () - fieldsets = ( - (None, {'fields': ('pseudo', 'password', 'access_group')}), - ) + fieldsets = ((None, {"fields": ("pseudo", "password", "access_group")}),) # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin # overrides get_fieldsets to use this attribute when creating a user. add_fieldsets = ( - ( - None, - { - 'classes': ('wide',), - 'fields': ('pseudo', 'password1', 'password2') - } - ), + (None, {"classes": ("wide",), "fields": ("pseudo", "password1", "password2")}), ) - search_fields = ('pseudo',) - ordering = ('pseudo',) + search_fields = ("pseudo",) + ordering = ("pseudo",) filter_horizontal = () diff --git a/users/forms.py b/users/forms.py index 6fcfea81..cef1e43a 100644 --- a/users/forms.py +++ b/users/forms.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -38,6 +38,7 @@ from django.forms import ModelForm, Form from django.contrib.auth.forms import ReadOnlyPasswordHashField from django.core.validators import MinLengthValidator from django.utils import timezone +from django.utils.functional import lazy from django.contrib.auth.models import Group, Permission from django.utils.translation import ugettext_lazy as _ from django.utils.safestring import mark_safe @@ -64,7 +65,7 @@ from .models import ( ListShell, Ban, Adherent, - Club + Club, ) @@ -72,22 +73,21 @@ class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm): """Formulaire de changement de mot de passe. Verifie que les 2 nouveaux mots de passe renseignés sont identiques et respectent une norme""" + selfpasswd = forms.CharField( - label=_("Current password"), - max_length=255, - widget=forms.PasswordInput + label=_("Current password"), max_length=255, widget=forms.PasswordInput ) passwd1 = forms.CharField( label=_("New password"), max_length=255, validators=[MinLengthValidator(8)], - widget=forms.PasswordInput + widget=forms.PasswordInput, ) passwd2 = forms.CharField( label=_("New password confirmation"), max_length=255, validators=[MinLengthValidator(8)], - widget=forms.PasswordInput + widget=forms.PasswordInput, ) class Meta: @@ -100,16 +100,12 @@ class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm): password1 = self.cleaned_data.get("passwd1") password2 = self.cleaned_data.get("passwd2") if password1 and password2 and password1 != password2: - raise forms.ValidationError( - _("The new passwords don't match.") - ) + raise forms.ValidationError(_("The new passwords don't match.")) return password2 def clean_selfpasswd(self): """Verifie si il y a lieu que le mdp self est correct""" - if not self.instance.check_password( - self.cleaned_data.get("selfpasswd") - ): + if not self.instance.check_password(self.cleaned_data.get("selfpasswd")): raise forms.ValidationError(_("The current password is incorrect.")) return @@ -128,34 +124,38 @@ class UserCreationForm(FormRevMixin, forms.ModelForm): Formulaire pour la création d'un user. N'est utilisé que pour l'admin, lors de la creation d'un user par admin. Inclu tous les champs obligatoires""" + password1 = forms.CharField( label=_("Password"), widget=forms.PasswordInput, validators=[MinLengthValidator(8)], - max_length=255 + max_length=255, ) password2 = forms.CharField( label=_("Password confirmation"), widget=forms.PasswordInput, validators=[MinLengthValidator(8)], - max_length=255 + max_length=255, ) is_admin = forms.BooleanField(label=_("Is admin")) def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(UserCreationForm, self).__init__(*args, prefix=prefix, **kwargs) def clean_email(self): - if not OptionalUser.objects.first().local_email_domain in self.cleaned_data.get('email'): - return self.cleaned_data.get('email').lower() + if not OptionalUser.objects.first().local_email_domain in self.cleaned_data.get( + "email" + ): + return self.cleaned_data.get("email").lower() else: - raise forms.ValidationError(_("You can't use an internal address" - " as your external address.")) + raise forms.ValidationError( + _("You can't use an internal address as your external address.") + ) class Meta: model = Adherent - fields = ('pseudo', 'surname', 'email') + fields = ("pseudo", "surname", "email") def clean_password2(self): """Verifie que password1 et 2 sont identiques""" @@ -181,30 +181,24 @@ class ServiceUserCreationForm(FormRevMixin, forms.ModelForm): Formulaire pour la creation de nouveaux serviceusers. Requiert seulement un mot de passe; et un pseudo""" + password1 = forms.CharField( - label=_("Password"), - widget=forms.PasswordInput, - min_length=8, - max_length=255 + label=_("Password"), widget=forms.PasswordInput, min_length=8, max_length=255 ) password2 = forms.CharField( label=_("Password confirmation"), widget=forms.PasswordInput, min_length=8, - max_length=255 + max_length=255, ) def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(ServiceUserCreationForm, self).__init__( - *args, - prefix=prefix, - **kwargs - ) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) + super(ServiceUserCreationForm, self).__init__(*args, prefix=prefix, **kwargs) class Meta: model = ServiceUser - fields = ('pseudo',) + fields = ("pseudo",) def clean_password2(self): """Verifie que password1 et 2 sont indentiques""" @@ -230,18 +224,19 @@ class UserChangeForm(FormRevMixin, forms.ModelForm): Formulaire pour la modification d'un user coté admin """ + password = ReadOnlyPasswordHashField() is_admin = forms.BooleanField(label=_("Is admin"), required=False) class Meta: model = Adherent - fields = ('pseudo', 'password', 'surname', 'email') + fields = ("pseudo", "password", "surname", "email") def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(UserChangeForm, self).__init__(*args, prefix=prefix, **kwargs) - print(_("User is admin: %s") % kwargs['instance'].is_admin) - self.initial['is_admin'] = kwargs['instance'].is_admin + print(_("User is admin: %s") % kwargs["instance"].is_admin) + self.initial["is_admin"] = kwargs["instance"].is_admin def clean_password(self): """Dummy fun""" @@ -266,19 +261,16 @@ class ServiceUserChangeForm(FormRevMixin, forms.ModelForm): Formulaire pour l'edition des service users coté admin """ + password = ReadOnlyPasswordHashField() def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(ServiceUserChangeForm, self).__init__( - *args, - prefix=prefix, - **kwargs - ) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) + super(ServiceUserChangeForm, self).__init__(*args, prefix=prefix, **kwargs) class Meta: model = ServiceUser - fields = ('pseudo',) + fields = ("pseudo",) def clean_password(self): """Dummy fun""" @@ -288,6 +280,7 @@ class ServiceUserChangeForm(FormRevMixin, forms.ModelForm): class ResetPasswordForm(forms.Form): """Formulaire de demande de reinitialisation de mot de passe, mdp oublié""" + pseudo = forms.CharField(label=_("Username"), max_length=255) email = forms.EmailField(max_length=255) @@ -296,77 +289,91 @@ class MassArchiveForm(forms.Form): """Formulaire d'archivage des users inactif. Prend en argument du formulaire la date de depart avant laquelle archiver les users""" - date = forms.DateTimeField(help_text='%d/%m/%y') + + date = forms.DateTimeField(help_text="%d/%m/%y") + full_archive = forms.BooleanField( + label=_( + "Fully archive users? WARNING: CRITICAL OPERATION IF TRUE" + ), + initial=False, + required=False, + ) def clean(self): cleaned_data = super(MassArchiveForm, self).clean() date = cleaned_data.get("date") if date: if date > timezone.now(): - raise forms.ValidationError(_("Impossible to archive users" - " whose end access date is in" - " the future.")) + raise forms.ValidationError( + _( + "Impossible to archive users" + " whose end access date is in" + " the future." + ) + ) class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): """Formulaire de base d'edition d'un user. Formulaire de base, utilisé pour l'edition de self par self ou un cableur. On formate les champs avec des label plus jolis""" + def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(AdherentForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['name'].label = _("First name") - self.fields['surname'].label = _("Surname") - self.fields['email'].label = _("Email address") - self.fields['school'].label = _("School") - self.fields['comment'].label = _("Comment") - if 'room' in self.fields: - self.fields['room'].label = _("Room") - self.fields['room'].empty_label = _("No room") - self.fields['school'].empty_label = _("Select a school") + self.fields["name"].label = _("First name") + self.fields["surname"].label = _("Surname") + self.fields["email"].label = _("Email address") + self.fields["school"].label = _("School") + self.fields["comment"].label = _("Comment") + if "room" in self.fields: + self.fields["room"].label = _("Room") + self.fields["room"].empty_label = _("No room") + self.fields["school"].empty_label = _("Select a school") class Meta: model = Adherent fields = [ - 'name', - 'surname', - 'pseudo', - 'email', - 'school', - 'comment', - 'telephone', - 'room', + "name", + "surname", + "pseudo", + "email", + "school", + "comment", + "telephone", + "room", ] force = forms.BooleanField( - label=_("Force the move?"), - initial=False, - required=False + label=_("Force the move?"), initial=False, required=False ) def clean_email(self): - if not OptionalUser.objects.first().local_email_domain in self.cleaned_data.get('email'): - return self.cleaned_data.get('email').lower() + if not OptionalUser.objects.first().local_email_domain in self.cleaned_data.get( + "email" + ): + return self.cleaned_data.get("email").lower() else: raise forms.ValidationError( - _("You can't use a {} address.").format( - OptionalUser.objects.first().local_email_domain)) + _("You can't use a {} address.").format( + OptionalUser.objects.first().local_email_domain + ) + ) def clean_telephone(self): """Verifie que le tel est présent si 'option est validée dans preferences""" - telephone = self.cleaned_data['telephone'] - if not telephone and OptionalUser.get_cached_value('is_tel_mandatory'): - raise forms.ValidationError( - _("A valid telephone number is required.") - ) + telephone = self.cleaned_data["telephone"] + if not telephone and OptionalUser.get_cached_value("is_tel_mandatory"): + raise forms.ValidationError(_("A valid telephone number is required.")) return telephone def clean_force(self): """On supprime l'ancien user de la chambre si et seulement si la case est cochée""" - if self.cleaned_data.get('force', False): - remove_user_room(self.cleaned_data.get('room')) + room = self.cleaned_data.get("room") + if self.cleaned_data.get("force", False) and room: + remove_user_room(room) return @@ -376,153 +383,167 @@ class AdherentCreationForm(AdherentForm): doublons d'utilisateurs""" # Champ permettant d'éviter au maxium les doublons d'utilisateurs - former_user_check_info = _("If you already have an account, please use it. "\ - + "If your lost access to it, please consider "\ - + "using the forgotten password button on the "\ - + "login page or contacting support.") - former_user_check = forms.BooleanField(required=True, help_text=former_user_check_info) - former_user_check.label = _("I certify that I have not had an account before") + former_user_check_info = _( + "If you already have an account, please use it. If your lost access to" + " it, please consider using the forgotten password button on the" + " login page or contacting support." + ) + former_user_check = forms.BooleanField( + required=True, help_text=former_user_check_info + ) + former_user_check.label = _("I certify that I have not had an account before.") # Checkbox for GTU gtu_check = forms.BooleanField(required=True) - #gtu_check.label = mark_safe("{} {}{}".format( - # _("I commit to accept the"), GeneralOption.get_cached_value('GTU'), _("General Terms of Use"), _("."))) class Meta: model = Adherent fields = [ - 'name', - 'surname', - 'pseudo', - 'email', - 'school', - 'comment', - 'telephone', - 'room', - 'state', + "name", + "surname", + "pseudo", + "email", + "school", + "comment", + "telephone", + "room", + "state", ] def __init__(self, *args, **kwargs): super(AdherentCreationForm, self).__init__(*args, **kwargs) + gtu_file = GeneralOption.get_cached_value("GTU") + self.fields["gtu_check"].label = mark_safe( + "%s %s." + % ( + _("I commit to accept the"), + gtu_file.url if gtu_file else "#", + _("General Terms of Use"), + ) + ) + class AdherentEditForm(AdherentForm): """Formulaire d'édition d'un user. AdherentForm incluant la modification des champs gpg et shell""" + def __init__(self, *args, **kwargs): - super(AdherentEditForm, self).__init__(*args, **kwargs) - self.fields['gpg_fingerprint'].widget.attrs['placeholder'] = _("Leave empty if you don't have any GPG key.") - if 'shell' in self.fields: - self.fields['shell'].empty_label = _("Default shell") + super(AdherentEditForm, self).__init__(*args, **kwargs) + self.fields["gpg_fingerprint"].widget.attrs["placeholder"] = _( + "Leave empty if you don't have any GPG key." + ) + if "shell" in self.fields: + self.fields["shell"].empty_label = _("Default shell") class Meta: model = Adherent fields = [ - 'name', - 'surname', - 'pseudo', - 'email', - 'school', - 'comment', - 'telephone', - 'room', - 'shell', - 'gpg_fingerprint' + "name", + "surname", + "pseudo", + "email", + "school", + "comment", + "telephone", + "room", + "shell", + "gpg_fingerprint", + "shortcuts_enabled", ] + class ClubForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): """Formulaire de base d'edition d'un user. Formulaire de base, utilisé pour l'edition de self par self ou un cableur. On formate les champs avec des label plus jolis""" + def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(ClubForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['surname'].label = _("Name") - self.fields['school'].label = _("School") - self.fields['comment'].label = _("Comment") - self.fields['email'].label = _("Email address") - if 'room' in self.fields: - self.fields['room'].label = _("Room") - self.fields['room'].empty_label = _("No room") - self.fields['school'].empty_label = _("Select a school") - self.fields['mailing'].label = _("Use a mailing list") + self.fields["surname"].label = _("Name") + self.fields["school"].label = _("School") + self.fields["comment"].label = _("Comment") + self.fields["email"].label = _("Email address") + if "room" in self.fields: + self.fields["room"].label = _("Room") + self.fields["room"].empty_label = _("No room") + self.fields["school"].empty_label = _("Select a school") + self.fields["mailing"].label = _("Use a mailing list") class Meta: model = Club fields = [ - 'surname', - 'pseudo', - 'school', - 'comment', - 'room', - 'email', - 'telephone', - 'email', - 'shell', - 'mailing' + "surname", + "pseudo", + "school", + "comment", + "room", + "email", + "telephone", + "email", + "shell", + "mailing", ] def clean_telephone(self): """Verifie que le tel est présent si 'option est validée dans preferences""" - telephone = self.cleaned_data['telephone'] - if not telephone and OptionalUser.get_cached_value('is_tel_mandatory'): - raise forms.ValidationError( - _("A valid telephone number is required.") - ) + telephone = self.cleaned_data["telephone"] + if not telephone and OptionalUser.get_cached_value("is_tel_mandatory"): + raise forms.ValidationError(_("A valid telephone number is required.")) return telephone class ClubAdminandMembersForm(FormRevMixin, ModelForm): """Permet d'éditer la liste des membres et des administrateurs d'un club""" + class Meta: model = Club - fields = ['administrators', 'members'] + fields = ["administrators", "members"] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(ClubAdminandMembersForm, self).__init__( - *args, - prefix=prefix, - **kwargs - ) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) + super(ClubAdminandMembersForm, self).__init__(*args, prefix=prefix, **kwargs) class PasswordForm(FormRevMixin, ModelForm): """ Formulaire de changement brut de mot de passe. Ne pas utiliser sans traitement""" + class Meta: model = User - fields = ['password', 'pwd_ntlm'] + fields = ["password", "pwd_ntlm"] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(PasswordForm, self).__init__(*args, prefix=prefix, **kwargs) class ServiceUserForm(FormRevMixin, ModelForm): """Service user creation force initial password set""" + password = forms.CharField( label=_("New password"), max_length=255, validators=[MinLengthValidator(8)], widget=forms.PasswordInput, - required=True + required=True, ) class Meta: model = ServiceUser - fields = ('pseudo', 'access_group','comment') + fields = ("pseudo", "access_group", "comment") def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(ServiceUserForm, self).__init__(*args, prefix=prefix, **kwargs) def save(self, commit=True): """Password change""" user = super(ServiceUserForm, self).save(commit=False) - if self.cleaned_data['password']: + if self.cleaned_data["password"]: user.set_password(self.cleaned_data.get("password")) user.save() @@ -530,209 +551,225 @@ class ServiceUserForm(FormRevMixin, ModelForm): class EditServiceUserForm(ServiceUserForm): """Formulaire d'edition de base d'un service user. Ne permet d'editer que son group d'acl et son commentaire""" + password = forms.CharField( label=_("New password"), max_length=255, validators=[MinLengthValidator(8)], widget=forms.PasswordInput, - required=False + required=False, ) class Meta(ServiceUserForm.Meta): - fields = ['access_group', 'comment'] + fields = ["access_group", "comment"] class StateForm(FormRevMixin, ModelForm): """ Changement de l'état d'un user""" + class Meta: model = User - fields = ['state'] + fields = ["state"] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(StateForm, self).__init__(*args, prefix=prefix, **kwargs) def save(self, commit=True): user = super(StateForm, self).save(commit=False) - if self.cleaned_data['state']: - user.state=self.cleaned_data.get('state') + if self.cleaned_data["state"]: + user.state = self.cleaned_data.get("state") user.state_sync() user.save() + class GroupForm(FieldPermissionFormMixin, FormRevMixin, ModelForm): """ Gestion des groupes d'un user""" + groups = forms.ModelMultipleChoiceField( - Group.objects.all(), - widget=forms.CheckboxSelectMultiple, - required=False + Group.objects.all(), widget=forms.CheckboxSelectMultiple, required=False ) class Meta: model = User - fields = ['is_superuser', 'groups'] + fields = ["is_superuser", "groups"] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(GroupForm, self).__init__(*args, prefix=prefix, **kwargs) - if 'is_superuser' in self.fields: - self.fields['is_superuser'].label = _("Superuser") + if "is_superuser" in self.fields: + self.fields["is_superuser"].label = _("Superuser") class SchoolForm(FormRevMixin, ModelForm): """Edition, creation d'un école""" + class Meta: model = School - fields = ['name'] + fields = ["name"] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(SchoolForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['name'].label = _("School") + self.fields["name"].label = _("School") class ShellForm(FormRevMixin, ModelForm): """Edition, creation d'un école""" + class Meta: model = ListShell - fields = ['shell'] + fields = ["shell"] def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(ShellForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['shell'].label = _("Shell name") + self.fields["shell"].label = _("Shell name") class ListRightForm(FormRevMixin, ModelForm): """Edition, d'un groupe , équivalent à un droit Ne permet pas d'editer le gid, car il sert de primary key""" + permissions = forms.ModelMultipleChoiceField( - Permission.objects.all().select_related('content_type'), + Permission.objects.all().select_related("content_type"), widget=forms.CheckboxSelectMultiple, - required=False + required=False, ) class Meta: model = ListRight - fields = ('name', 'unix_name', 'critical', 'permissions', 'details') + fields = ("name", "unix_name", "critical", "permissions", "details") def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(ListRightForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['unix_name'].label = _("Name of the group of rights") + self.fields["unix_name"].label = _("Name of the group of rights") class NewListRightForm(ListRightForm): """Ajout d'un groupe/list de droit """ + class Meta(ListRightForm.Meta): - fields = ('name', 'unix_name', 'gid', 'critical', 'permissions', - 'details') + fields = ("name", "unix_name", "gid", "critical", "permissions", "details") def __init__(self, *args, **kwargs): super(NewListRightForm, self).__init__(*args, **kwargs) - self.fields['gid'].label = _("GID. Warning: this field must not be" - " edited after creation.") + self.fields["gid"].label = _( + "GID. Warning: this field must not be edited after creation." + ) class DelListRightForm(Form): """Suppression d'un ou plusieurs groupes""" + listrights = forms.ModelMultipleChoiceField( queryset=ListRight.objects.none(), label=_("Current groups of rights"), - widget=forms.CheckboxSelectMultiple + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) + instances = kwargs.pop("instances", None) super(DelListRightForm, self).__init__(*args, **kwargs) if instances: - self.fields['listrights'].queryset = instances + self.fields["listrights"].queryset = instances else: - self.fields['listrights'].queryset = ListRight.objects.all() + self.fields["listrights"].queryset = ListRight.objects.all() class DelSchoolForm(Form): """Suppression d'une ou plusieurs écoles""" + schools = forms.ModelMultipleChoiceField( queryset=School.objects.none(), label=_("Current schools"), - widget=forms.CheckboxSelectMultiple + widget=forms.CheckboxSelectMultiple, ) def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) + instances = kwargs.pop("instances", None) super(DelSchoolForm, self).__init__(*args, **kwargs) if instances: - self.fields['schools'].queryset = instances + self.fields["schools"].queryset = instances else: - self.fields['schools'].queryset = School.objects.all() + self.fields["schools"].queryset = School.objects.all() class BanForm(FormRevMixin, ModelForm): """Creation, edition d'un objet bannissement""" + def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(BanForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['date_end'].label = _("End date") - self.fields['date_end'].localize = False + self.fields["date_end"].label = _("End date") + self.fields["date_end"].localize = False class Meta: model = Ban - exclude = ['user'] - widgets = {'date_end':DateTimePicker} + exclude = ["user"] + widgets = {"date_end": DateTimePicker} class WhitelistForm(FormRevMixin, ModelForm): """Creation, edition d'un objet whitelist""" + def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(WhitelistForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['date_end'].label = _("End date") - self.fields['date_end'].localize = False + self.fields["date_end"].label = _("End date") + self.fields["date_end"].localize = False class Meta: model = Whitelist - exclude = ['user'] - widgets = {'date_end':DateTimePicker} + exclude = ["user"] + widgets = {"date_end": DateTimePicker} class EMailAddressForm(FormRevMixin, ModelForm): """Create and edit a local email address""" + def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(EMailAddressForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['local_part'].label = _("Local part of the email address") - self.fields['local_part'].help_text = _("Can't contain @") + self.fields["local_part"].label = _("Local part of the email address") + self.fields["local_part"].help_text = _("Can't contain @.") def clean_local_part(self): - return self.cleaned_data.get('local_part').lower() + return self.cleaned_data.get("local_part").lower() class Meta: model = EMailAddress - exclude = ['user'] + exclude = ["user"] class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): """Edit email-related settings""" + def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) + prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(EmailSettingsForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['email'].label = _("Main email address") - if 'local_email_redirect' in self.fields: - self.fields['local_email_redirect'].label = _("Redirect local emails") - if 'local_email_enabled' in self.fields: - self.fields['local_email_enabled'].label = _("Use local emails") + self.fields["email"].label = _("Main email address") + if "local_email_redirect" in self.fields: + self.fields["local_email_redirect"].label = _("Redirect local emails") + if "local_email_enabled" in self.fields: + self.fields["local_email_enabled"].label = _("Use local emails") def clean_email(self): - if not OptionalUser.objects.first().local_email_domain in self.cleaned_data.get('email'): - return self.cleaned_data.get('email').lower() + if not OptionalUser.objects.first().local_email_domain in self.cleaned_data.get( + "email" + ): + return self.cleaned_data.get("email").lower() else: raise forms.ValidationError( - _("You can't use a {} address.").format( - OptionalUser.objects.first().local_email_domain)) + _("You can't use a {} address.").format( + OptionalUser.objects.first().local_email_domain + ) + ) class Meta: model = User - fields = ['email','local_email_enabled', 'local_email_redirect'] + fields = ["email", "local_email_enabled", "local_email_redirect"] class InitialRegisterForm(forms.Form): @@ -740,16 +777,22 @@ class InitialRegisterForm(forms.Form): register_machine = forms.BooleanField(required=False) def __init__(self, *args, **kwargs): - switch_ip = kwargs.pop('switch_ip') - switch_port = kwargs.pop('switch_port') - client_mac = kwargs.pop('client_mac') - self.user = kwargs.pop('user') + switch_ip = kwargs.pop("switch_ip") + switch_port = kwargs.pop("switch_port") + client_mac = kwargs.pop("client_mac") + self.user = kwargs.pop("user") if switch_ip and switch_port: # Looking for a port - port = Port.objects.filter(switch__interface__ipv4__ipv4=switch_ip, port=switch_port).first() + port = Port.objects.filter( + switch__interface__ipv4__ipv4=switch_ip, port=switch_port + ).first() # If a port exists, checking there is a room AND radius if port: - if port.get_port_profile.radius_type != 'NO' and port.get_port_profile.radius_mode == 'STRICT' and hasattr(port, 'room'): + if ( + port.get_port_profile.radius_type != "NO" + and port.get_port_profile.radius_mode == "STRICT" + and hasattr(port, "room") + ): # Requesting user is not in this room ? if self.user.room != port.room: self.new_room = port.room @@ -757,19 +800,23 @@ class InitialRegisterForm(forms.Form): # If this interface doesn't already exists if not Interface.objects.filter(mac_address=client_mac): self.mac_address = client_mac - self.nas_type = Nas.objects.filter(nas_type__interface__ipv4__ipv4=switch_ip).first() + self.nas_type = Nas.objects.filter( + nas_type__interface__ipv4__ipv4=switch_ip + ).first() super(InitialRegisterForm, self).__init__(*args, **kwargs) - if hasattr(self, 'new_room'): - self.fields['register_room'].label = _("This room is my room") + if hasattr(self, "new_room"): + self.fields["register_room"].label = _("This room is my room") else: - self.fields.pop('register_room') - if hasattr(self, 'mac_address'): - self.fields['register_machine'].label = _("This new connected device is mine") + self.fields.pop("register_room") + if hasattr(self, "mac_address"): + self.fields["register_machine"].label = _( + "This new connected device is mine" + ) else: - self.fields.pop('register_machine') + self.fields.pop("register_machine") def clean_register_room(self): - if self.cleaned_data['register_room']: + if self.cleaned_data["register_room"]: if self.user.is_class_adherent: remove_user_room(self.new_room) user = self.user.adherent @@ -781,6 +828,6 @@ class InitialRegisterForm(forms.Form): user.save() def clean_register_machine(self): - if self.cleaned_data['register_machine']: + if self.cleaned_data["register_machine"]: if self.mac_address and self.nas_type: self.user.autoregister_machine(self.mac_address, self.nas_type) diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 965a1a8a..55780636 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-01-09 00:27+0100\n" +"POT-Creation-Date: 2019-11-19 23:43+0100\n" "PO-Revision-Date: 2018-06-27 23:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -31,121 +31,133 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: acl.py:41 +#: users/acl.py:44 msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: forms.py:76 +#: users/forms.py:78 msgid "Current password" msgstr "Mot de passe actuel" -#: forms.py:81 forms.py:507 forms.py:534 +#: users/forms.py:81 users/forms.py:528 users/forms.py:556 msgid "New password" msgstr "Nouveau mot de passe" -#: forms.py:87 +#: users/forms.py:87 msgid "New password confirmation" msgstr "Confirmation du nouveau mot de passe" -#: forms.py:104 +#: users/forms.py:103 msgid "The new passwords don't match." msgstr "Les nouveaux mots de passe ne correspondent pas." -#: forms.py:113 +#: users/forms.py:109 msgid "The current password is incorrect." msgstr "Le mot de passe actuel est incorrect." -#: forms.py:132 forms.py:185 models.py:1597 +#: users/forms.py:129 users/forms.py:186 users/models.py:1760 msgid "Password" msgstr "Mot de passe" -#: forms.py:138 forms.py:191 +#: users/forms.py:135 users/forms.py:189 msgid "Password confirmation" msgstr "Confirmation du mot de passe" -#: forms.py:143 forms.py:234 +#: users/forms.py:140 users/forms.py:229 msgid "Is admin" msgstr "Est admin" -#: forms.py:153 +#: users/forms.py:153 msgid "You can't use an internal address as your external address." msgstr "" "Vous ne pouvez pas utiliser une adresse interne pour votre adresse externe." -#: forms.py:166 forms.py:215 +#: users/forms.py:166 users/forms.py:209 msgid "The passwords don't match." msgstr "Les mots de passe ne correspondent pas." -#: forms.py:243 +#: users/forms.py:238 #, python-format msgid "User is admin: %s" msgstr "L'utilisateur est admin : %s" -#: forms.py:291 templates/users/aff_clubs.html:38 -#: templates/users/aff_listright.html:63 templates/users/aff_listright.html:168 -#: templates/users/aff_users.html:39 templates/users/profil.html:177 -#: templates/users/profil.html:331 templates/users/profil.html:350 +#: users/forms.py:284 users/templates/users/aff_clubs.html:38 +#: users/templates/users/aff_listright.html:63 +#: users/templates/users/aff_listright.html:168 +#: users/templates/users/aff_users.html:39 +#: users/templates/users/profil.html:178 users/templates/users/profil.html:342 +#: users/templates/users/profil.html:361 msgid "Username" msgstr "Pseudo" -#: forms.py:306 +#: users/forms.py:296 +msgid "Fully archive users? WARNING: CRITICAL OPERATION IF TRUE" +msgstr "" +"Complètement archiver les utilisateurs ? ATTENTION: OPÉRATION CRITIQUE SI OUI" + +#: users/forms.py:309 msgid "Impossible to archive users whose end access date is in the future." msgstr "" "Impossible d'archiver des utilisateurs dont la date de fin de connexion est " "dans le futur." -#: forms.py:318 templates/users/profil.html:166 templates/users/profil.html:330 -#: templates/users/profil.html:349 +#: users/forms.py:324 users/templates/users/profil.html:167 +#: users/templates/users/profil.html:341 users/templates/users/profil.html:360 msgid "First name" msgstr "Prénom" -#: forms.py:319 templates/users/aff_users.html:37 -#: templates/users/profil.html:172 templates/users/profil.html:329 -#: templates/users/profil.html:348 +#: users/forms.py:325 users/templates/users/aff_users.html:37 +#: users/templates/users/profil.html:173 users/templates/users/profil.html:340 +#: users/templates/users/profil.html:359 msgid "Surname" msgstr "Nom" -#: forms.py:320 forms.py:442 models.py:1598 -#: templates/users/aff_emailaddress.html:36 templates/users/profil.html:182 +#: users/forms.py:326 users/forms.py:466 users/models.py:1760 +#: users/templates/users/aff_emailaddress.html:36 +#: users/templates/users/profil.html:183 msgid "Email address" msgstr "Adresse mail" -#: forms.py:321 forms.py:440 forms.py:590 templates/users/aff_schools.html:37 -#: templates/users/profil.html:200 +#: users/forms.py:327 users/forms.py:464 users/forms.py:614 +#: users/templates/users/aff_schools.html:37 +#: users/templates/users/profil.html:204 msgid "School" msgstr "Établissement" -#: forms.py:322 forms.py:441 models.py:1251 -#: templates/users/aff_serviceusers.html:34 templates/users/profil.html:205 +#: users/forms.py:328 users/forms.py:465 +#: users/templates/users/aff_serviceusers.html:34 +#: users/templates/users/profil.html:209 msgid "Comment" msgstr "Commentaire" -#: forms.py:324 forms.py:444 templates/users/aff_clubs.html:40 -#: templates/users/aff_users.html:41 templates/users/profil.html:187 +#: users/forms.py:330 users/forms.py:468 +#: users/templates/users/aff_clubs.html:40 +#: users/templates/users/aff_users.html:41 +#: users/templates/users/profil.html:188 msgid "Room" msgstr "Chambre" -#: forms.py:325 forms.py:445 +#: users/forms.py:331 users/forms.py:469 msgid "No room" msgstr "Pas de chambre" -#: forms.py:326 forms.py:446 +#: users/forms.py:332 users/forms.py:470 msgid "Select a school" msgstr "Sélectionnez un établissement" -#: forms.py:342 +#: users/forms.py:348 msgid "Force the move?" msgstr "Forcer le déménagement ?" -#: forms.py:352 forms.py:730 +#: users/forms.py:358 users/forms.py:765 msgid "You can't use a {} address." msgstr "Vous ne pouvez pas utiliser une adresse {}." -#: forms.py:361 forms.py:470 +#: users/forms.py:368 users/forms.py:493 msgid "A valid telephone number is required." msgstr "Un numéro de téléphone valide est requis." -#: forms.py:379 +#: users/forms.py:387 msgid "" "If you already have an account, please use it. If your lost access to it, " "please consider using the forgotten password button on the login page or " @@ -156,279 +168,293 @@ msgstr "" "passe oublié est à votre disposition. Si vous avez oublié votre login, " "contactez le support." -#: forms.py:384 -msgid "I certify that I have not had an account before" -msgstr "Je certifie sur l'honneur ne pas déjà avoir de compte" +#: users/forms.py:394 +msgid "I certify that I have not had an account before." +msgstr "Je certifie sur l'honneur ne pas déjà avoir de compte." -#: forms.py:413 +#: users/forms.py:419 +msgid "I commit to accept the" +msgstr "J'accepte les" + +#: users/forms.py:421 +msgid "General Terms of Use" +msgstr "Conditions Générales d'Utilisation" + +#: users/forms.py:433 msgid "Leave empty if you don't have any GPG key." msgstr "Laissez vide si vous n'avez pas de clé GPG." -#: forms.py:415 +#: users/forms.py:436 msgid "Default shell" msgstr "Interface en ligne de commande par défaut" -#: forms.py:439 templates/users/aff_clubs.html:36 -#: templates/users/aff_serviceusers.html:32 +#: users/forms.py:463 users/templates/users/aff_clubs.html:36 +#: users/templates/users/aff_serviceusers.html:32 msgid "Name" msgstr "Nom" -#: forms.py:447 +#: users/forms.py:471 msgid "Use a mailing list" msgstr "Utiliser une liste de diffusion" -#: forms.py:578 templates/users/aff_listright.html:38 +#: users/forms.py:601 users/templates/users/aff_listright.html:38 msgid "Superuser" msgstr "Superutilisateur" -#: forms.py:602 +#: users/forms.py:627 msgid "Shell name" msgstr "Nom de l'interface en ligne de commande" -#: forms.py:621 +#: users/forms.py:647 msgid "Name of the group of rights" msgstr "Nom du groupe de droits" -#: forms.py:632 +#: users/forms.py:659 msgid "GID. Warning: this field must not be edited after creation." msgstr "GID. Attention : ce champ ne doit pas être modifié après création." -#: forms.py:640 +#: users/forms.py:668 msgid "Current groups of rights" msgstr "Groupes de droits actuels" -#: forms.py:657 +#: users/forms.py:686 msgid "Current schools" msgstr "Établissements actuels" -#: forms.py:675 forms.py:689 templates/users/aff_bans.html:41 -#: templates/users/aff_whitelists.html:41 +#: users/forms.py:705 users/forms.py:720 users/templates/users/aff_bans.html:41 +#: users/templates/users/aff_whitelists.html:41 msgid "End date" msgstr "Date de fin" -#: forms.py:703 models.py:1795 +#: users/forms.py:735 msgid "Local part of the email address" msgstr "Partie locale de l'adresse mail" -#: forms.py:704 -msgid "Can't contain @" -msgstr "Ne peut pas contenir @" +#: users/forms.py:736 +msgid "Can't contain @." +msgstr "Ne peut pas contenir @." -#: forms.py:719 +#: users/forms.py:752 msgid "Main email address" msgstr "Adresse mail principale" -#: forms.py:721 +#: users/forms.py:754 msgid "Redirect local emails" msgstr "Rediriger les mails locaux" -#: forms.py:723 +#: users/forms.py:756 msgid "Use local emails" msgstr "Utiliser les mails locaux" -#: forms.py:763 +#: users/forms.py:808 msgid "This room is my room" msgstr "Il s'agit bien de ma chambre" -#: forms.py:767 +#: users/forms.py:813 msgid "This new connected device is mine" msgstr "Ce nouvel appareil connecté m'appartient" -#: models.py:106 +#: users/models.py:105 #, python-format -msgid "The username '%(label)s' contains forbidden characters." -msgstr "Le pseudo '%(label)s' contient des caractères interdits." +msgid "The username \"%(label)s\" contains forbidden characters." +msgstr "Le pseudo « %(label)s » contient des caractères interdits." -#: models.py:148 +#: users/models.py:134 msgid "Users must have an username." msgstr "Les utilisateurs doivent avoir un pseudo." -#: models.py:151 +#: users/models.py:137 msgid "Username should only contain [a-z0-9-]." msgstr "Le pseudo devrait seulement contenir [a-z0-9-]" -#: models.py:192 templates/users/aff_clubs.html:55 -#: templates/users/aff_users.html:57 templates/users/profil.html:249 +#: users/models.py:180 users/templates/users/aff_clubs.html:55 +#: users/templates/users/aff_users.html:57 +#: users/templates/users/profil.html:253 msgid "Active" msgstr "Actif" -#: models.py:193 templates/users/aff_clubs.html:57 -#: templates/users/aff_users.html:59 templates/users/profil.html:251 -#: templates/users/profil.html:265 +#: users/models.py:181 users/templates/users/aff_clubs.html:57 +#: users/templates/users/aff_users.html:59 +#: users/templates/users/profil.html:255 users/templates/users/profil.html:271 msgid "Disabled" msgstr "Désactivé" -#: models.py:194 templates/users/profil.html:253 +#: users/models.py:182 users/templates/users/profil.html:257 msgid "Archived" msgstr "Archivé" -#: models.py:195 +#: users/models.py:183 users/templates/users/profil.html:259 msgid "Not yet active" msgstr "Pas encore adhéré" -#: models.py:202 models.py:1242 +#: users/models.py:184 users/templates/users/profil.html:261 +msgid "Fully archived" +msgstr "Complètement archivé" + +#: users/models.py:191 users/models.py:1416 msgid "Must only contain letters, numerals or dashes." msgstr "Doit seulement contenir des lettres, chiffres ou tirets." -#: models.py:208 +#: users/models.py:197 msgid "External email address allowing us to contact you." msgstr "Adresse mail externe nous permettant de vous contacter." -#: models.py:212 +#: users/models.py:202 msgid "" "Enable redirection of the local email messages to the main email address." msgstr "" "Activer la redirection des mails locaux vers l'adresse mail principale." -#: models.py:217 +#: users/models.py:207 msgid "Enable the local email account." msgstr "Activer le compte mail local" -#: models.py:232 -msgid "Comment, school year" -msgstr "Commentaire, promotion" +#: users/models.py:216 +msgid "Comment, school year." +msgstr "Commentaire, promotion." -#: models.py:258 +#: users/models.py:225 +msgid "enable shortcuts on Re2o website" +msgstr "activer les raccourcis sur le site de Re2o" + +#: users/models.py:235 msgid "Can change the password of a user" msgstr "Peut changer le mot de passe d'un utilisateur" -#: models.py:259 +#: users/models.py:236 msgid "Can edit the state of a user" msgstr "Peut changer l'état d'un utilisateur" -#: models.py:260 +#: users/models.py:237 msgid "Can force the move" msgstr "Peut forcer le déménagement" -#: models.py:261 +#: users/models.py:238 msgid "Can edit the shell of a user" msgstr "Peut modifier l'interface en ligne de commande d'un utilisateur" -#: models.py:263 +#: users/models.py:241 msgid "Can edit the groups of rights of a user (critical permission)" msgstr "" "Peut modifier les groupes de droits d'un utilisateur (permission critique)" -#: models.py:266 -msgid "Can edit all users, including those with rights." +#: users/models.py:243 +msgid "Can edit all users, including those with rights" msgstr "" -"Peut modifier tous les utilisateurs, y compris ceux possédant des droits." +"Peut modifier tous les utilisateurs, y compris ceux possédant des droits" -#: models.py:268 +#: users/models.py:244 msgid "Can view a user object" msgstr "Peut voir un objet utilisateur" -#: models.py:270 +#: users/models.py:246 msgid "user (member or club)" msgstr "utilisateur (adhérent ou club)" -#: models.py:271 +#: users/models.py:247 msgid "users (members or clubs)" msgstr "utilisateurs (adhérents ou clubs)" -#: models.py:289 models.py:313 +#: users/models.py:265 users/models.py:293 msgid "Unknown type." msgstr "Type inconnu." -#: models.py:309 templates/users/aff_listright.html:75 -#: templates/users/aff_listright.html:180 +#: users/models.py:289 users/templates/users/aff_listright.html:75 +#: users/templates/users/aff_listright.html:180 msgid "Member" msgstr "Adhérent" -#: models.py:311 +#: users/models.py:291 msgid "Club" msgstr "Club" -#: models.py:536 -msgid "IPv4 assigning" -msgstr "Attribution de l'IPv4" - -#: models.py:545 -msgid "IPv4 unassigning" -msgstr "Désattribution de l'IPv4" - -#: models.py:701 +#: users/models.py:781 msgid "Maximum number of registered machines reached." msgstr "Nombre maximum de machines enregistrées atteint." -#: models.py:703 +#: users/models.py:783 msgid "Re2o doesn't know wich machine type to assign." msgstr "Re2o ne sait pas quel type de machine attribuer." -#: models.py:726 templates/users/user_autocapture.html:64 +#: users/models.py:806 users/templates/users/user_autocapture.html:64 msgid "OK" msgstr "OK" -#: models.py:799 models.py:836 +#: users/models.py:878 +msgid "This user is archived." +msgstr "Cet utilisateur est archivé." + +#: users/models.py:892 users/models.py:946 msgid "You don't have the right to edit this club." msgstr "Vous n'avez pas le droit de modifier ce club." -#: models.py:807 +#: users/models.py:904 msgid "User with critical rights, can't be edited." msgstr "Utilisateur avec des droits critiques, ne peut être modifié." -#: models.py:810 +#: users/models.py:911 msgid "" -"Impossible to edit the organisation's user without the 'change_all_users' " +"Impossible to edit the organisation's user without the \"change_all_users\" " "right." msgstr "" -"Impossible de modifier l'utilisateur de l'association sans le droit " -"'change_all_users'." +"Impossible de modifier l'utilisateur de l'association sans le droit « " +"change_all_users »." -#: models.py:818 models.py:847 +#: users/models.py:923 users/models.py:961 msgid "You don't have the right to edit another user." msgstr "Vous n'avez pas le droit de modifier un autre utilisateur." -#: models.py:865 -msgid "Permission required to change the room." -msgstr "Permission requise pour changer la chambre." +#: users/models.py:987 +msgid "You don't have the right to change the room." +msgstr "Vous n'avez pas le droit de changer la chambre." -#: models.py:879 -msgid "Permission required to change the state." -msgstr "Permission requise pour changer l'état." +#: users/models.py:1004 +msgid "You don't have the right to change the state." +msgstr "Vous n'avez pas le droit de changer l'état." -#: models.py:891 -msgid "Permission required to change the shell." -msgstr "Permission requise pour changer l'interface en ligne de commande." +#: users/models.py:1024 +msgid "You don't have the right to change the shell." +msgstr "Vous n'avez pas le droit de changer l'interface en ligne de commande." -#: models.py:905 models.py:918 +#: users/models.py:1041 users/models.py:1056 msgid "Local email accounts must be enabled." msgstr "Les comptes mail locaux doivent être activés." -#: models.py:931 -msgid "Permission required to force the move." -msgstr "Permission requise pour forcer le déménagement." +#: users/models.py:1071 +msgid "You don't have the right to force the move." +msgstr "Vous n'avez pas le droit de forcer le déménagement." -#: models.py:944 -msgid "Permission required to edit the user's groups of rights." +#: users/models.py:1086 +msgid "You don't have the right to edit the user's groups of rights." msgstr "" -"Permission requise pour modifier les groupes de droits de l'utilisateur." +"Vous n'avez pas le droit de modifier les groupes de droits d'un autre " +"utilisateur." -#: models.py:956 -msgid "'superuser' right required to edit the superuser flag." -msgstr "Droit 'superuser' requis pour modifier le signalement superuser." +#: users/models.py:1102 +msgid "\"superuser\" right required to edit the superuser flag." +msgstr "Droit « superuser » requis pour modifier le signalement superuser." -#: models.py:974 +#: users/models.py:1127 msgid "You don't have the right to view this club." msgstr "Vous n'avez pas le droit de voir ce club." -#: models.py:980 +#: users/models.py:1136 msgid "You don't have the right to view another user." msgstr "Vous n'avez pas le droit de voir un autre utilisateur." -#: models.py:993 models.py:1174 +#: users/models.py:1151 users/models.py:1354 msgid "You don't have the right to view the list of users." msgstr "Vous n'avez pas le droit de voir la liste des utilisateurs." -#: models.py:1006 +#: users/models.py:1168 msgid "You don't have the right to delete this user." msgstr "Vous n'avez pas le droit de supprimer cet utilisateur." -#: models.py:1027 +#: users/models.py:1190 msgid "This username is already used." msgstr "Ce pseudo est déjà utilisé." -#: models.py:1029 +#: users/models.py:1198 msgid "" "There is neither a local email address nor an external email address for " "this user." @@ -436,7 +462,7 @@ msgstr "" "Il n'y a pas d'adresse mail locale ni d'adresse mail externe pour cet " "utilisateur." -#: models.py:1033 +#: users/models.py:1205 msgid "" "You can't redirect your local emails if no external email address has been " "set." @@ -444,178 +470,195 @@ msgstr "" "Vous ne pouvez pas rediriger vos mails locaux si aucune adresse mail externe " "n'a été définie." -#: models.py:1059 +#: users/models.py:1225 msgid "member" msgstr "adhérent" -#: models.py:1060 +#: users/models.py:1226 msgid "members" msgstr "adhérents" -#: models.py:1071 -msgid "A GPG fingerprint must contain 40 hexadecimal characters" -msgstr "Une empreinte GPG doit contenir 40 caractères hexadécimaux" +#: users/models.py:1243 +msgid "A GPG fingerprint must contain 40 hexadecimal characters." +msgstr "Une empreinte GPG doit contenir 40 caractères hexadécimaux." -#: models.py:1101 +#: users/models.py:1267 +msgid "Self registration is disabled." +msgstr "L'auto inscription est désactivée." + +#: users/models.py:1277 msgid "You don't have the right to create a user." msgstr "Vous n'avez pas le droit de créer un utilisateur." -#: models.py:1137 +#: users/models.py:1307 msgid "club" msgstr "club" -#: models.py:1138 +#: users/models.py:1308 msgid "clubs" msgstr "clubs" -#: models.py:1156 +#: users/models.py:1319 +msgid "You must be authenticated." +msgstr "Vous devez être authentifié." + +#: users/models.py:1327 msgid "You don't have the right to create a club." msgstr "Vous n'avez pas le droit de créer un club." -#: models.py:1261 +#: users/models.py:1420 +msgid "Comment." +msgstr "Commentaire." + +#: users/models.py:1426 msgid "Can view a service user object" msgstr "Peut voir un objet utilisateur service" -#: models.py:1263 +#: users/models.py:1427 users/views.py:332 msgid "service user" msgstr "utilisateur service" -#: models.py:1264 +#: users/models.py:1428 msgid "service users" msgstr "utilisateurs service" -#: models.py:1268 +#: users/models.py:1432 #, python-brace-format msgid "Service user <{name}>" msgstr "Utilisateur service <{name}>" -#: models.py:1330 +#: users/models.py:1499 msgid "Can view a school object" msgstr "Peut voir un objet établissement" -#: models.py:1332 +#: users/models.py:1500 msgid "school" msgstr "établissement" -#: models.py:1333 +#: users/models.py:1501 msgid "schools" msgstr "établissements" -#: models.py:1351 -msgid "UNIX groups can only contain lower case letters." -msgstr "Les groupes UNIX peuvent seulement contenir des lettres minuscules." +#: users/models.py:1520 +msgid "UNIX group names can only contain lower case letters." +msgstr "" +"Les noms de groupe UNIX peuvent seulement contenir des lettres minuscules." -#: models.py:1357 -msgid "Description" -msgstr "Description" +#: users/models.py:1526 +msgid "Description." +msgstr "Description." -#: models.py:1364 +#: users/models.py:1529 msgid "Can view a group of rights object" msgstr "Peut voir un objet groupe de droits" -#: models.py:1366 +#: users/models.py:1530 msgid "group of rights" msgstr "groupe de droits" -#: models.py:1367 +#: users/models.py:1531 msgid "groups of rights" msgstr "groupes de droits" -#: models.py:1414 +#: users/models.py:1576 msgid "Can view a shell object" msgstr "Peut voir un objet interface en ligne de commande" -#: models.py:1416 +#: users/models.py:1577 users/views.py:643 msgid "shell" msgstr "interface en ligne de commande" -#: models.py:1417 +#: users/models.py:1578 msgid "shells" msgstr "interfaces en ligne de commande" -#: models.py:1436 +#: users/models.py:1596 msgid "HARD (no access)" msgstr "HARD (pas d'accès)" -#: models.py:1437 +#: users/models.py:1597 msgid "SOFT (local access only)" msgstr "SOFT (accès local uniquement)" -#: models.py:1438 +#: users/models.py:1598 msgid "RESTRICTED (speed limitation)" msgstr "RESTRICTED (limitation de vitesse)" -#: models.py:1449 +#: users/models.py:1608 msgid "Can view a ban object" msgstr "Peut voir un objet bannissement" -#: models.py:1451 +#: users/models.py:1609 users/views.py:383 msgid "ban" msgstr "bannissement" -#: models.py:1452 +#: users/models.py:1610 msgid "bans" msgstr "bannissements" -#: models.py:1486 -msgid "You don't have the right to view bans other than yours." +#: users/models.py:1645 +msgid "You don't have the right to view other bans than yours." msgstr "" -"Vous n'avez pas le droit de voir des bannissements autres que les vôtres." +"Vous n'avez pas le droit de voir d'autres bannissements que les vôtres." -#: models.py:1534 +#: users/models.py:1693 msgid "Can view a whitelist object" msgstr "Peut voir un objet accès gracieux" -#: models.py:1536 +#: users/models.py:1694 msgid "whitelist (free of charge access)" msgstr "Accès gracieux" -#: models.py:1537 +#: users/models.py:1695 msgid "whitelists (free of charge access)" msgstr "Accès gracieux" -#: models.py:1553 -msgid "You don't have the right to view whitelists other than yours." +#: users/models.py:1715 +msgid "You don't have the right to view other whitelists than yours." msgstr "" -"Vous n'avez pas le droit de voir des accès gracieux autres que les vôtres." +"Vous n'avez pas le droit de voir d'autres accès gracieux que les vôtres." -#: models.py:1790 -msgid "User of the local email account" -msgstr "Utilisateur du compte mail local" +#: users/models.py:1912 +msgid "User of the local email account." +msgstr "Utilisateur du compte mail local." -#: models.py:1800 +#: users/models.py:1915 +msgid "Local part of the email address." +msgstr "Partie locale de l'adresse mail." + +#: users/models.py:1920 msgid "Can view a local email account object" msgstr "Peut voir un objet compte mail local" -#: models.py:1802 +#: users/models.py:1922 msgid "local email account" msgstr "compte mail local" -#: models.py:1803 +#: users/models.py:1923 msgid "local email accounts" msgstr "comptes mail locaux" -#: models.py:1827 models.py:1850 models.py:1872 models.py:1894 +#: users/models.py:1951 users/models.py:1986 users/models.py:2020 +#: users/models.py:2054 msgid "The local email accounts are not enabled." msgstr "Les comptes mail locaux ne sont pas activés." -#: models.py:1829 +#: users/models.py:1956 msgid "You don't have the right to add a local email account to another user." msgstr "" "Vous n'avez pas le droit d'ajouter un compte mail local à un autre " "utilisateur." -#: models.py:1832 +#: users/models.py:1966 msgid "You reached the limit of {} local email accounts." msgstr "Vous avez atteint la limite de {} comptes mail locaux." -#: models.py:1853 models.py:1897 -msgid "You don't have the right to edit another user's local email account." +#: users/models.py:1992 +msgid "You don't have the right to view another user's local email account." msgstr "" -"Vous n'avez pas le droit de modifier le compte mail local d'un autre " -"utilisateur." +"Vous n'avez pas le droit de voir le compte mail local d'un autre utilisateur." -#: models.py:1867 +#: users/models.py:2012 msgid "" "You can't delete a local email account whose local part is the same as the " "username." @@ -623,13 +666,13 @@ msgstr "" "Vous ne pouvez pas supprimer un compte mail local dont la partie locale est " "la même que le pseudo." -#: models.py:1875 -msgid "You don't have the right to delete another user's local email account" +#: users/models.py:2026 +msgid "You don't have the right to delete another user's local email account." msgstr "" "Vous n'avez pas le droit de supprimer le compte mail local d'un autre " "utilisateur." -#: models.py:1889 +#: users/models.py:2046 msgid "" "You can't edit a local email account whose local part is the same as the " "username." @@ -637,135 +680,160 @@ msgstr "" "Vous ne pouvez pas modifier un compte mail local dont la partie locale est " "la même que le pseudo." -#: models.py:1903 +#: users/models.py:2060 +msgid "You don't have the right to edit another user's local email account." +msgstr "" +"Vous n'avez pas le droit de modifier le compte mail local d'un autre " +"utilisateur." + +#: users/models.py:2069 msgid "The local part must not contain @ or +." msgstr "La partie locale ne doit pas contenir @ ou +." -#: templates/users/aff_bans.html:36 templates/users/aff_whitelists.html:36 +#: users/templates/users/aff_bans.html:36 +#: users/templates/users/aff_whitelists.html:36 msgid "User" msgstr "Utilisateur" -#: templates/users/aff_bans.html:38 templates/users/aff_whitelists.html:38 +#: users/templates/users/aff_bans.html:38 +#: users/templates/users/aff_whitelists.html:38 msgid "Reason" msgstr "Raison" -#: templates/users/aff_bans.html:39 templates/users/aff_whitelists.html:39 +#: users/templates/users/aff_bans.html:39 +#: users/templates/users/aff_whitelists.html:39 msgid "Start date" msgstr "Date de début" -#: templates/users/aff_clubs.html:42 templates/users/aff_users.html:43 +#: users/templates/users/aff_clubs.html:42 +#: users/templates/users/aff_users.html:43 msgid "End of subscription on" msgstr "Fin de cotisation le" -#: templates/users/aff_clubs.html:43 templates/users/aff_users.html:44 -#: templates/users/profil.html:260 +#: users/templates/users/aff_clubs.html:43 +#: users/templates/users/aff_users.html:44 +#: users/templates/users/profil.html:266 msgid "Internet access" msgstr "Accès Internet" -#: templates/users/aff_clubs.html:44 templates/users/aff_users.html:45 -#: templates/users/profil.html:31 +#: users/templates/users/aff_clubs.html:44 +#: users/templates/users/aff_users.html:45 users/templates/users/profil.html:31 msgid "Profile" msgstr "Profil" -#: templates/users/aff_clubs.html:53 templates/users/aff_users.html:54 -#: templates/users/profil.html:224 +#: users/templates/users/aff_clubs.html:53 +#: users/templates/users/aff_users.html:54 +#: users/templates/users/profil.html:228 msgid "Not a member" msgstr "Non adhérent" -#: templates/users/aff_listright.html:40 -msgid "Django's specific pre-defined right that supersed any other rights." +#: users/templates/users/aff_listright.html:40 +msgid "Django's specific pre-defined right that supersedes any other rights." msgstr "" "Droit prédéfini spécifique à Django qui outrepasse tous les autres droits." -#: templates/users/aff_listright.html:44 +#: users/templates/users/aff_listright.html:44 msgid "Total: all permissions" msgstr "Total: toutes les permissions" -#: templates/users/aff_listright.html:49 templates/users/aff_listright.html:56 +#: users/templates/users/aff_listright.html:49 +#: users/templates/users/aff_listright.html:56 msgid "Users in Superuser" msgstr "Utilisateurs dans Superuser" -#: templates/users/aff_listright.html:53 templates/users/aff_listright.html:154 -#: widgets.py:35 +#: users/templates/users/aff_listright.html:53 +#: users/templates/users/aff_listright.html:154 users/widgets.py:38 msgid "Close" msgstr "Fermer" -#: templates/users/aff_listright.html:64 templates/users/aff_listright.html:169 +#: users/templates/users/aff_listright.html:64 +#: users/templates/users/aff_listright.html:169 msgid "Membership" msgstr "Adhésion" -#: templates/users/aff_listright.html:65 templates/users/aff_listright.html:170 +#: users/templates/users/aff_listright.html:65 +#: users/templates/users/aff_listright.html:170 msgid "Last seen" msgstr "Dernière connexion" -#: templates/users/aff_listright.html:66 templates/users/aff_listright.html:171 +#: users/templates/users/aff_listright.html:66 +#: users/templates/users/aff_listright.html:171 msgid "Actions" msgstr "Actions" -#: templates/users/aff_listright.html:67 templates/users/aff_listright.html:172 +#: users/templates/users/aff_listright.html:67 +#: users/templates/users/aff_listright.html:172 msgid "Last action" msgstr "Dernière action" -#: templates/users/aff_listright.html:77 templates/users/aff_listright.html:182 +#: users/templates/users/aff_listright.html:77 +#: users/templates/users/aff_listright.html:182 msgid "No membership records" msgstr "Aucune adhésion enregistrée" -#: templates/users/aff_listright.html:80 templates/users/aff_listright.html:185 +#: users/templates/users/aff_listright.html:80 +#: users/templates/users/aff_listright.html:185 #, python-format msgid "Not since %(end_date)s" msgstr "Plus depuis %(end_date)s" -#: templates/users/aff_listright.html:88 templates/users/aff_listright.html:193 +#: users/templates/users/aff_listright.html:88 +#: users/templates/users/aff_listright.html:193 msgid "Never" msgstr "Jamais" -#: templates/users/aff_listright.html:123 +#: users/templates/users/aff_listright.html:123 #, python-format msgid "%(right_name)s (gid: %(right_gid)s)" msgstr "%(right_name)s (gid: %(right_gid)s)" -#: templates/users/aff_listright.html:132 +#: users/templates/users/aff_listright.html:132 #, python-format msgid "Total: %(perm_count)s permission" msgid_plural "Total: %(perm_count)s permissions" msgstr[0] "Total: %(perm_count)s permission" msgstr[1] "Total: %(perm_count)s permissions" -#: templates/users/aff_listright.html:150 templates/users/index.html:29 -#: templates/users/index.html:32 templates/users/index_ban.html:29 -#: templates/users/index_clubs.html:29 -#: templates/users/index_emailaddress.html:29 -#: templates/users/index_listright.html:30 templates/users/index_rights.html:29 -#: templates/users/index_schools.html:30 -#: templates/users/index_serviceusers.html:30 -#: templates/users/index_shell.html:30 templates/users/index_whitelist.html:29 -#: templates/users/plugin_out.html:31 templates/users/sidebar.html:52 -#: templates/users/user.html:30 templates/users/user_autocapture.html:30 +#: users/templates/users/aff_listright.html:150 +#: users/templates/users/index.html:29 users/templates/users/index.html:32 +#: users/templates/users/index_ban.html:29 +#: users/templates/users/index_clubs.html:29 +#: users/templates/users/index_emailaddress.html:29 +#: users/templates/users/index_listright.html:30 +#: users/templates/users/index_rights.html:29 +#: users/templates/users/index_schools.html:30 +#: users/templates/users/index_serviceusers.html:30 +#: users/templates/users/index_shell.html:30 +#: users/templates/users/index_whitelist.html:29 +#: users/templates/users/plugin_out.html:31 +#: users/templates/users/sidebar.html:52 users/templates/users/user.html:30 +#: users/templates/users/user_autocapture.html:30 msgid "Users" msgstr "Utilisateurs" -#: templates/users/aff_listright.html:158 +#: users/templates/users/aff_listright.html:158 #, python-format msgid "Users in %(right_name)s" msgstr "Utilisateurs dans %(right_name)s" -#: templates/users/aff_serviceusers.html:33 -msgid "Role" -msgstr "Rôle" +#: users/templates/users/aff_serviceusers.html:33 +msgid "Access group" +msgstr "Group d'accès" -#: templates/users/aff_shell.html:32 templates/users/profil.html:301 +#: users/templates/users/aff_shell.html:32 +#: users/templates/users/profil.html:307 msgid "Shell" msgstr "Interface en ligne de commande" -#: templates/users/aff_users.html:35 +#: users/templates/users/aff_users.html:35 msgid "Firt name" msgstr "Prénom" -#: templates/users/delete.html:29 +#: users/templates/users/delete.html:29 msgid "Deletion of users" msgstr "Suppression d'utilisateurs" -#: templates/users/delete.html:35 +#: users/templates/users/delete.html:35 #, python-format msgid "" "Warning: are you sure you want to delete this %(objet_name)s object " @@ -774,60 +842,62 @@ msgstr "" "Attention : voulez-vous vraiment supprimer cet objet %(objet_name)s " "( %(objet)s ) ?" -#: templates/users/delete.html:36 templates/users/mass_archive.html:36 +#: users/templates/users/delete.html:36 +#: users/templates/users/mass_archive.html:36 users/views.py:600 +#: users/views.py:704 msgid "Confirm" msgstr "Confirmer" -#: templates/users/index_ban.html:32 templates/users/profil.html:427 -#: templates/users/sidebar.html:58 +#: users/templates/users/index_ban.html:32 +#: users/templates/users/profil.html:438 users/templates/users/sidebar.html:58 msgid "Bans" msgstr "Bannissements" -#: templates/users/index_clubs.html:32 +#: users/templates/users/index_clubs.html:32 msgid "Clubs" msgstr "Clubs" -#: templates/users/index_emailaddress.html:32 +#: users/templates/users/index_emailaddress.html:32 msgid "Local email accounts" msgstr "Comptes mail locaux" -#: templates/users/index_listright.html:33 +#: users/templates/users/index_listright.html:33 msgid "List of groups of rights" msgstr "Liste des groupes de droits" -#: templates/users/index_listright.html:35 -msgid " Add a group of rights" -msgstr " Ajouter un groupe de droits" +#: users/templates/users/index_listright.html:35 +msgid "Add a group of rights" +msgstr "Ajouter un groupe de droits" -#: templates/users/index_listright.html:37 -msgid " Delete one or several groups of rights" -msgstr " Supprimer un ou plusieurs groupes de droits" +#: users/templates/users/index_listright.html:37 +msgid "Delete one or several groups of rights" +msgstr "Supprimer un ou plusieurs groupes de droits" -#: templates/users/index_rights.html:32 +#: users/templates/users/index_rights.html:32 msgid "Rights" msgstr "Droits" -#: templates/users/index_schools.html:33 +#: users/templates/users/index_schools.html:33 msgid "List of schools" msgstr "Liste des établissements" -#: templates/users/index_schools.html:34 -msgid "List of schools for created users" -msgstr "Liste des établissements pour les utilisateurs créés" +#: users/templates/users/index_schools.html:34 +msgid "List of schools for registered users" +msgstr "Liste des établissements pour les utilisateurs enregistrés" -#: templates/users/index_schools.html:36 -msgid " Add a school" -msgstr " Ajouter un établissement" +#: users/templates/users/index_schools.html:36 +msgid "Add a school" +msgstr "Ajouter un établissement" -#: templates/users/index_schools.html:38 -msgid " Delete one or several schools" -msgstr " Supprimer un ou plusieurs établissements" +#: users/templates/users/index_schools.html:38 +msgid "Delete one or several schools" +msgstr "Supprimer un ou plusieurs établissements" -#: templates/users/index_serviceusers.html:33 +#: users/templates/users/index_serviceusers.html:33 msgid "List of LDAP service users" msgstr "Liste des utilisateurs service LDAP" -#: templates/users/index_serviceusers.html:34 +#: users/templates/users/index_serviceusers.html:34 msgid "" "The LDAP service users are special users having access only to the LDAP for " "authentication operations. It is recommended to create a service user with a " @@ -837,38 +907,36 @@ msgstr "" "seulement au LDAP pour des opérations d'authentification. Il est recommandé " "de créer un service utilisateur avec un indentifiant et un mot de passe." -#: templates/users/index_serviceusers.html:36 -msgid " Add a service user" -msgstr " Ajouter un utilisateur" +#: users/templates/users/index_serviceusers.html:36 +msgid "Add a service user" +msgstr "Ajouter un utilisateur" -#: templates/users/index_shell.html:33 +#: users/templates/users/index_shell.html:33 msgid "List of shells" msgstr "Liste des interfaces en ligne de commande" -#: templates/users/index_shell.html:35 -msgid " Add a shell" -msgstr " Ajouter une interface en ligne de commande" +#: users/templates/users/index_shell.html:35 +msgid "Add a shell" +msgstr "Ajouter une interface en ligne de commande" -#: templates/users/index_whitelist.html:32 templates/users/profil.html:452 -#: templates/users/sidebar.html:64 +#: users/templates/users/index_whitelist.html:32 +#: users/templates/users/profil.html:463 users/templates/users/sidebar.html:64 msgid "Whitelists" msgstr "Accès gracieux" -#: templates/users/mass_archive.html:29 +#: users/templates/users/mass_archive.html:29 msgid "Users to archive" msgstr "Utilisateurs à archiver" -#: templates/users/mass_archive.html:35 +#: users/templates/users/mass_archive.html:35 msgid "Search" msgstr "Rechercher" -#: templates/users/mass_archive.html:39 -#, python-format -msgid "The following users will be archived (%(to_archive_list|length)s):" -msgstr "" -"Les utilisateus suivants vont être archivés (%(to_archive_list|length)s) :" +#: users/templates/users/mass_archive.html:39 +msgid "The following users will be archived:" +msgstr "Les utilisateus suivants vont être archivés :" -#: templates/users/plugin_out.html:35 +#: users/templates/users/plugin_out.html:35 msgid "" "Your machine and your room were successfully registered. Please disconnect " "and reconnect your Ethernet cable to benefit from a wired connection." @@ -877,479 +945,444 @@ msgstr "" "débrancher et rebrancher votre câble Ethernet pour bénéficier d'une " "connexion filaire." -#: templates/users/profil.html:36 +#: users/templates/users/profil.html:36 #, python-format msgid "Welcome %(name)s %(surname)s" msgstr "Bienvenue %(name)s %(surname)s" -#: templates/users/profil.html:38 +#: users/templates/users/profil.html:38 #, python-format msgid "Profile of %(name)s %(surname)s" msgstr "Profil de %(name)s %(surname)s" -#: templates/users/profil.html:46 -msgid "Your account has been banned" -msgstr "Votre compte a été banni" +#: users/templates/users/profil.html:46 +msgid "Your account has been banned." +msgstr "Votre compte a été banni." -#: templates/users/profil.html:48 +#: users/templates/users/profil.html:48 #, python-format msgid "End of the ban: %(end_ban_date)s" msgstr "Fin du bannissement : %(end_ban_date)s" -#: templates/users/profil.html:53 +#: users/templates/users/profil.html:53 msgid "No connection" msgstr "Pas de connexion" -#: templates/users/profil.html:57 +#: users/templates/users/profil.html:57 msgid "Pay for a connection" msgstr "Payer une connexion" -#: templates/users/profil.html:60 -msgid "Ask for someone with the appropriate rights to pay for a connection." +#: users/templates/users/profil.html:60 +msgid "Ask someone with the appropriate rights to pay for a connection." msgstr "" "Demandez à quelqu'un ayant les droits appropriés de payer une connexion." -#: templates/users/profil.html:66 +#: users/templates/users/profil.html:66 #, python-format msgid "Connection (until %(end_connection_date)s )" msgstr "Connexion (jusqu'au %(end_connection_date)s)" -#: templates/users/profil.html:70 +#: users/templates/users/profil.html:70 msgid "Extend the connection period" msgstr "Étendre la durée de connexion" -#: templates/users/profil.html:86 +#: users/templates/users/profil.html:86 msgid "Refill the balance" msgstr "Recharger le solde" -#: templates/users/profil.html:97 -msgid " Machines" -msgstr " Machines" +#: users/templates/users/profil.html:97 users/templates/users/profil.html:382 +msgid "Machines" +msgstr "Machines" -#: templates/users/profil.html:101 templates/users/profil.html:113 -msgid " Add a machine" -msgstr " Ajouter une machine" +#: users/templates/users/profil.html:101 users/templates/users/profil.html:113 +#: users/templates/users/profil.html:390 +msgid "Add a machine" +msgstr "Ajouter une machine" -#: templates/users/profil.html:109 templates/users/profil.html:386 +#: users/templates/users/profil.html:109 users/templates/users/profil.html:397 msgid "No machine" msgstr "Pas de machine" -#: templates/users/profil.html:127 -msgid " Detailed information" -msgstr " Informations détaillées" +#: users/templates/users/profil.html:128 +msgid "Detailed information" +msgstr "Informations détaillées" -#: templates/users/profil.html:134 +#: users/templates/users/profil.html:135 users/views.py:184 users/views.py:211 +#: users/views.py:228 users/views.py:245 users/views.py:317 users/views.py:371 +#: users/views.py:425 users/views.py:490 users/views.py:533 users/views.py:569 +#: users/views.py:629 users/views.py:675 msgid "Edit" msgstr "Modifier" -#: templates/users/profil.html:138 views.py:286 views.py:1084 +#: users/templates/users/profil.html:139 users/views.py:264 users/views.py:1001 msgid "Change the password" msgstr "Changer le mot de passe" -#: templates/users/profil.html:143 +#: users/templates/users/profil.html:144 msgid "Change the state" msgstr "Changer l'état" -#: templates/users/profil.html:149 views.py:264 +#: users/templates/users/profil.html:150 msgid "Edit the groups" msgstr "Modifier les groupes" -#: templates/users/profil.html:159 +#: users/templates/users/profil.html:160 msgid "Mailing" msgstr "Envoi de mails" -#: templates/users/profil.html:163 +#: users/templates/users/profil.html:164 msgid "Mailing disabled" msgstr "Envoi de mails désactivé" -#: templates/users/profil.html:195 +#: users/templates/users/profil.html:192 +msgid "Connected" +msgstr "Connecté" + +#: users/templates/users/profil.html:193 +msgid "Pending connection..." +msgstr "Connexion en attente..." + +#: users/templates/users/profil.html:199 msgid "Telephone number" msgstr "Numéro de téléphone" -#: templates/users/profil.html:210 +#: users/templates/users/profil.html:214 msgid "Registration date" msgstr "Date d'inscription" -#: templates/users/profil.html:215 +#: users/templates/users/profil.html:219 msgid "Last login" msgstr "Dernière connexion" -#: templates/users/profil.html:220 +#: users/templates/users/profil.html:224 msgid "End of membership" msgstr "Fin d'adhésion" -#: templates/users/profil.html:229 +#: users/templates/users/profil.html:233 msgid "Whitelist" msgstr "Accès gracieux" -#: templates/users/profil.html:233 templates/users/profil.html:274 +#: users/templates/users/profil.html:237 users/templates/users/profil.html:280 msgid "None" msgstr "Aucun" -#: templates/users/profil.html:238 +#: users/templates/users/profil.html:242 msgid "Ban" msgstr "Bannissement" -#: templates/users/profil.html:242 +#: users/templates/users/profil.html:246 msgid "Not banned" msgstr "Non banni" -#: templates/users/profil.html:247 +#: users/templates/users/profil.html:251 msgid "State" msgstr "État" -#: templates/users/profil.html:255 -msgid "Not yet member" -msgstr "Pas encore adhérent" - -#: templates/users/profil.html:263 +#: users/templates/users/profil.html:269 #, python-format msgid "Active (until %(end_access)s)" msgstr "Actif (jusqu'au %(end_access)s)" -#: templates/users/profil.html:270 templates/users/sidebar.html:82 +#: users/templates/users/profil.html:276 users/templates/users/sidebar.html:82 msgid "Groups of rights" msgstr "Groupes de droits" -#: templates/users/profil.html:279 +#: users/templates/users/profil.html:285 msgid "Balance" msgstr "Solde" -#: templates/users/profil.html:286 +#: users/templates/users/profil.html:292 msgid "Refill" msgstr "Recharger" -#: templates/users/profil.html:294 +#: users/templates/users/profil.html:300 msgid "GPG fingerprint" msgstr "Empreinte GPG" -#: templates/users/profil.html:313 -msgid " Manage the club" -msgstr " Gérer le club" +#: users/templates/users/profil.html:312 +msgid "Shortcuts enabled" +msgstr "Raccourcis activés" -#: templates/users/profil.html:320 +#: users/templates/users/profil.html:323 +msgid "Manage the club" +msgstr "Gérer le club" + +#: users/templates/users/profil.html:331 msgid "Manage the admins and members" msgstr "Gérer les admins et les membres" -#: templates/users/profil.html:324 +#: users/templates/users/profil.html:335 msgid "Club admins" msgstr "Admins du clubs" -#: templates/users/profil.html:343 +#: users/templates/users/profil.html:354 msgid "Members" msgstr "Adhérents" -#: templates/users/profil.html:371 -msgid "Machines" -msgstr "Machines" - -#: templates/users/profil.html:379 -msgid "Add a machine" -msgstr "Ajouter une machine" - -#: templates/users/profil.html:396 +#: users/templates/users/profil.html:407 msgid "Subscriptions" msgstr "Cotisations" -#: templates/users/profil.html:404 +#: users/templates/users/profil.html:415 msgid "Add a subscription" msgstr "Ajouter une cotisation" -#: templates/users/profil.html:409 +#: users/templates/users/profil.html:420 msgid "Edit the balance" msgstr "Modifier le solde" -#: templates/users/profil.html:418 +#: users/templates/users/profil.html:429 msgid "No invoice" msgstr "Pas de facture" -#: templates/users/profil.html:435 views.py:388 +#: users/templates/users/profil.html:446 msgid "Add a ban" msgstr "Ajouter un bannissement" -#: templates/users/profil.html:443 +#: users/templates/users/profil.html:454 msgid "No ban" msgstr "Pas de bannissement" -#: templates/users/profil.html:460 +#: users/templates/users/profil.html:471 msgid "Grant a whitelist" msgstr "Donner un accès gracieux" -#: templates/users/profil.html:468 +#: users/templates/users/profil.html:479 msgid "No whitelist" msgstr "Pas d'accès gracieux" -#: templates/users/profil.html:476 -msgid " Email settings" -msgstr " Paramètres mail" +#: users/templates/users/profil.html:487 +msgid "Email settings" +msgstr "Paramètres mail" -#: templates/users/profil.html:483 -msgid " Edit email settings" -msgstr " Modifier les paramètres mail" +#: users/templates/users/profil.html:494 +msgid "Edit email settings" +msgstr "Modifier les paramètres mail" -#: templates/users/profil.html:492 templates/users/profil.html:518 +#: users/templates/users/profil.html:503 users/templates/users/profil.html:529 msgid "Contact email address" msgstr "Adresse mail de contact" -#: templates/users/profil.html:496 +#: users/templates/users/profil.html:507 msgid "Enable the local email account" msgstr "Activer le compte mail local" -#: templates/users/profil.html:498 +#: users/templates/users/profil.html:509 msgid "Enable the local email redirection" msgstr "Activer la redirection mail locale" -#: templates/users/profil.html:502 +#: users/templates/users/profil.html:513 msgid "" -"The contact email address is the email address where we send emails to " +"The contact email address is the email address to which we send emails to " "contact you. If you would like to use your external email address for that, " "you can either disable your local email address or enable the local email " "redirection." msgstr "" -"L'adresse mail de contact est l'adresse mail où nous envoyons des mails pour " -"vous contacter. Si vous voulez utiliser votre adresse mail externe pour " -"cela, vous pouvez soit désactiver votre adresse mail locale soit activer la " -"redirection des mails locaux." +"L'adresse mail de contact est l'adresse mail à laquelle nous envoyons des " +"mails pour vous contacter. Si vous voulez utiliser votre adresse mail " +"externe pour cela, vous pouvez soit désactiver votre adresse mail locale " +"soit activer la redirection des mails locaux." -#: templates/users/profil.html:507 -msgid " Add an email address" -msgstr " Ajouter une adresse mail" +#: users/templates/users/profil.html:518 +msgid "Add an email address" +msgstr "Ajouter une adresse mail" -#: templates/users/sidebar.html:33 +#: users/templates/users/sidebar.html:33 msgid "Create a club or organisation" msgstr "Créer un club ou une association" -#: templates/users/sidebar.html:39 +#: users/templates/users/sidebar.html:39 msgid "Create a user" msgstr "Créer un utilisateur" -#: templates/users/sidebar.html:46 +#: users/templates/users/sidebar.html:46 msgid "Clubs and organisations" msgstr "Clubs et associations" -#: templates/users/sidebar.html:70 +#: users/templates/users/sidebar.html:70 msgid "Schools" msgstr "Établissements" -#: templates/users/sidebar.html:76 +#: users/templates/users/sidebar.html:76 msgid "Shells" msgstr "Interfaces en ligne de commande" -#: templates/users/sidebar.html:88 +#: users/templates/users/sidebar.html:88 msgid "Service users" msgstr "Utilisateurs service" -#: templates/users/sidebar.html:94 +#: users/templates/users/sidebar.html:94 msgid "Massively archive" msgstr "Archiver en masse" -#: templates/users/user.html:45 +#: users/templates/users/user.html:45 msgid "Summary of the General Terms of Use" msgstr "Résumé des Conditions Générales d'Utilisation" -#: templates/users/user_autocapture.html:35 +#: users/templates/users/user_autocapture.html:35 msgid "Device and room register form" msgstr "Enregistrement de votre chambre et machine fixe" -#: templates/users/user_autocapture.html:44 +#: users/templates/users/user_autocapture.html:44 msgid "Connected from:" msgstr "Connecté depuis :" -#: templates/users/user_autocapture.html:46 +#: users/templates/users/user_autocapture.html:46 #, python-format msgid "Room %(room)s" msgstr "Chambre %(room)s" -#: templates/users/user_autocapture.html:47 +#: users/templates/users/user_autocapture.html:47 #, python-format msgid "Port %(port)s" msgstr "Port %(port)s" -#: templates/users/user_autocapture.html:54 +#: users/templates/users/user_autocapture.html:54 msgid "Connected with device:" msgstr "Connecté avec l'appareil :" -#: templates/users/user_autocapture.html:56 +#: users/templates/users/user_autocapture.html:56 #, python-format msgid "MAC address %(mac)s" msgstr "Adresse MAC %(mac)s" -#: views.py:126 +#: users/views.py:127 #, python-format msgid "The user %s was created, an email to set the password was sent." msgstr "" "L'utilisateur %s a été créé, un mail pour initialiser le mot de passe a été " "envoyé." -#: views.py:138 +#: users/views.py:137 msgid "Commit" msgstr "Valider" -#: views.py:155 +#: users/views.py:156 #, python-format msgid "The club %s was created, an email to set the password was sent." msgstr "" "Le club %s a été créé, un mail pour initialiser le mot de passe a été envoyé." -#: views.py:162 +#: users/views.py:161 msgid "Create a club" msgstr "Créer un club" -#: views.py:180 +#: users/views.py:176 msgid "The club was edited." msgstr "Le club a été modifié." -#: views.py:189 -msgid "Edit the admins and members" -msgstr "Modifier les admins et les membres" - -#: views.py:217 +#: users/views.py:208 msgid "The user was edited." msgstr "L'utilisateur a été modifié." -#: views.py:223 -msgid "Edit the user" -msgstr "Modifier l'utilisateur" - -#: views.py:237 +#: users/views.py:225 msgid "The state was edited." msgstr "L'état a été modifié." -#: views.py:243 -msgid "Edit the state" -msgstr "Modifier l'état" - -#: views.py:258 +#: users/views.py:242 msgid "The groups were edited." msgstr "Les groupes ont été modifiés." -#: views.py:280 views.py:1081 +#: users/views.py:261 users/views.py:998 msgid "The password was changed." msgstr "Le mot de passe a été changé." -#: views.py:298 +#: users/views.py:276 #, python-format msgid "%s was removed from the group." msgstr "%s a été retiré du groupe." -#: views.py:308 +#: users/views.py:286 #, python-format msgid "%s is no longer superuser." msgstr "%s n'est plus superutilisateur." -#: views.py:321 +#: users/views.py:297 msgid "The service user was created." msgstr "L'utilisateur service a été créé." -#: views.py:325 -msgid "Create a service user" -msgstr "Créer un utilisateur service" +#: users/views.py:300 users/views.py:354 users/views.py:405 users/views.py:463 +#: users/views.py:551 users/views.py:614 users/views.py:657 +msgid "Add" +msgstr "Ajouter" -#: views.py:342 +#: users/views.py:314 msgid "The service user was edited." msgstr "L'utilisateur service a été modifié." -#: views.py:345 -msgid "Edit a service user" -msgstr "Modifier un utilisateur service" - -#: views.py:357 +#: users/views.py:329 msgid "The service user was deleted." msgstr "L'utilisateur service a été supprimé." -#: views.py:377 +#: users/views.py:349 msgid "The ban was added." msgstr "Le bannissement a été ajouté." -#: views.py:385 +#: users/views.py:352 msgid "Warning: this user already has an active ban." msgstr "Attention : cet utilisateur a déjà un bannissement actif." -#: views.py:404 +#: users/views.py:368 msgid "The ban was edited." msgstr "Le bannissement a été modifié." -#: views.py:407 -msgid "Edit a ban" -msgstr "Modifier un bannissement" - -#: views.py:419 +#: users/views.py:381 msgid "The ban was deleted." msgstr "Le bannissement a été supprimé." -#: views.py:446 +#: users/views.py:398 msgid "The whitelist was added." msgstr "L'accès gracieux a été ajouté." -#: views.py:454 +#: users/views.py:402 msgid "Warning: this user already has an active whitelist." msgstr "Attention : cet utilisateur a déjà un accès gracieux actif." -#: views.py:457 -msgid "Add a whitelist" -msgstr "Ajouter un accès gracieux" - -#: views.py:477 +#: users/views.py:422 msgid "The whitelist was edited." msgstr "L'accès gracieux a été ajouté." -#: views.py:480 -msgid "Edit a whitelist" -msgstr "Modifier un accès gracieux" - -#: views.py:492 +#: users/views.py:437 msgid "The whitelist was deleted." msgstr "L'accès gracieux a été supprimé." -#: views.py:516 +#: users/views.py:442 +msgid "whitelist" +msgstr "accès gracieux" + +#: users/views.py:457 msgid "The local email account was created." msgstr "Le compte mail local a été créé." -#: views.py:524 -msgid "Add a local email account" -msgstr "Ajouter un compte mail local" - -#: views.py:541 +#: users/views.py:480 msgid "The local email account was edited." msgstr "Le compte mail local a été modifié." -#: views.py:549 -msgid "Edit a local email account" -msgstr "Modifier un compte mail local" - -#: views.py:561 +#: users/views.py:503 msgid "The local email account was deleted." msgstr "Le compte mail local a été supprimé." -#: views.py:585 +#: users/views.py:508 +msgid "email address" +msgstr "adresse mail" + +#: users/views.py:524 msgid "The email settings were edited." msgstr "Les paramètres mail ont été modifiés." -#: views.py:594 -msgid "Edit the email settings" -msgstr "Modifier les paramètres mail" - -#: views.py:608 +#: users/views.py:548 msgid "The school was added." msgstr "L'établissement a été ajouté." -#: views.py:611 -msgid "Add a school" -msgstr "Ajouter un établissement" - -#: views.py:626 +#: users/views.py:566 msgid "The school was edited." msgstr "L'établissement a été modifié." -#: views.py:629 -msgid "Edit a school" -msgstr "Modifier un établissement" - -#: views.py:648 +#: users/views.py:588 msgid "The school was deleted." msgstr "L'établissement a été supprimé." -#: views.py:652 +#: users/views.py:593 #, python-format msgid "" "The school %s is assigned to at least one user, impossible to delete it." @@ -1357,51 +1390,31 @@ msgstr "" "L'établissement %s est assigné à au moins un utilisateur, impossible de le " "supprimer." -#: views.py:656 views.py:768 -msgid "Delete" -msgstr "Supprimer" - -#: views.py:669 +#: users/views.py:611 msgid "The shell was added." msgstr "L'interface en ligne de commande a été ajoutée." -#: views.py:672 -msgid "Add a shell" -msgstr "Ajouter une interface en ligne de commande" - -#: views.py:686 +#: users/views.py:626 msgid "The shell was edited." msgstr "L'interface en ligne de commande a été modifiée." -#: views.py:689 -msgid "Edit a shell" -msgstr "Modifier une interface en ligne de commande" - -#: views.py:701 +#: users/views.py:641 msgid "The shell was deleted." msgstr "L'interface en ligne de commande a été supprimée." -#: views.py:718 +#: users/views.py:654 msgid "The group of rights was added." msgstr "Le groupe de droits a été ajouté." -#: views.py:721 -msgid "Add a group of rights" -msgstr "Ajouter un groupe de droits" - -#: views.py:739 +#: users/views.py:672 msgid "The group of rights was edited." msgstr "Le groupe de droits a été modifié." -#: views.py:742 -msgid "Edit a group of rights" -msgstr "Modifier un groupe de droits" - -#: views.py:759 +#: users/views.py:692 msgid "The group of rights was deleted." msgstr "Le groupe de droits a été supprimé." -#: views.py:764 +#: users/views.py:697 #, python-format msgid "" "The group of rights %s is assigned to at least one user, impossible to " @@ -1410,40 +1423,32 @@ msgstr "" "Le groupe de droits %s est assigné à au moins un utilisateur, impossible de " "le supprimer." -#: views.py:792 -msgid "Archiving" -msgstr "Archivage" - -#: views.py:793 +#: users/views.py:733 #, python-format msgid "%s users were archived." msgstr "%s utilisateurs ont été archivés." -#: views.py:1043 +#: users/views.py:962 msgid "The user doesn't exist." msgstr "L'utilisateur n'existe pas." -#: views.py:1045 views.py:1053 +#: users/views.py:964 users/views.py:972 msgid "Reset" msgstr "Réinitialiser" -#: views.py:1050 +#: users/views.py:969 msgid "An email to reset the password was sent." msgstr "Un mail pour réinitialiser le mot de passe a été envoyé." -#: views.py:1067 +#: users/views.py:984 msgid "Error: please contact an admin." msgstr "Erreur : veuillez contacter un admin." -#: views.py:1079 -msgid "Password reset" -msgstr "Réinitialisation du mot de passe" - -#: views.py:1096 +#: users/views.py:1020 msgid "Incorrect URL, or already registered device." msgstr "URL incorrect, ou appareil déjà enregistré." -#: views.py:1104 +#: users/views.py:1032 msgid "" "Successful registration! Please disconnect and reconnect your Ethernet cable " "to get Internet access." @@ -1451,22 +1456,22 @@ msgstr "" "Enregistrement réussi ! Veuillez débrancher et rebrancher votre câble " "Ethernet pour avoir accès à Internet." -#: views.py:1148 views.py:1172 views.py:1187 +#: users/views.py:1072 users/views.py:1096 users/views.py:1111 msgid "The mailing list doesn't exist." msgstr "La liste de diffusion n'existe pas." -#: widgets.py:36 +#: users/widgets.py:39 msgid "Today" msgstr "Aujourd'hui" -#: widgets.py:44 +#: users/widgets.py:59 msgid "Next" msgstr "Suivant" -#: widgets.py:45 +#: users/widgets.py:60 msgid "Previous" msgstr "Précédent" -#: widgets.py:46 +#: users/widgets.py:61 msgid "Wk" -msgstr "Wk" +msgstr "Sem" diff --git a/users/management/commands/anonymise.py b/users/management/commands/anonymise.py new file mode 100644 index 00000000..700905b9 --- /dev/null +++ b/users/management/commands/anonymise.py @@ -0,0 +1,117 @@ +from django.core.management.base import BaseCommand +from users.models import User, School, Adherent, Club +from machines.models import Domain, Machine +from reversion.models import Revision +from django.db.models import F, Value +from django.db.models import Q +from django.db.models.functions import Concat + +from re2o.login import hashNT, makeSecret + +import os, random, string +from random import randint + + +class Command(BaseCommand): + help = "Anonymise the data in the database in order to use them on critical servers (dev, personal...). Every information will be overwritten using non-personal information. This script must follow any modification of the database.\nOptional argument: {id|id|id|...} to exclude users from anonymisation." + + def add_arguments(self, parser): + parser.add_argument("user_id", nargs="+", type=int, help="User ID") + + def handle(self, *args, **kwargs): + users_ids = kwargs["user_id"] + for user_id in users_ids: + self.stdout.write( + "User: {} will not be anonymised.".format( + User.objects.filter(id=user_id).get().name + ) + ) + + self.stdout.write( + self.style.WARNING( + "\nDISCLAIMER\nThis function will make your database unusable for production. Are you sure you want to run this? (doit): " + ) + ) + if input() == "doit": + + total = Adherent.objects.count() + self.stdout.write("Starting anonymizing the {} users data.".format(total)) + + u = User.objects.filter(~Q(id__in=users_ids)) + a = Adherent.objects.filter(~Q(id__in=users_ids)) + c = Club.objects.filter(~Q(id__in=users_ids)) + d = Domain.objects.all() + m = Machine.objects.filter(~Q(user_id__in=users_ids)) + + self.stdout.write("Deletion of the school...") + # Create a fake School to put everyone in it. + ecole = School(name="Ninja School") + ecole.save() + u.update(school=ecole) + self.stdout.write(self.style.SUCCESS("Done...")) + + self.stdout.write("Deletion of rooms...") + a.update(room=None) + c.update(room=None) + self.stdout.write(self.style.SUCCESS("Done...")) + + self.stdout.write("Deletion of email addresses...") + u.update( + email="example@example.org", + local_email_redirect=False, + local_email_enabled=False, + ) + self.stdout.write(self.style.SUCCESS("Done...")) + + self.stdout.write( + "Deletion of first names, surnames, usernames, telephone numbers, comments..." + ) + a.update(name=Concat(Value("First name of "), "id")) + self.stdout.write(self.style.SUCCESS("Done for first names...")) + + a.update(surname=Concat(Value("Surname of "), "id")) + self.stdout.write(self.style.SUCCESS("Done for surnames...")) + + u.update(pseudo=F("id")) + self.stdout.write(self.style.SUCCESS("Done for usernames...")) + + a.update(telephone=Concat(Value("Telephone number of "), "id")) + self.stdout.write(self.style.SUCCESS("Done for telephone numbers...")) + + a.update(comment=Concat(Value("Comment of "), "id")) + self.stdout.write(self.style.SUCCESS("Done for comments...")) + + self.stdout.write("Renaming of machines...") + m.update( + name=Concat(Value("Machine "), F("id"), Value(" of "), F("user_id")) + ) + d.update(name=Concat(Value("Domain id "), F("id"))) + self.stdout.write(self.style.SUCCESS("Done...")) + + self.stdout.write("Unification of the password...") + # Define the password + chars = string.ascii_letters + string.digits + "!@#$%^&*()" + taille = 20 + random.seed = os.urandom(1024) + password = "" + for i in range(taille): + password += random.choice(chars) + + self.stdout.write( + self.style.HTTP_NOT_MODIFIED( + "The password will be: {}.".format(password) + ) + ) + + u.update(pwd_ntlm=hashNT(password)) + u.update(password=makeSecret(password)) + self.stdout.write(self.style.SUCCESS("Done...")) + + self.stdout.write("Deletion of the history (this may take some time)...") + Revision.objects.all().delete() + self.stdout.write(self.style.SUCCESS("Done...")) + + self.stdout.write("Data anonymised!") + + else: + self.stdout.write("Anonymisation aborted!") diff --git a/users/management/commands/anonymize.py b/users/management/commands/anonymize.py deleted file mode 100644 index a6bce971..00000000 --- a/users/management/commands/anonymize.py +++ /dev/null @@ -1,98 +0,0 @@ -from django.core.management.base import BaseCommand -from users.models import User, School, Adherent, Club -from machines.models import Domain, Machine -from reversion.models import Revision -from django.db.models import F, Value -from django.db.models import Q -from django.db.models.functions import Concat - -from re2o.login import hashNT, makeSecret - -import os, random, string -from random import randint - -class Command(BaseCommand): - help="Anonymize the data in the database in order to use them on critical servers (dev, personnal...). Every information will be overwritten using non-personnal informations. This script must follow any modification of the database.\nOptionnal argument: {id|id|id|...} to exclude users from anonymisation" - - def add_arguments(self, parser): - parser.add_argument('user_id', nargs='+', type=int, help='User ID') - - def handle(self, *args, **kwargs): - users_ids = kwargs['user_id'] - for user_id in users_ids: - self.stdout.write("User: {} will not be anonymised".format(User.objects.filter(id=user_id).get().name)) - - self.stdout.write(self.style.WARNING('\nDISCLAIMER\nThis function will make your database unusable for production. Are you sure you want to run this ?(doit): ')) - if(input()=="doit"): - - total = Adherent.objects.count() - self.stdout.write("Starting anonymizing the {} users data.".format(total)) - - u = User.objects.filter(~Q(id__in=users_ids)) - a = Adherent.objects.filter(~Q(id__in=users_ids)) - c = Club.objects.filter(~Q(id__in=users_ids)) - d = Domain.objects.all() - m = Machine.objects.filter(~Q(user_id__in=users_ids)) - - self.stdout.write('Supression de l\'école...') - # Create a fake School to put everyone in it. - ecole = School(name="Ecole des Ninja") - ecole.save() - u.update(school=ecole) - self.stdout.write(self.style.SUCCESS('done ...')) - - self.stdout.write('Supression des chambres...') - a.update(room=None) - c.update(room=None) - self.stdout.write(self.style.SUCCESS('done ...')) - - self.stdout.write('Supression des mails...') - u.update(email='example@example.org', - local_email_redirect = False, - local_email_enabled=False) - self.stdout.write(self.style.SUCCESS('done ...')) - - self.stdout.write('Supression des noms, prenoms, pseudo, telephone, commentaire...') - a.update(name=Concat(Value('name of '), 'id')) - self.stdout.write(self.style.SUCCESS('done name')) - - a.update(surname=Concat(Value('surname of '), 'id')) - self.stdout.write(self.style.SUCCESS('done surname')) - - u.update(pseudo=F('id')) - self.stdout.write(self.style.SUCCESS('done pseudo')) - - a.update(telephone=Concat(Value('phone of '), 'id')) - self.stdout.write(self.style.SUCCESS('done phone')) - - a.update(comment=Concat(Value('commentaire of '), 'id')) - self.stdout.write(self.style.SUCCESS('done ...')) - - self.stdout.write('Renommage des machines...') - m.update(name=Concat(Value('Machine '),F('id'),Value(' of '),F('user_id'))) - d.update(name=Concat(Value('Domaine id '),F('id'))) - self.stdout.write(self.style.SUCCESS('done ...')) - - self.stdout.write('Unification du mot de passe...') - # Define the password - chars = string.ascii_letters + string.digits + '!@#$%^&*()' - taille = 20 - random.seed = (os.urandom(1024)) - password = "" - for i in range(taille): - password+=random.choice(chars) - - self.stdout.write(self.style.HTTP_NOT_MODIFIED('The password will be: {}'.format(password))) - - u.update(pwd_ntlm = hashNT(password)) - u.update(password = makeSecret(password)) - self.stdout.write(self.style.SUCCESS('done...')) - - self.stdout.write('Suppression de l\'historique (This may take some time)') - Revision.objects.all().delete() - self.stdout.write(self.style.SUCCESS('done...')) - - self.stdout.write("Data anonymized!") - - else: - self.stdout.write("Anonymisation aborted") diff --git a/users/management/commands/archive.py b/users/management/commands/archive.py new file mode 100644 index 00000000..1e4601a0 --- /dev/null +++ b/users/management/commands/archive.py @@ -0,0 +1,111 @@ +# ⁻*- 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 © 2019 Hugo Levy--Falk +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import argparse +import datetime + +from django.core.management.base import BaseCommand, CommandError +from django.utils.timezone import make_aware + +from re2o.utils import all_has_access +from users.models import User + + +def valid_date(s): + try: + return make_aware(datetime.datetime.strptime(s, "%d/%m/%Y")) + except ValueError: + msg = "Not a valid date: '{0}'.".format(s) + raise argparse.ArgumentTypeError(msg) + + +class Command(BaseCommand): + help = "Allow unactive users archiving by unassigning their IP addresses." + + def add_arguments(self, parser): + parser.add_argument( + "--full", + "-f", + action="store_true", + help="Fully archive users, i.e. delete their email address, machines and remove them from the LDAP.", + ) + parser.add_argument( + "--date", + "-d", + default=datetime.date.today().strftime("%d/%m/%Y"), + type=valid_date, + help="Users whose membership ends sooner than this date will be archived.", + ) + parser.add_argument( + "--show", + "-s", + action="store_true", + help="Only show a list of users, without doing anything.", + ) + parser.add_argument( + "-y", + action="store_true", + help="Do not ask for confirmation before fully archiving.", + ) + + def handle(self, *args, **kwargs): + full_archive = kwargs["full"] + date = kwargs["date"] + force = kwargs["y"] + show = kwargs["show"] + + to_archive_list = ( + User.objects.exclude(id__in=all_has_access()) + .exclude(id__in=all_has_access(search_time=date)) + .exclude(state=User.STATE_NOT_YET_ACTIVE) + .exclude(state=User.STATE_FULL_ARCHIVE) + ) + + if show: + self.stdout.write("%s users found : " % to_archive_list.count()) + self.stdout.write("\n".join(map(str, to_archive_list.all()))) + return + + if full_archive and not force: + self.stdout.write( + self.style.WARNING( + "Please confirm fully archiving (it is a critical operation!) [Y/n]" + ) + ) + if input() != "Y": + self.stdout.write("Leaving without archiving.") + return + if full_archive: + self.stdout.write( + "Fully archiving users with a membership ending prior to %s." + % date.strftime("%d/%m/%Y") + ) + User.mass_full_archive(to_archive_list) + else: + self.stdout.write( + "Archiving users with a membership ending prior to %s." + % date.strftime("%d/%m/%Y") + ) + to_archive_list = to_archive_list.exclude(state=User.STATE_ARCHIVE) + User.mass_archive(to_archive_list) + self.stdout.write( + self.style.SUCCESS("%s users were archived." % to_archive_list.count()) + ) diff --git a/users/management/commands/chgpass.py b/users/management/commands/chgpass.py index 9763ae4c..2937bb3c 100644 --- a/users/management/commands/chgpass.py +++ b/users/management/commands/chgpass.py @@ -28,10 +28,10 @@ from re2o.script_utils import get_user, get_system_user, form_cli class Command(BaseCommand): - help = "Changer le mot de passe d'un utilisateur" + help = "Change the password of a user." def add_arguments(self, parser): - parser.add_argument('target_username', nargs='?') + parser.add_argument("target_username", nargs="?") def handle(self, *args, **kwargs): @@ -44,13 +44,8 @@ class Command(BaseCommand): if not ok: raise CommandError(msg) - self.stdout.write( - "Changement du mot de passe de %s" % target_user.pseudo - ) + self.stdout.write("Password change of %s" % target_user.pseudo) form_cli( - PassForm, - current_user, - "Changement du mot de passe", - instance=target_user + PassForm, current_user, "Password change", instance=target_user ) diff --git a/users/management/commands/chsh.py b/users/management/commands/chsh.py index 6921ad79..95be7b44 100644 --- a/users/management/commands/chsh.py +++ b/users/management/commands/chsh.py @@ -32,10 +32,10 @@ from re2o.script_utils import get_user, get_system_user class Command(BaseCommand): - help = 'Change the default shell of a user' + help = "Change the default shell of a user." def add_arguments(self, parser): - parser.add_argument('target_username', nargs='?') + parser.add_argument("target_username", nargs="?") def handle(self, *args, **options): @@ -52,37 +52,36 @@ class Command(BaseCommand): shells = ListShell.objects.all() - current_shell = "inconnu" + current_shell = "unknown" if target_user.shell: current_shell = target_user.shell.get_pretty_name() self.stdout.write( - "Choisissez un shell pour l'utilisateur %s (le shell actuel est " - "%s) :" % (target_user.pseudo, current_shell) + "Choose a shell for the user %s (the current shell is" + " %s):" % (target_user.pseudo, current_shell) ) for shell in shells: - self.stdout.write("%d - %s (%s)" % ( - shell.id, - shell.get_pretty_name(), - shell.shell - )) - shell_id = input("Entrez un nombre : ") + self.stdout.write( + "%d - %s (%s)" % (shell.id, shell.get_pretty_name(), shell.shell) + ) + shell_id = input("Enter a number: ") try: shell_id = int(shell_id) except: - raise CommandError("Choix invalide") + raise CommandError("Invalid choice.") shell = ListShell.objects.filter(id=shell_id) if not shell: - raise CommandError("Choix invalide") + raise CommandError("Invalid choice.") target_user.shell = shell.first() with transaction.atomic(), reversion.create_revision(): target_user.save() reversion.set_user(current_user) - reversion.set_comment("Shell modifié") + reversion.set_comment("Shell changed.") - self.stdout.write(self.style.SUCCESS( - "Shell modifié. La modification peut prendre quelques minutes " - "pour s'appliquer." - )) + self.stdout.write( + self.style.SUCCESS( + "Shell changed. The change may take a few minutes to apply." + ) + ) diff --git a/users/management/commands/clean_notyetactive.py b/users/management/commands/clean_notyetactive.py index 44c5b24e..994abfc2 100644 --- a/users/management/commands/clean_notyetactive.py +++ b/users/management/commands/clean_notyetactive.py @@ -1,5 +1,5 @@ # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -25,13 +25,19 @@ from datetime import timedelta from django.utils import timezone + class Command(BaseCommand): - help = "Delete non members users (not yet active)" + help = "Delete non members users (not yet active)." def handle(self, *args, **options): """First deleting invalid invoices, and then deleting the users""" - days = OptionalUser.get_cached_value('delete_notyetactive') - users_to_delete = User.objects.filter(state=User.STATE_NOT_YET_ACTIVE).filter(registered__lte=timezone.now() - timedelta(days=days)).exclude(facture__valid=True).distinct() - print("Deleting " + str(users_to_delete.count()) + " users") + days = OptionalUser.get_cached_value("delete_notyetactive") + users_to_delete = ( + User.objects.filter(state=User.STATE_NOT_YET_ACTIVE) + .filter(registered__lte=timezone.now() - timedelta(days=days)) + .exclude(facture__valid=True) + .distinct() + ) + print("Deleting " + str(users_to_delete.count()) + " users.") Facture.objects.filter(user__in=users_to_delete).delete() users_to_delete.delete() diff --git a/users/management/commands/derniere_connexion.py b/users/management/commands/derniere_connexion.py index d936fda8..38352640 100644 --- a/users/management/commands/derniere_connexion.py +++ b/users/management/commands/derniere_connexion.py @@ -33,28 +33,35 @@ from users.models import User # Elles doivent contenir un groupe 'date' et un groupe 'user'. # Pour le CAS on prend comme entrée # cat ~/cas.log | grep -B 2 -A 2 "ACTION: AUTHENTICATION_SUCCESS"| grep 'WHEN\|WHO'|sed 'N;s/\n/ /' -COMPILED_REGEX = map(re.compile, [ - r'^(?P\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}).*(?:'r'dovecot.*Login: user=<|'r'sshd.*Accepted.*for 'r')(?P[^ >]+).*$', - r'^(?P.*) LOGIN INFO User logged in : (?P.*)', - r'WHO: \[username: (?P.*)\] WHEN: (?P.* CET .*)', - r'WHO: \[username: (?P.*)\] WHEN: (?P.* CEST .*)' -]) +COMPILED_REGEX = map( + re.compile, + [ + r"^(?P\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}).*(?:" + r"dovecot.*Login: user=<|" + r"sshd.*Accepted.*for " + r")(?P[^ >]+).*$", + r"^(?P.*) LOGIN INFO User logged in : (?P.*)", + r"WHO: \[username: (?P.*)\] WHEN: (?P.* CET .*)", + r"WHO: \[username: (?P.*)\] WHEN: (?P.* CEST .*)", + ], +) # Les formats de date en strftime associés aux expressions ci-dessus. DATE_FORMATS = [ "%Y-%m-%dT%H:%M:%S", "%d/%b/%Y:%H:%M:%S", "%a %b %d CET %H:%M:%S%Y", - "%a %b %d CEST %H:%M:%S%Y" + "%a %b %d CEST %H:%M:%S%Y", ] class Command(BaseCommand): - help = ('Update the time of the latest connection for users by matching ' - 'stdin against a set of regular expressions') + help = ( + "Update the time of the latest connection for users by matching" + " stdin against a set of regular expressions." + ) def handle(self, *args, **options): - def parse_logs(logfile): """ Parse les logs sur l'entrée standard et rempli un dictionnaire @@ -67,8 +74,8 @@ class Command(BaseCommand): for i, regex in enumerate(COMPILED_REGEX): m = regex.match(line) if m: - parsed_log[m.group('user')] = make_aware( - datetime.strptime(m.group('date'), DATE_FORMATS[i]) + parsed_log[m.group("user")] = make_aware( + datetime.strptime(m.group("date"), DATE_FORMATS[i]) ) return parsed_log diff --git a/users/management/commands/email.py b/users/management/commands/email.py deleted file mode 100644 index 5e1d02c4..00000000 --- a/users/management/commands/email.py +++ /dev/null @@ -1,35 +0,0 @@ -from django.core.management.base import BaseCommand, CommandError - -from datetime import datetime, timedelta -from pytz - -from users.models import User - -UTC = pytz.timezone('UTC') - - -# TODO : remove of finsihed this because currently it should -# be failing! Who commited that ?! -class Command(BaseCommand): - commands = ['email_remainder'] - args = '[command]' - help = 'Send email remainders' - - def handle(self, *args, **options): - ''' - Sends an email before the end of a user's subscription - ''' - users = User.objects.filter(state="STATE_ACTIVE") - - for user in users: - remaining = user.end_adhesion() - datetime.today(tz=UTC) - if (timedelta(weeks=4) - remaining).days == 1: - 4_weeks_reminder() - elif (timedelta(weeks=1) - remaining).days == 1: - week_reminder() - elif remaining.days == 1: - last_day_reminder() - - -def month_reminder(): - pass diff --git a/users/management/commands/ldap_rebuild.py b/users/management/commands/ldap_rebuild.py index 9deecbd3..c8b172f9 100644 --- a/users/management/commands/ldap_rebuild.py +++ b/users/management/commands/ldap_rebuild.py @@ -29,9 +29,9 @@ def split_lines(lines): following system lines begins with a space. """ ret = [] - for line in lines.split(b'\n'): - if line.startswith(b' ') and len(ret) > 1: - ret[-1] += line[len(b' '):] + for line in lines.split(b"\n"): + if line.startswith(b" ") and len(ret) > 1: + ret[-1] += line[len(b" ") :] else: ret.append(line) return ret @@ -61,28 +61,28 @@ def flush_ldap(binddn, bindpass, server, usersdn, groupsdn): for lookup in (usersdn, groupsdn): search_cmd = [ - 'ldapsearch', - '-LLL', - '-s', 'one', - '-D', binddn, - '-w', bindpass, - '-H', server, - '-b', lookup, - 'dn' + "ldapsearch", + "-LLL", + "-s", + "one", + "-D", + binddn, + "-w", + bindpass, + "-H", + server, + "-b", + lookup, + "dn", ] for line in split_lines(subprocess.check_output(search_cmd)): - if line.startswith(b'dn: '): - to_remove.append(line[len(b'dn: '):]) - elif line.startswith(b'dn:: '): + if line.startswith(b"dn: "): + to_remove.append(line[len(b"dn: ") :]) + elif line.startswith(b"dn:: "): # Non ASCII value ares are base64-encoded - to_remove.append(decodebytes(line[len(b'dn:: '):])) + to_remove.append(decodebytes(line[len(b"dn:: ") :])) - delete_cmd = [ - 'ldapdelete', - '-D', binddn, - '-w', bindpass, - '-H', server - ] + to_remove + delete_cmd = ["ldapdelete", "-D", binddn, "-w", bindpass, "-H", server] + to_remove subprocess.check_call(delete_cmd) @@ -95,18 +95,20 @@ def sync_ldap(): class Command(BaseCommand): - help = ('Destroy the current LDAP data and rebuild it from the DB data. ' - 'Use with caution.') + help = ( + "Destroy the current LDAP data and rebuild it from the DB data." + " Use with caution." + ) def handle(self, *args, **options): - usersdn = settings.LDAP['base_user_dn'] - groupsdn = settings.LDAP['base_usergroup_dn'] - binddn = settings.DATABASES['ldap']['USER'] - bindpass = settings.DATABASES['ldap']['PASSWORD'] - server = settings.DATABASES['ldap']['NAME'] + usersdn = settings.LDAP["base_user_dn"] + groupsdn = settings.LDAP["base_usergroup_dn"] + binddn = settings.DATABASES["ldap"]["USER"] + bindpass = settings.DATABASES["ldap"]["PASSWORD"] + server = settings.DATABASES["ldap"]["NAME"] flush_ldap(binddn, bindpass, server, usersdn, groupsdn) - self.stdout.write("LDAP emptied") + self.stdout.write("LDAP emptied.") sync_ldap() - self.stdout.write("LDAP rebuilt") + self.stdout.write("LDAP rebuilt.") diff --git a/users/management/commands/ldap_sync.py b/users/management/commands/ldap_sync.py index 9301c788..73f6698e 100644 --- a/users/management/commands/ldap_sync.py +++ b/users/management/commands/ldap_sync.py @@ -1,5 +1,5 @@ # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -22,19 +22,19 @@ from users.models import User class Command(BaseCommand): - help = 'Synchronise le ldap à partir du sql. A utiliser dans un cron' + help = "Synchronise the LDAP from SQL. To be used in a cron." def add_arguments(self, parser): # Named (optional) arguments parser.add_argument( - '--full', - action='store_true', - dest='full', + "--full", + action="store_true", + dest="full", default=False, - help='Régénération complète du ldap (y compris des machines)', + help="Complete regeneration of the LDAP (including machines).", ) def handle(self, *args, **options): for usr in User.objects.all(): - usr.ldap_sync(mac_refresh=options['full']) + usr.ldap_sync(mac_refresh=options["full"]) diff --git a/users/migrations/0001_initial.py b/users/migrations/0001_initial.py index f718e4f0..b10f2bd3 100644 --- a/users/migrations/0001_initial.py +++ b/users/migrations/0001_initial.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,30 +29,61 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='School', + name="School", fields=[ - ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)), - ('name', models.CharField(max_length=255)), + ( + "id", + models.AutoField( + primary_key=True, + serialize=False, + verbose_name="ID", + auto_created=True, + ), + ), + ("name", models.CharField(max_length=255)), ], ), migrations.CreateModel( - name='User', + name="User", fields=[ - ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)), - ('name', models.CharField(max_length=255)), - ('surname', models.CharField(max_length=255)), - ('pseudo', models.CharField(max_length=255)), - ('email', models.EmailField(max_length=254)), - ('promo', models.CharField(max_length=255)), - ('pwd_ssha', models.CharField(max_length=255)), - ('pwd_ntlm', models.CharField(max_length=255)), - ('state', models.CharField(default=0, max_length=30, choices=[(0, 'STATE_ACTIVE'), (1, 'STATE_DEACTIVATED'), (2, 'STATE_ARCHIVED')])), - ('school', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='users.School')), + ( + "id", + models.AutoField( + primary_key=True, + serialize=False, + verbose_name="ID", + auto_created=True, + ), + ), + ("name", models.CharField(max_length=255)), + ("surname", models.CharField(max_length=255)), + ("pseudo", models.CharField(max_length=255)), + ("email", models.EmailField(max_length=254)), + ("promo", models.CharField(max_length=255)), + ("pwd_ssha", models.CharField(max_length=255)), + ("pwd_ntlm", models.CharField(max_length=255)), + ( + "state", + models.CharField( + default=0, + max_length=30, + choices=[ + (0, "STATE_ACTIVE"), + (1, "STATE_DEACTIVATED"), + (2, "STATE_ARCHIVED"), + ], + ), + ), + ( + "school", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, to="users.School" + ), + ), ], ), ] diff --git a/users/migrations/0002_auto_20160630_2301.py b/users/migrations/0002_auto_20160630_2301.py index b45d09e3..7994825e 100644 --- a/users/migrations/0002_auto_20160630_2301.py +++ b/users/migrations/0002_auto_20160630_2301.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,19 +28,24 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0001_initial'), - ] + dependencies = [("users", "0001_initial")] operations = [ migrations.AlterField( - model_name='user', - name='pseudo', + model_name="user", + name="pseudo", field=models.CharField(unique=True, max_length=255), ), migrations.AlterField( - model_name='user', - name='state', - field=models.IntegerField(default=0, choices=[(0, 'STATE_ACTIVE'), (1, 'STATE_DEACTIVATED'), (2, 'STATE_ARCHIVED')]), + model_name="user", + name="state", + field=models.IntegerField( + default=0, + choices=[ + (0, "STATE_ACTIVE"), + (1, "STATE_DEACTIVATED"), + (2, "STATE_ARCHIVED"), + ], + ), ), ] diff --git a/users/migrations/0003_listrights_rights.py b/users/migrations/0003_listrights_rights.py index a16345f7..a67a0f4d 100644 --- a/users/migrations/0003_listrights_rights.py +++ b/users/migrations/0003_listrights_rights.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,24 +29,49 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('users', '0002_auto_20160630_2301'), - ] + dependencies = [("users", "0002_auto_20160630_2301")] operations = [ migrations.CreateModel( - name='ListRights', + name="ListRights", fields=[ - ('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')), - ('listright', models.CharField(max_length=255)), + ( + "id", + models.AutoField( + serialize=False, + primary_key=True, + auto_created=True, + verbose_name="ID", + ), + ), + ("listright", models.CharField(max_length=255)), ], ), migrations.CreateModel( - name='Rights', + name="Rights", fields=[ - ('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')), - ('right', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='users.ListRights')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='users.User')), + ( + "id", + models.AutoField( + serialize=False, + primary_key=True, + auto_created=True, + verbose_name="ID", + ), + ), + ( + "right", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to="users.ListRights", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, to="users.User" + ), + ), ], ), ] diff --git a/users/migrations/0004_auto_20160701_2312.py b/users/migrations/0004_auto_20160701_2312.py index e0fb528e..e925c6df 100644 --- a/users/migrations/0004_auto_20160701_2312.py +++ b/users/migrations/0004_auto_20160701_2312.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,17 +28,9 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0003_listrights_rights'), - ] + dependencies = [("users", "0003_listrights_rights")] operations = [ - migrations.RenameModel( - old_name='ListRights', - new_name='ListRight', - ), - migrations.RenameModel( - old_name='Rights', - new_name='Right', - ), + migrations.RenameModel(old_name="ListRights", new_name="ListRight"), + migrations.RenameModel(old_name="Rights", new_name="Right"), ] diff --git a/users/migrations/0005_auto_20160702_0006.py b/users/migrations/0005_auto_20160702_0006.py index 45d011c3..e45cc6cf 100644 --- a/users/migrations/0005_auto_20160702_0006.py +++ b/users/migrations/0005_auto_20160702_0006.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,13 +28,10 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0004_auto_20160701_2312'), - ] + dependencies = [("users", "0004_auto_20160701_2312")] operations = [ migrations.AlterUniqueTogether( - name='right', - unique_together=set([('user', 'right')]), - ), + name="right", unique_together=set([("user", "right")]) + ) ] diff --git a/users/migrations/0006_ban.py b/users/migrations/0006_ban.py index 9ff71e57..d47c33c0 100644 --- a/users/migrations/0006_ban.py +++ b/users/migrations/0006_ban.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,19 +29,30 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('users', '0005_auto_20160702_0006'), - ] + dependencies = [("users", "0005_auto_20160702_0006")] operations = [ migrations.CreateModel( - name='Ban', + name="Ban", fields=[ - ('id', models.AutoField(serialize=False, auto_created=True, verbose_name='ID', primary_key=True)), - ('raison', models.CharField(max_length=255)), - ('date_start', models.DateTimeField(help_text='%m/%d/%y %H:%M:%S')), - ('date_end', models.DateTimeField(help_text='%m/%d/%y %H:%M:%S')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='users.User')), + ( + "id", + models.AutoField( + serialize=False, + auto_created=True, + verbose_name="ID", + primary_key=True, + ), + ), + ("raison", models.CharField(max_length=255)), + ("date_start", models.DateTimeField(help_text="%m/%d/%y %H:%M:%S")), + ("date_end", models.DateTimeField(help_text="%m/%d/%y %H:%M:%S")), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, to="users.User" + ), + ), ], - ), + ) ] diff --git a/users/migrations/0007_auto_20160702_2322.py b/users/migrations/0007_auto_20160702_2322.py index 4e6e75b1..45e1901e 100644 --- a/users/migrations/0007_auto_20160702_2322.py +++ b/users/migrations/0007_auto_20160702_2322.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0006_ban'), - ] + dependencies = [("users", "0006_ban")] operations = [ migrations.AlterField( - model_name='ban', - name='date_start', + model_name="ban", + name="date_start", field=models.DateTimeField(auto_now_add=True), - ), + ) ] diff --git a/users/migrations/0008_user_registered.py b/users/migrations/0008_user_registered.py index 1c262423..36bd4b6b 100644 --- a/users/migrations/0008_user_registered.py +++ b/users/migrations/0008_user_registered.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -30,15 +30,16 @@ import datetime class Migration(migrations.Migration): - dependencies = [ - ('users', '0007_auto_20160702_2322'), - ] + dependencies = [("users", "0007_auto_20160702_2322")] operations = [ migrations.AddField( - model_name='user', - name='registered', - field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2016, 7, 2, 23, 25, 21, 698883, tzinfo=utc)), + model_name="user", + name="registered", + field=models.DateTimeField( + auto_now_add=True, + default=datetime.datetime(2016, 7, 2, 23, 25, 21, 698883, tzinfo=utc), + ), preserve_default=False, - ), + ) ] diff --git a/users/migrations/0009_user_room.py b/users/migrations/0009_user_room.py index df5bcfbb..e03c3119 100644 --- a/users/migrations/0009_user_room.py +++ b/users/migrations/0009_user_room.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -30,15 +30,19 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('topologie', '0009_auto_20160703_1200'), - ('users', '0008_user_registered'), + ("topologie", "0009_auto_20160703_1200"), + ("users", "0008_user_registered"), ] operations = [ migrations.AddField( - model_name='user', - name='room', - field=models.ForeignKey(to='topologie.Room', on_delete=django.db.models.deletion.PROTECT, default=1), + model_name="user", + name="room", + field=models.ForeignKey( + to="topologie.Room", + on_delete=django.db.models.deletion.PROTECT, + default=1, + ), preserve_default=False, - ), + ) ] diff --git a/users/migrations/0010_auto_20160703_1226.py b/users/migrations/0010_auto_20160703_1226.py index 47f89c6d..80b0153a 100644 --- a/users/migrations/0010_auto_20160703_1226.py +++ b/users/migrations/0010_auto_20160703_1226.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,14 +29,17 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('users', '0009_user_room'), - ] + dependencies = [("users", "0009_user_room")] operations = [ migrations.AlterField( - model_name='user', - name='room', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, blank=True, to='topologie.Room', null=True), - ), + model_name="user", + name="room", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + blank=True, + to="topologie.Room", + null=True, + ), + ) ] diff --git a/users/migrations/0011_auto_20160703_1227.py b/users/migrations/0011_auto_20160703_1227.py index a6c86731..a064dfe7 100644 --- a/users/migrations/0011_auto_20160703_1227.py +++ b/users/migrations/0011_auto_20160703_1227.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,14 +29,18 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('users', '0010_auto_20160703_1226'), - ] + dependencies = [("users", "0010_auto_20160703_1226")] operations = [ migrations.AlterField( - model_name='user', - name='room', - field=models.ForeignKey(null=True, blank=True, unique=True, on_delete=django.db.models.deletion.PROTECT, to='topologie.Room'), - ), + model_name="user", + name="room", + field=models.ForeignKey( + null=True, + blank=True, + unique=True, + on_delete=django.db.models.deletion.PROTECT, + to="topologie.Room", + ), + ) ] diff --git a/users/migrations/0012_auto_20160703_1230.py b/users/migrations/0012_auto_20160703_1230.py index 4cb69574..9a9cdcf6 100644 --- a/users/migrations/0012_auto_20160703_1230.py +++ b/users/migrations/0012_auto_20160703_1230.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,14 +29,17 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('users', '0011_auto_20160703_1227'), - ] + dependencies = [("users", "0011_auto_20160703_1227")] operations = [ migrations.AlterField( - model_name='user', - name='room', - field=models.OneToOneField(blank=True, on_delete=django.db.models.deletion.PROTECT, to='topologie.Room', null=True), - ), + model_name="user", + name="room", + field=models.OneToOneField( + blank=True, + on_delete=django.db.models.deletion.PROTECT, + to="topologie.Room", + null=True, + ), + ) ] diff --git a/users/migrations/0013_auto_20160704_1547.py b/users/migrations/0013_auto_20160704_1547.py index 11b146d3..adfbf928 100644 --- a/users/migrations/0013_auto_20160704_1547.py +++ b/users/migrations/0013_auto_20160704_1547.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,19 +28,21 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0012_auto_20160703_1230'), - ] + dependencies = [("users", "0012_auto_20160703_1230")] operations = [ migrations.AddField( - model_name='user', - name='comment', - field=models.CharField(max_length=255, help_text="Infos sur l'etablissement (optionnel)", blank=True), + model_name="user", + name="comment", + field=models.CharField( + max_length=255, + help_text="Infos sur l'etablissement (optionnel)", + blank=True, + ), ), migrations.AlterField( - model_name='user', - name='promo', + model_name="user", + name="promo", field=models.CharField(max_length=255, blank=True), ), ] diff --git a/users/migrations/0014_auto_20160704_1548.py b/users/migrations/0014_auto_20160704_1548.py index ccdc9c35..72dd09ef 100644 --- a/users/migrations/0014_auto_20160704_1548.py +++ b/users/migrations/0014_auto_20160704_1548.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,18 +28,15 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0013_auto_20160704_1547'), - ] + dependencies = [("users", "0013_auto_20160704_1547")] operations = [ - migrations.RemoveField( - model_name='user', - name='promo', - ), + migrations.RemoveField(model_name="user", name="promo"), migrations.AlterField( - model_name='user', - name='comment', - field=models.CharField(blank=True, help_text='Commentaire, promo', max_length=255), + model_name="user", + name="comment", + field=models.CharField( + blank=True, help_text="Commentaire, promo", max_length=255 + ), ), ] diff --git a/users/migrations/0015_whitelist.py b/users/migrations/0015_whitelist.py index bb356041..6d582050 100644 --- a/users/migrations/0015_whitelist.py +++ b/users/migrations/0015_whitelist.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,19 +29,30 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('users', '0014_auto_20160704_1548'), - ] + dependencies = [("users", "0014_auto_20160704_1548")] operations = [ migrations.CreateModel( - name='Whitelist', + name="Whitelist", fields=[ - ('id', models.AutoField(serialize=False, verbose_name='ID', auto_created=True, primary_key=True)), - ('raison', models.CharField(max_length=255)), - ('date_start', models.DateTimeField(auto_now_add=True)), - ('date_end', models.DateTimeField(help_text='%m/%d/%y %H:%M:%S')), - ('user', models.ForeignKey(to='users.User', on_delete=django.db.models.deletion.PROTECT)), + ( + "id", + models.AutoField( + serialize=False, + verbose_name="ID", + auto_created=True, + primary_key=True, + ), + ), + ("raison", models.CharField(max_length=255)), + ("date_start", models.DateTimeField(auto_now_add=True)), + ("date_end", models.DateTimeField(help_text="%m/%d/%y %H:%M:%S")), + ( + "user", + models.ForeignKey( + to="users.User", on_delete=django.db.models.deletion.PROTECT + ), + ), ], - ), + ) ] diff --git a/users/migrations/0016_auto_20160706_1220.py b/users/migrations/0016_auto_20160706_1220.py index 0a6e969a..c3596986 100644 --- a/users/migrations/0016_auto_20160706_1220.py +++ b/users/migrations/0016_auto_20160706_1220.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,24 +29,27 @@ import users.models class Migration(migrations.Migration): - dependencies = [ - ('users', '0015_whitelist'), - ] + dependencies = [("users", "0015_whitelist")] operations = [ migrations.AlterField( - model_name='ban', - name='date_end', - field=models.DateTimeField(help_text='%d/%m/%y %H:%M:%S'), + model_name="ban", + name="date_end", + field=models.DateTimeField(help_text="%d/%m/%y %H:%M:%S"), ), migrations.AlterField( - model_name='user', - name='pseudo', - field=models.CharField(unique=True, validators=[users.models.linux_user_validator], max_length=32, help_text='Doit contenir uniquement des lettres, chiffres, ou tirets'), + model_name="user", + name="pseudo", + field=models.CharField( + unique=True, + validators=[users.models.linux_user_validator], + max_length=32, + help_text="Doit contenir uniquement des lettres, chiffres, ou tirets", + ), ), migrations.AlterField( - model_name='whitelist', - name='date_end', - field=models.DateTimeField(help_text='%d/%m/%y %H:%M:%S'), + model_name="whitelist", + name="date_end", + field=models.DateTimeField(help_text="%d/%m/%y %H:%M:%S"), ), ] diff --git a/users/migrations/0017_auto_20160707_0105.py b/users/migrations/0017_auto_20160707_0105.py index c0459213..2ce1d48e 100644 --- a/users/migrations/0017_auto_20160707_0105.py +++ b/users/migrations/0017_auto_20160707_0105.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -27,7 +27,7 @@ from django.db import models, migrations def move_passwords(apps, schema_editor): - User = apps.get_model('users', 'User') + User = apps.get_model("users", "User") for row in User.objects.all(): row.password = row.pwd_ssha row.save() @@ -35,23 +35,23 @@ def move_passwords(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('users', '0016_auto_20160706_1220'), - ] + dependencies = [("users", "0016_auto_20160706_1220")] operations = [ migrations.AddField( - model_name='user', - name='last_login', - field=models.DateTimeField(null=True, blank=True, verbose_name='last login'), + model_name="user", + name="last_login", + field=models.DateTimeField( + null=True, blank=True, verbose_name="last login" + ), ), migrations.AddField( - model_name='user', - name='password', - field=models.CharField(verbose_name='password', default='!', max_length=128), + model_name="user", + name="password", + field=models.CharField( + verbose_name="password", default="!", max_length=128 + ), preserve_default=False, ), - migrations.RunPython( - move_passwords, - reverse_code=migrations.RunPython.noop), + migrations.RunPython(move_passwords, reverse_code=migrations.RunPython.noop), ] diff --git a/users/migrations/0018_auto_20160707_0115.py b/users/migrations/0018_auto_20160707_0115.py index dc97f9d4..39e72a2c 100644 --- a/users/migrations/0018_auto_20160707_0115.py +++ b/users/migrations/0018_auto_20160707_0115.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,13 +28,6 @@ from django.db import models, migrations class Migration(migrations.Migration): - dependencies = [ - ('users', '0017_auto_20160707_0105'), - ] + dependencies = [("users", "0017_auto_20160707_0105")] - operations = [ - migrations.RemoveField( - model_name='user', - name='pwd_ssha', - ), - ] + operations = [migrations.RemoveField(model_name="user", name="pwd_ssha")] diff --git a/users/migrations/0019_auto_20160708_1633.py b/users/migrations/0019_auto_20160708_1633.py index 13a72514..e4cbdbd8 100644 --- a/users/migrations/0019_auto_20160708_1633.py +++ b/users/migrations/0019_auto_20160708_1633.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0018_auto_20160707_0115'), - ] + dependencies = [("users", "0018_auto_20160707_0115")] operations = [ migrations.AlterField( - model_name='listright', - name='listright', + model_name="listright", + name="listright", field=models.CharField(unique=True, max_length=255), - ), + ) ] diff --git a/users/migrations/0020_request.py b/users/migrations/0020_request.py index 8fa7a498..6d059c6a 100644 --- a/users/migrations/0020_request.py +++ b/users/migrations/0020_request.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -30,20 +30,37 @@ from django.conf import settings class Migration(migrations.Migration): - dependencies = [ - ('users', '0019_auto_20160708_1633'), - ] + dependencies = [("users", "0019_auto_20160708_1633")] operations = [ migrations.CreateModel( - name='Request', + name="Request", fields=[ - ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), - ('type', models.CharField(choices=[('PW', 'Mot de passe'), ('EM', 'Email')], max_length=2)), - ('token', models.CharField(max_length=32)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('expires_at', models.DateTimeField()), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=django.db.models.deletion.PROTECT)), + ( + "id", + models.AutoField( + auto_created=True, + verbose_name="ID", + primary_key=True, + serialize=False, + ), + ), + ( + "type", + models.CharField( + choices=[("PW", "Mot de passe"), ("EM", "Email")], max_length=2 + ), + ), + ("token", models.CharField(max_length=32)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("expires_at", models.DateTimeField()), + ( + "user", + models.ForeignKey( + to=settings.AUTH_USER_MODEL, + on_delete=django.db.models.deletion.PROTECT, + ), + ), ], - ), + ) ] diff --git a/users/migrations/0021_ldapuser.py b/users/migrations/0021_ldapuser.py index e2ee9844..1d475027 100644 --- a/users/migrations/0021_ldapuser.py +++ b/users/migrations/0021_ldapuser.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,29 +29,69 @@ import ldapdb.models.fields class Migration(migrations.Migration): - dependencies = [ - ('users', '0020_request'), - ] + dependencies = [("users", "0020_request")] operations = [ migrations.CreateModel( - name='LdapUser', + name="LdapUser", fields=[ - ('dn', models.CharField(max_length=200)), - ('gid', ldapdb.models.fields.IntegerField(db_column='gidNumber')), - ('name', ldapdb.models.fields.CharField(primary_key=True, max_length=200, db_column='cn', serialize=False)), - ('uid', ldapdb.models.fields.CharField(max_length=200, db_column='uid')), - ('uidNumber', ldapdb.models.fields.IntegerField(unique=True, db_column='uidNumber')), - ('sn', ldapdb.models.fields.CharField(max_length=200, db_column='sn')), - ('loginShell', ldapdb.models.fields.CharField(default='/bin/zsh', max_length=200, db_column='loginShell')), - ('mail', ldapdb.models.fields.CharField(max_length=200, db_column='mail')), - ('given_name', ldapdb.models.fields.CharField(max_length=200, db_column='givenName')), - ('home_directory', ldapdb.models.fields.CharField(max_length=200, db_column='homeDirectory')), - ('dialupAccess', ldapdb.models.fields.CharField(max_length=200, db_column='dialupAccess')), - ('mac_list', ldapdb.models.fields.CharField(max_length=200, db_column='radiusCallingStationId')), + ("dn", models.CharField(max_length=200)), + ("gid", ldapdb.models.fields.IntegerField(db_column="gidNumber")), + ( + "name", + ldapdb.models.fields.CharField( + primary_key=True, + max_length=200, + db_column="cn", + serialize=False, + ), + ), + ( + "uid", + ldapdb.models.fields.CharField(max_length=200, db_column="uid"), + ), + ( + "uidNumber", + ldapdb.models.fields.IntegerField( + unique=True, db_column="uidNumber" + ), + ), + ("sn", ldapdb.models.fields.CharField(max_length=200, db_column="sn")), + ( + "loginShell", + ldapdb.models.fields.CharField( + default="/bin/zsh", max_length=200, db_column="loginShell" + ), + ), + ( + "mail", + ldapdb.models.fields.CharField(max_length=200, db_column="mail"), + ), + ( + "given_name", + ldapdb.models.fields.CharField( + max_length=200, db_column="givenName" + ), + ), + ( + "home_directory", + ldapdb.models.fields.CharField( + max_length=200, db_column="homeDirectory" + ), + ), + ( + "dialupAccess", + ldapdb.models.fields.CharField( + max_length=200, db_column="dialupAccess" + ), + ), + ( + "mac_list", + ldapdb.models.fields.CharField( + max_length=200, db_column="radiusCallingStationId" + ), + ), ], - options={ - 'abstract': False, - }, - ), + options={"abstract": False}, + ) ] diff --git a/users/migrations/0022_ldapuser_sambasid.py b/users/migrations/0022_ldapuser_sambasid.py index 4bee551f..18d214bd 100644 --- a/users/migrations/0022_ldapuser_sambasid.py +++ b/users/migrations/0022_ldapuser_sambasid.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,15 +29,24 @@ import ldapdb.models.fields class Migration(migrations.Migration): - dependencies = [ - ('users', '0021_ldapuser'), - ] + dependencies = [("users", "0021_ldapuser")] operations = [ - migrations.AddField( - model_name='ldapuser', - name='sambaSID', - field=ldapdb.models.fields.IntegerField(db_column='sambaSID', unique=True, null=True), - preserve_default=False, - ), + migrations.SeparateDatabaseAndState( + state_operations=[ + migrations.AddField( + model_name="ldapuser", + name="sambaSID", + field=ldapdb.models.fields.IntegerField( + db_column="sambaSID", unique=True, null=True + ), + preserve_default=False, + ) + ], + database_operations=[ + migrations.RunSQL( + 'ALTER TABLE "users_ldapuser" ADD COLUMN "sambaSID" integer NULL UNIQUE;' + ) + ], + ) ] diff --git a/users/migrations/0023_auto_20160724_1908.py b/users/migrations/0023_auto_20160724_1908.py index 14e9fee7..3e9baf2c 100644 --- a/users/migrations/0023_auto_20160724_1908.py +++ b/users/migrations/0023_auto_20160724_1908.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,14 +29,12 @@ import ldapdb.models.fields class Migration(migrations.Migration): - dependencies = [ - ('users', '0022_ldapuser_sambasid'), - ] + dependencies = [("users", "0022_ldapuser_sambasid")] operations = [ migrations.AlterField( - model_name='ldapuser', - name='sambaSID', - field=ldapdb.models.fields.IntegerField(db_column='sambaSID', unique=True), - ), + model_name="ldapuser", + name="sambaSID", + field=ldapdb.models.fields.IntegerField(db_column="sambaSID", unique=True), + ) ] diff --git a/users/migrations/0024_remove_ldapuser_mac_list.py b/users/migrations/0024_remove_ldapuser_mac_list.py index c0a218f6..72f3b11a 100644 --- a/users/migrations/0024_remove_ldapuser_mac_list.py +++ b/users/migrations/0024_remove_ldapuser_mac_list.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,13 +28,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0023_auto_20160724_1908'), - ] + dependencies = [("users", "0023_auto_20160724_1908")] - operations = [ - migrations.RemoveField( - model_name='ldapuser', - name='mac_list', - ), - ] + operations = [migrations.RemoveField(model_name="ldapuser", name="mac_list")] diff --git a/users/migrations/0025_listshell.py b/users/migrations/0025_listshell.py index f96bc335..e3a8f7bd 100644 --- a/users/migrations/0025_listshell.py +++ b/users/migrations/0025_listshell.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,16 +28,22 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0024_remove_ldapuser_mac_list'), - ] + dependencies = [("users", "0024_remove_ldapuser_mac_list")] operations = [ migrations.CreateModel( - name='ListShell', + name="ListShell", fields=[ - ('id', models.AutoField(auto_created=True, serialize=False, primary_key=True, verbose_name='ID')), - ('shell', models.CharField(unique=True, max_length=255)), + ( + "id", + models.AutoField( + auto_created=True, + serialize=False, + primary_key=True, + verbose_name="ID", + ), + ), + ("shell", models.CharField(unique=True, max_length=255)), ], - ), + ) ] diff --git a/users/migrations/0026_user_shell.py b/users/migrations/0026_user_shell.py index f506ae78..c7f14040 100644 --- a/users/migrations/0026_user_shell.py +++ b/users/migrations/0026_user_shell.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,15 +29,17 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('users', '0025_listshell'), - ] + dependencies = [("users", "0025_listshell")] operations = [ migrations.AddField( - model_name='user', - name='shell', - field=models.ForeignKey(to='users.ListShell', default=1, on_delete=django.db.models.deletion.PROTECT), + model_name="user", + name="shell", + field=models.ForeignKey( + to="users.ListShell", + default=1, + on_delete=django.db.models.deletion.PROTECT, + ), preserve_default=False, - ), + ) ] diff --git a/users/migrations/0027_auto_20160726_0216.py b/users/migrations/0027_auto_20160726_0216.py index 03309afe..0d09063b 100644 --- a/users/migrations/0027_auto_20160726_0216.py +++ b/users/migrations/0027_auto_20160726_0216.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -30,21 +30,28 @@ import ldapdb.models.fields class Migration(migrations.Migration): - dependencies = [ - ('users', '0026_user_shell'), - ] + dependencies = [("users", "0026_user_shell")] operations = [ migrations.CreateModel( - name='LdapUserGroup', + name="LdapUserGroup", fields=[ - ('dn', models.CharField(max_length=200)), - ('gid', ldapdb.models.fields.IntegerField(db_column='gidNumber')), - ('members', ldapdb.models.fields.ListField(db_column='memberUid', blank=True)), - ('name', ldapdb.models.fields.CharField(db_column='cn', primary_key=True, serialize=False, max_length=200)), + ("dn", models.CharField(max_length=200)), + ("gid", ldapdb.models.fields.IntegerField(db_column="gidNumber")), + ( + "members", + ldapdb.models.fields.ListField(db_column="memberUid", blank=True), + ), + ( + "name", + ldapdb.models.fields.CharField( + db_column="cn", + primary_key=True, + serialize=False, + max_length=200, + ), + ), ], - options={ - 'abstract': False, - }, - ), + options={"abstract": False}, + ) ] diff --git a/users/migrations/0028_auto_20160726_0227.py b/users/migrations/0028_auto_20160726_0227.py index b0ae36f0..5191e228 100644 --- a/users/migrations/0028_auto_20160726_0227.py +++ b/users/migrations/0028_auto_20160726_0227.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -30,39 +30,70 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('users', '0027_auto_20160726_0216'), - ] + dependencies = [("users", "0027_auto_20160726_0216")] operations = [ - migrations.AddField( - model_name='ldapuser', - name='display_name', - field=ldapdb.models.fields.CharField(null=True, blank=True, max_length=200, db_column='displayName'), + migrations.SeparateDatabaseAndState( + state_operations=[ + migrations.AddField( + model_name="ldapuser", + name="display_name", + field=ldapdb.models.fields.CharField( + null=True, blank=True, max_length=200, db_column="displayName" + ), + ), + migrations.AddField( + model_name="ldapuser", + name="sambat_nt_password", + field=ldapdb.models.fields.CharField( + null=True, + blank=True, + max_length=200, + db_column="sambaNTPassword", + ), + ), + migrations.AddField( + model_name="ldapuser", + name="user_password", + field=ldapdb.models.fields.CharField( + null=True, blank=True, max_length=200, db_column="userPassword" + ), + ), + ], + database_operations=[ + migrations.RunSQL( + 'ALTER TABLE users_ldapuser ADD COLUMN "displayName" varchar(200) NULL;' + ), + migrations.RunSQL( + 'ALTER TABLE users_ldapuser ADD COLUMN "sambaNTPassword" varchar(200) NULL;' + ), + migrations.RunSQL( + 'ALTER TABLE users_ldapuser ADD COLUMN "userPassword" varchar(200) NULL;' + ), + ], ), migrations.AddField( - model_name='ldapuser', - name='macs', - field=ldapdb.models.fields.ListField(null=True, blank=True, max_length=200, db_column='radiusCallingStationId'), + model_name="ldapuser", + name="macs", + field=ldapdb.models.fields.ListField( + null=True, + blank=True, + max_length=200, + db_column="radiusCallingStationId", + ), ), migrations.AddField( - model_name='ldapuser', - name='sambat_nt_password', - field=ldapdb.models.fields.CharField(null=True, blank=True, max_length=200, db_column='sambaNTPassword'), - ), - migrations.AddField( - model_name='ldapuser', - name='user_password', - field=ldapdb.models.fields.CharField(null=True, blank=True, max_length=200, db_column='userPassword'), - ), - migrations.AddField( - model_name='listright', - name='gid', + model_name="listright", + name="gid", field=models.IntegerField(null=True, unique=True), ), migrations.AlterField( - model_name='user', - name='shell', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, default=1, to='users.ListShell'), + model_name="user", + name="shell", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + default=1, + to="users.ListShell", + ), ), ] diff --git a/users/migrations/0029_auto_20160726_0229.py b/users/migrations/0029_auto_20160726_0229.py index 62f765f9..1abd06b2 100644 --- a/users/migrations/0029_auto_20160726_0229.py +++ b/users/migrations/0029_auto_20160726_0229.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,14 +29,14 @@ import ldapdb.models.fields class Migration(migrations.Migration): - dependencies = [ - ('users', '0028_auto_20160726_0227'), - ] + dependencies = [("users", "0028_auto_20160726_0227")] operations = [ migrations.AlterField( - model_name='ldapuser', - name='display_name', - field=ldapdb.models.fields.CharField(db_column='displayName', max_length=200), - ), + model_name="ldapuser", + name="display_name", + field=ldapdb.models.fields.CharField( + db_column="displayName", max_length=200 + ), + ) ] diff --git a/users/migrations/0030_auto_20160726_0357.py b/users/migrations/0030_auto_20160726_0357.py index 753f1ad7..9ccca318 100644 --- a/users/migrations/0030_auto_20160726_0357.py +++ b/users/migrations/0030_auto_20160726_0357.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -30,14 +30,14 @@ import ldapdb.models.fields class Migration(migrations.Migration): - dependencies = [ - ('users', '0029_auto_20160726_0229'), - ] + dependencies = [("users", "0029_auto_20160726_0229")] operations = [ migrations.AlterField( - model_name='ldapuser', - name='display_name', - field=ldapdb.models.fields.CharField(null=True, max_length=200, db_column='displayName', blank=True), - ), + model_name="ldapuser", + name="display_name", + field=ldapdb.models.fields.CharField( + null=True, max_length=200, db_column="displayName", blank=True + ), + ) ] diff --git a/users/migrations/0031_auto_20160726_0359.py b/users/migrations/0031_auto_20160726_0359.py index 7df9b3a0..5cf65e41 100644 --- a/users/migrations/0031_auto_20160726_0359.py +++ b/users/migrations/0031_auto_20160726_0359.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -30,14 +30,17 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('users', '0030_auto_20160726_0357'), - ] + dependencies = [("users", "0030_auto_20160726_0357")] operations = [ migrations.AlterField( - model_name='user', - name='shell', - field=models.ForeignKey(to='users.ListShell', on_delete=django.db.models.deletion.PROTECT, null=True, blank=True), - ), + model_name="user", + name="shell", + field=models.ForeignKey( + to="users.ListShell", + on_delete=django.db.models.deletion.PROTECT, + null=True, + blank=True, + ), + ) ] diff --git a/users/migrations/0032_auto_20160727_2122.py b/users/migrations/0032_auto_20160727_2122.py index cdafbd57..1dc27579 100644 --- a/users/migrations/0032_auto_20160727_2122.py +++ b/users/migrations/0032_auto_20160727_2122.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -30,32 +30,60 @@ import ldapdb.models.fields class Migration(migrations.Migration): - dependencies = [ - ('users', '0031_auto_20160726_0359'), - ] + dependencies = [("users", "0031_auto_20160726_0359")] operations = [ migrations.CreateModel( - name='LdapServiceUser', + name="LdapServiceUser", fields=[ - ('dn', models.CharField(max_length=200)), - ('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, serialize=False, primary_key=True)), - ('user_password', ldapdb.models.fields.CharField(db_column='userPassword', blank=True, max_length=200, null=True)), + ("dn", models.CharField(max_length=200)), + ( + "name", + ldapdb.models.fields.CharField( + db_column="cn", + max_length=200, + serialize=False, + primary_key=True, + ), + ), + ( + "user_password", + ldapdb.models.fields.CharField( + db_column="userPassword", blank=True, max_length=200, null=True + ), + ), ], - options={ - 'abstract': False, - }, + options={"abstract": False}, ), migrations.CreateModel( - name='ServiceUser', + name="ServiceUser", fields=[ - ('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)), - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(blank=True, verbose_name='last login', null=True)), - ('pseudo', models.CharField(max_length=32, help_text='Doit contenir uniquement des lettres, chiffres, ou tirets', unique=True, validators=[users.models.linux_user_validator])), + ( + "id", + models.AutoField( + verbose_name="ID", + auto_created=True, + primary_key=True, + serialize=False, + ), + ), + ("password", models.CharField(max_length=128, verbose_name="password")), + ( + "last_login", + models.DateTimeField( + blank=True, verbose_name="last login", null=True + ), + ), + ( + "pseudo", + models.CharField( + max_length=32, + help_text="Doit contenir uniquement des lettres, chiffres, ou tirets", + unique=True, + validators=[users.models.linux_user_validator], + ), + ), ], - options={ - 'abstract': False, - }, + options={"abstract": False}, ), ] diff --git a/users/migrations/0033_remove_ldapuser_loginshell.py b/users/migrations/0033_remove_ldapuser_loginshell.py index 4d73f138..70be687d 100644 --- a/users/migrations/0033_remove_ldapuser_loginshell.py +++ b/users/migrations/0033_remove_ldapuser_loginshell.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,13 +28,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0032_auto_20160727_2122'), - ] + dependencies = [("users", "0032_auto_20160727_2122")] - operations = [ - migrations.RemoveField( - model_name='ldapuser', - name='loginShell', - ), - ] + operations = [migrations.RemoveField(model_name="ldapuser", name="loginShell")] diff --git a/users/migrations/0034_auto_20161018_0037.py b/users/migrations/0034_auto_20161018_0037.py index 2275843b..ed0fbab0 100644 --- a/users/migrations/0034_auto_20161018_0037.py +++ b/users/migrations/0034_auto_20161018_0037.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -31,29 +31,41 @@ import users.models class Migration(migrations.Migration): - dependencies = [ - ('users', '0033_remove_ldapuser_loginshell'), - ] + dependencies = [("users", "0033_remove_ldapuser_loginshell")] operations = [ - migrations.AddField( - model_name='ldapuser', - name='login_shell', - field=ldapdb.models.fields.CharField(blank=True, db_column='loginShell', max_length=200, null=True), + migrations.SeparateDatabaseAndState( + state_operations=[ + migrations.AddField( + model_name="ldapuser", + name="login_shell", + field=ldapdb.models.fields.CharField( + blank=True, db_column="loginShell", max_length=200, null=True + ), + ) + ], + database_operations=[ + migrations.RunSQL( + 'ALTER TABLE users_ldapuser ADD COLUMN "loginShell" varchar(200) NULL;' + ) + ], ), migrations.AddField( - model_name='user', - name='rezo_rez_uid', + model_name="user", + name="rezo_rez_uid", field=models.IntegerField(blank=True, unique=True, null=True), ), migrations.AddField( - model_name='user', - name='uid_number', - field=models.IntegerField(unique=True), + model_name="user", name="uid_number", field=models.IntegerField(unique=True) ), migrations.AlterField( - model_name='user', - name='school', - field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.PROTECT, to='users.School', null=True), + model_name="user", + name="school", + field=models.ForeignKey( + blank=True, + on_delete=django.db.models.deletion.PROTECT, + to="users.School", + null=True, + ), ), ] diff --git a/users/migrations/0035_auto_20161018_0046.py b/users/migrations/0035_auto_20161018_0046.py index aea0d13d..ab27d7e2 100644 --- a/users/migrations/0035_auto_20161018_0046.py +++ b/users/migrations/0035_auto_20161018_0046.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -29,14 +29,14 @@ import users.models class Migration(migrations.Migration): - dependencies = [ - ('users', '0034_auto_20161018_0037'), - ] + dependencies = [("users", "0034_auto_20161018_0037")] operations = [ migrations.AlterField( - model_name='user', - name='uid_number', - field=models.IntegerField(unique=True, default=users.models.get_fresh_user_uid), - ), + model_name="user", + name="uid_number", + field=models.IntegerField( + unique=True, default=users.models.get_fresh_user_uid + ), + ) ] diff --git a/users/migrations/0036_auto_20161022_2146.py b/users/migrations/0036_auto_20161022_2146.py index 16016a27..7d7a4521 100644 --- a/users/migrations/0036_auto_20161022_2146.py +++ b/users/migrations/0036_auto_20161022_2146.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,19 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0035_auto_20161018_0046'), - ] + dependencies = [("users", "0035_auto_20161018_0046")] operations = [ migrations.AlterField( - model_name='user', - name='state', - field=models.IntegerField(default=0, choices=[(0, 'STATE_ACTIVE'), (1, 'STATE_DISABLED'), (2, 'STATE_ARCHIVE')]), - ), + model_name="user", + name="state", + field=models.IntegerField( + default=0, + choices=[ + (0, "STATE_ACTIVE"), + (1, "STATE_DISABLED"), + (2, "STATE_ARCHIVE"), + ], + ), + ) ] diff --git a/users/migrations/0037_auto_20161028_1906.py b/users/migrations/0037_auto_20161028_1906.py index 4022ba9f..3e2c3887 100644 --- a/users/migrations/0037_auto_20161028_1906.py +++ b/users/migrations/0037_auto_20161028_1906.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,24 +28,22 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0036_auto_20161022_2146'), - ] + dependencies = [("users", "0036_auto_20161022_2146")] operations = [ migrations.AlterField( - model_name='ldapserviceuser', - name='dn', + model_name="ldapserviceuser", + name="dn", field=models.CharField(serialize=False, primary_key=True, max_length=200), ), migrations.AlterField( - model_name='ldapuser', - name='dn', + model_name="ldapuser", + name="dn", field=models.CharField(serialize=False, primary_key=True, max_length=200), ), migrations.AlterField( - model_name='ldapusergroup', - name='dn', + model_name="ldapusergroup", + name="dn", field=models.CharField(serialize=False, primary_key=True, max_length=200), ), ] diff --git a/users/migrations/0038_auto_20161031_0258.py b/users/migrations/0038_auto_20161031_0258.py index bc10db42..bb3f9f86 100644 --- a/users/migrations/0038_auto_20161031_0258.py +++ b/users/migrations/0038_auto_20161031_0258.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,24 +28,20 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0037_auto_20161028_1906'), - ] + dependencies = [("users", "0037_auto_20161028_1906")] operations = [ migrations.AlterField( - model_name='ldapserviceuser', - name='dn', + model_name="ldapserviceuser", + name="dn", field=models.CharField(max_length=200), ), migrations.AlterField( - model_name='ldapuser', - name='dn', - field=models.CharField(max_length=200), + model_name="ldapuser", name="dn", field=models.CharField(max_length=200) ), migrations.AlterField( - model_name='ldapusergroup', - name='dn', + model_name="ldapusergroup", + name="dn", field=models.CharField(max_length=200), ), ] diff --git a/users/migrations/0039_auto_20161119_0033.py b/users/migrations/0039_auto_20161119_0033.py index f4028b1f..9c08ca33 100644 --- a/users/migrations/0039_auto_20161119_0033.py +++ b/users/migrations/0039_auto_20161119_0033.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,24 +28,22 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0038_auto_20161031_0258'), - ] + dependencies = [("users", "0038_auto_20161031_0258")] operations = [ migrations.AlterField( - model_name='ldapserviceuser', - name='dn', + model_name="ldapserviceuser", + name="dn", field=models.CharField(serialize=False, max_length=200, primary_key=True), ), migrations.AlterField( - model_name='ldapuser', - name='dn', + model_name="ldapuser", + name="dn", field=models.CharField(serialize=False, max_length=200, primary_key=True), ), migrations.AlterField( - model_name='ldapusergroup', - name='dn', + model_name="ldapusergroup", + name="dn", field=models.CharField(serialize=False, max_length=200, primary_key=True), ), ] diff --git a/users/migrations/0040_auto_20161119_1709.py b/users/migrations/0040_auto_20161119_1709.py index cd917def..d1bd88ab 100644 --- a/users/migrations/0040_auto_20161119_1709.py +++ b/users/migrations/0040_auto_20161119_1709.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,24 +28,20 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0039_auto_20161119_0033'), - ] + dependencies = [("users", "0039_auto_20161119_0033")] operations = [ migrations.AlterField( - model_name='ldapserviceuser', - name='dn', + model_name="ldapserviceuser", + name="dn", field=models.CharField(max_length=200), ), migrations.AlterField( - model_name='ldapuser', - name='dn', - field=models.CharField(max_length=200), + model_name="ldapuser", name="dn", field=models.CharField(max_length=200) ), migrations.AlterField( - model_name='ldapusergroup', - name='dn', + model_name="ldapusergroup", + name="dn", field=models.CharField(max_length=200), ), ] diff --git a/users/migrations/0041_listright_details.py b/users/migrations/0041_listright_details.py index 324856b3..a28b246c 100644 --- a/users/migrations/0041_listright_details.py +++ b/users/migrations/0041_listright_details.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,14 +28,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0040_auto_20161119_1709'), - ] + dependencies = [("users", "0040_auto_20161119_1709")] operations = [ migrations.AddField( - model_name='listright', - name='details', - field=models.CharField(help_text='Description', max_length=255, blank=True), - ), + model_name="listright", + name="details", + field=models.CharField(help_text="Description", max_length=255, blank=True), + ) ] diff --git a/users/migrations/0042_auto_20161126_2028.py b/users/migrations/0042_auto_20161126_2028.py index ca732521..491ecec3 100644 --- a/users/migrations/0042_auto_20161126_2028.py +++ b/users/migrations/0042_auto_20161126_2028.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -28,24 +28,22 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0041_listright_details'), - ] + dependencies = [("users", "0041_listright_details")] operations = [ migrations.AlterField( - model_name='ldapserviceuser', - name='dn', + model_name="ldapserviceuser", + name="dn", field=models.CharField(serialize=False, primary_key=True, max_length=200), ), migrations.AlterField( - model_name='ldapuser', - name='dn', + model_name="ldapuser", + name="dn", field=models.CharField(serialize=False, primary_key=True, max_length=200), ), migrations.AlterField( - model_name='ldapusergroup', - name='dn', + model_name="ldapusergroup", + name="dn", field=models.CharField(serialize=False, primary_key=True, max_length=200), ), ] diff --git a/users/migrations/0043_auto_20161224_1156.py b/users/migrations/0043_auto_20161224_1156.py index 18aa1e35..0502ea7b 100644 --- a/users/migrations/0043_auto_20161224_1156.py +++ b/users/migrations/0043_auto_20161224_1156.py @@ -6,24 +6,20 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0042_auto_20161126_2028'), - ] + dependencies = [("users", "0042_auto_20161126_2028")] operations = [ migrations.AlterField( - model_name='ldapserviceuser', - name='dn', + model_name="ldapserviceuser", + name="dn", field=models.CharField(max_length=200), ), migrations.AlterField( - model_name='ldapuser', - name='dn', - field=models.CharField(max_length=200), + model_name="ldapuser", name="dn", field=models.CharField(max_length=200) ), migrations.AlterField( - model_name='ldapusergroup', - name='dn', + model_name="ldapusergroup", + name="dn", field=models.CharField(max_length=200), ), ] diff --git a/users/migrations/0043_ban_state.py b/users/migrations/0043_ban_state.py index 897415e4..266bdfe5 100644 --- a/users/migrations/0043_ban_state.py +++ b/users/migrations/0043_ban_state.py @@ -6,14 +6,15 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0042_auto_20161126_2028'), - ] + dependencies = [("users", "0042_auto_20161126_2028")] operations = [ migrations.AddField( - model_name='ban', - name='state', - field=models.IntegerField(choices=[(0, 'STATE_HARD'), (1, 'STATE_SOFT'), (2, 'STATE_BRIDAGE')], default=0), - ), + model_name="ban", + name="state", + field=models.IntegerField( + choices=[(0, "STATE_HARD"), (1, "STATE_SOFT"), (2, "STATE_BRIDAGE")], + default=0, + ), + ) ] diff --git a/users/migrations/0044_user_ssh_public_key.py b/users/migrations/0044_user_ssh_public_key.py index e25194d2..7554ee6f 100644 --- a/users/migrations/0044_user_ssh_public_key.py +++ b/users/migrations/0044_user_ssh_public_key.py @@ -6,14 +6,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0043_auto_20161224_1156'), - ] + dependencies = [("users", "0043_auto_20161224_1156")] operations = [ migrations.AddField( - model_name='user', - name='ssh_public_key', + model_name="user", + name="ssh_public_key", field=models.CharField(max_length=2047, null=True, blank=True), - ), + ) ] diff --git a/users/migrations/0045_merge.py b/users/migrations/0045_merge.py index fa8b8712..90761425 100644 --- a/users/migrations/0045_merge.py +++ b/users/migrations/0045_merge.py @@ -6,10 +6,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0043_ban_state'), - ('users', '0044_user_ssh_public_key'), - ] + dependencies = [("users", "0043_ban_state"), ("users", "0044_user_ssh_public_key")] - operations = [ - ] + operations = [] diff --git a/users/migrations/0046_auto_20170617_1433.py b/users/migrations/0046_auto_20170617_1433.py index 7b846abf..ddb7eb87 100644 --- a/users/migrations/0046_auto_20170617_1433.py +++ b/users/migrations/0046_auto_20170617_1433.py @@ -7,33 +7,35 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0045_merge'), - ] + dependencies = [("users", "0045_merge")] operations = [ - migrations.RemoveField( - model_name='user', - name='ssh_public_key', + migrations.RemoveField(model_name="user", name="ssh_public_key"), + migrations.AlterField( + model_name="ban", + name="state", + field=models.IntegerField( + choices=[ + (0, "HARD (aucun accès)"), + (1, "SOFT (accès local seulement)"), + (2, "BRIDAGE (bridage du débit)"), + ], + default=0, + ), ), migrations.AlterField( - model_name='ban', - name='state', - field=models.IntegerField(choices=[(0, 'HARD (aucun accès)'), (1, 'SOFT (accès local seulement)'), (2, 'BRIDAGE (bridage du débit)')], default=0), - ), - migrations.AlterField( - model_name='ldapserviceuser', - name='dn', + model_name="ldapserviceuser", + name="dn", field=models.CharField(max_length=200, primary_key=True, serialize=False), ), migrations.AlterField( - model_name='ldapuser', - name='dn', + model_name="ldapuser", + name="dn", field=models.CharField(max_length=200, primary_key=True, serialize=False), ), migrations.AlterField( - model_name='ldapusergroup', - name='dn', + model_name="ldapusergroup", + name="dn", field=models.CharField(max_length=200, primary_key=True, serialize=False), ), ] diff --git a/users/migrations/0047_auto_20170618_0156.py b/users/migrations/0047_auto_20170618_0156.py index dc19b38a..6175a378 100644 --- a/users/migrations/0047_auto_20170618_0156.py +++ b/users/migrations/0047_auto_20170618_0156.py @@ -8,25 +8,34 @@ import ldapdb.models.fields class Migration(migrations.Migration): - dependencies = [ - ('users', '0046_auto_20170617_1433'), - ] + dependencies = [("users", "0046_auto_20170617_1433")] operations = [ migrations.CreateModel( - name='LdapServiceUserGroup', + name="LdapServiceUserGroup", fields=[ - ('dn', models.CharField(max_length=200, primary_key=True, serialize=False)), - ('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, serialize=False)), - ('members', ldapdb.models.fields.ListField(blank=True, db_column='member')), + ( + "dn", + models.CharField(max_length=200, primary_key=True, serialize=False), + ), + ( + "name", + ldapdb.models.fields.CharField( + db_column="cn", max_length=200, serialize=False + ), + ), + ( + "members", + ldapdb.models.fields.ListField(blank=True, db_column="member"), + ), ], - options={ - 'abstract': False, - }, + options={"abstract": False}, ), migrations.AddField( - model_name='serviceuser', - name='access_group', - field=models.IntegerField(choices=[(0, 'auth'), (1, 'readonly'), (2, 'usermgmt')], default=1), + model_name="serviceuser", + name="access_group", + field=models.IntegerField( + choices=[(0, "auth"), (1, "readonly"), (2, "usermgmt")], default=1 + ), ), ] diff --git a/users/migrations/0048_auto_20170618_0210.py b/users/migrations/0048_auto_20170618_0210.py index 7ef8c397..8d7ff90d 100644 --- a/users/migrations/0048_auto_20170618_0210.py +++ b/users/migrations/0048_auto_20170618_0210.py @@ -8,14 +8,14 @@ import ldapdb.models.fields class Migration(migrations.Migration): - dependencies = [ - ('users', '0047_auto_20170618_0156'), - ] + dependencies = [("users", "0047_auto_20170618_0156")] operations = [ migrations.AlterField( - model_name='ldapserviceusergroup', - name='name', - field=ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False), - ), + model_name="ldapserviceusergroup", + name="name", + field=ldapdb.models.fields.CharField( + db_column="cn", max_length=200, primary_key=True, serialize=False + ), + ) ] diff --git a/users/migrations/0049_auto_20170618_1424.py b/users/migrations/0049_auto_20170618_1424.py index 773544a0..f73aa4b4 100644 --- a/users/migrations/0049_auto_20170618_1424.py +++ b/users/migrations/0049_auto_20170618_1424.py @@ -7,14 +7,20 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0048_auto_20170618_0210'), - ] + dependencies = [("users", "0048_auto_20170618_0210")] operations = [ migrations.AlterField( - model_name='serviceuser', - name='access_group', - field=models.CharField(choices=[('auth', 'auth'), ('readonly', 'readonly'), ('usermgmt', 'usermgmt')], default='readonly', max_length=32), - ), + model_name="serviceuser", + name="access_group", + field=models.CharField( + choices=[ + ("auth", "auth"), + ("readonly", "readonly"), + ("usermgmt", "usermgmt"), + ], + default="readonly", + max_length=32, + ), + ) ] diff --git a/users/migrations/0050_serviceuser_comment.py b/users/migrations/0050_serviceuser_comment.py index 37bfd5cf..f2503573 100644 --- a/users/migrations/0050_serviceuser_comment.py +++ b/users/migrations/0050_serviceuser_comment.py @@ -7,14 +7,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0049_auto_20170618_1424'), - ] + dependencies = [("users", "0049_auto_20170618_1424")] operations = [ migrations.AddField( - model_name='serviceuser', - name='comment', - field=models.CharField(blank=True, help_text='Commentaire', max_length=255), - ), + model_name="serviceuser", + name="comment", + field=models.CharField(blank=True, help_text="Commentaire", max_length=255), + ) ] diff --git a/users/migrations/0051_user_telephone.py b/users/migrations/0051_user_telephone.py index 32aedebc..ce5a4764 100644 --- a/users/migrations/0051_user_telephone.py +++ b/users/migrations/0051_user_telephone.py @@ -7,14 +7,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0050_serviceuser_comment'), - ] + dependencies = [("users", "0050_serviceuser_comment")] operations = [ migrations.AddField( - model_name='user', - name='telephone', + model_name="user", + name="telephone", field=models.CharField(blank=True, max_length=15, null=True), - ), + ) ] diff --git a/users/migrations/0052_ldapuser_shadowexpire.py b/users/migrations/0052_ldapuser_shadowexpire.py index fa818004..2737d76d 100644 --- a/users/migrations/0052_ldapuser_shadowexpire.py +++ b/users/migrations/0052_ldapuser_shadowexpire.py @@ -8,14 +8,23 @@ import ldapdb.models.fields class Migration(migrations.Migration): - dependencies = [ - ('users', '0051_user_telephone'), - ] + dependencies = [("users", "0051_user_telephone")] operations = [ - migrations.AddField( - model_name='ldapuser', - name='shadowexpire', - field=ldapdb.models.fields.CharField(blank=True, db_column='shadowExpire', max_length=200, null=True), - ), + migrations.SeparateDatabaseAndState( + state_operations=[ + migrations.AddField( + model_name="ldapuser", + name="shadowexpire", + field=ldapdb.models.fields.CharField( + blank=True, db_column="shadowExpire", max_length=200, null=True + ), + ) + ], + database_operations=[ + migrations.RunSQL( + 'ALTER TABLE users_ldapuser ADD COLUMN "shadowExpire" varchar(200) NULL;' + ) + ], + ) ] diff --git a/users/migrations/0053_auto_20170626_2105.py b/users/migrations/0053_auto_20170626_2105.py index 03a23f12..482ba175 100644 --- a/users/migrations/0053_auto_20170626_2105.py +++ b/users/migrations/0053_auto_20170626_2105.py @@ -8,14 +8,14 @@ import ldapdb.models.fields class Migration(migrations.Migration): - dependencies = [ - ('users', '0052_ldapuser_shadowexpire'), - ] + dependencies = [("users", "0052_ldapuser_shadowexpire")] operations = [ migrations.AlterField( - model_name='ldapuser', - name='shadowexpire', - field=ldapdb.models.fields.IntegerField(blank=True, db_column='shadowExpire', null=True), - ), + model_name="ldapuser", + name="shadowexpire", + field=ldapdb.models.fields.IntegerField( + blank=True, db_column="shadowExpire", null=True + ), + ) ] diff --git a/users/migrations/0054_auto_20170626_2219.py b/users/migrations/0054_auto_20170626_2219.py index b22d1e1f..4b3710a5 100644 --- a/users/migrations/0054_auto_20170626_2219.py +++ b/users/migrations/0054_auto_20170626_2219.py @@ -8,14 +8,14 @@ import ldapdb.models.fields class Migration(migrations.Migration): - dependencies = [ - ('users', '0053_auto_20170626_2105'), - ] + dependencies = [("users", "0053_auto_20170626_2105")] operations = [ migrations.AlterField( - model_name='ldapuser', - name='shadowexpire', - field=ldapdb.models.fields.CharField(blank=True, db_column='shadowExpire', max_length=200, null=True), - ), + model_name="ldapuser", + name="shadowexpire", + field=ldapdb.models.fields.CharField( + blank=True, db_column="shadowExpire", max_length=200, null=True + ), + ) ] diff --git a/users/migrations/0055_auto_20171003_0556.py b/users/migrations/0055_auto_20171003_0556.py index 84d901c8..f14f2c68 100644 --- a/users/migrations/0055_auto_20171003_0556.py +++ b/users/migrations/0055_auto_20171003_0556.py @@ -8,14 +8,21 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0054_auto_20170626_2219'), - ] + dependencies = [("users", "0054_auto_20170626_2219")] operations = [ migrations.AlterField( - model_name='listright', - name='listright', - field=models.CharField(max_length=255, unique=True, validators=[django.core.validators.RegexValidator('^[a-z]+$', message='Les groupes unix ne peuvent contenir que des lettres minuscules')]), - ), + model_name="listright", + name="listright", + field=models.CharField( + max_length=255, + unique=True, + validators=[ + django.core.validators.RegexValidator( + "^[a-z]+$", + message="Les groupes unix ne peuvent contenir que des lettres minuscules", + ) + ], + ), + ) ] diff --git a/users/migrations/0056_auto_20171015_2033.py b/users/migrations/0056_auto_20171015_2033.py index 90423340..d319b302 100644 --- a/users/migrations/0056_auto_20171015_2033.py +++ b/users/migrations/0056_auto_20171015_2033.py @@ -9,29 +9,38 @@ import users.models class Migration(migrations.Migration): - dependencies = [ - ('users', '0055_auto_20171003_0556'), - ] + dependencies = [("users", "0055_auto_20171003_0556")] operations = [ migrations.AlterField( - model_name='listright', - name='gid', + model_name="listright", + name="gid", field=models.PositiveIntegerField(null=True, unique=True), ), migrations.AlterField( - model_name='listright', - name='listright', - field=models.CharField(max_length=255, unique=True, validators=[django.core.validators.RegexValidator('^[a-z]+$', message='Les groupes unix ne peuvent contenir que des lettres minuscules')]), + model_name="listright", + name="listright", + field=models.CharField( + max_length=255, + unique=True, + validators=[ + django.core.validators.RegexValidator( + "^[a-z]+$", + message="Les groupes unix ne peuvent contenir que des lettres minuscules", + ) + ], + ), ), migrations.AlterField( - model_name='user', - name='rezo_rez_uid', + model_name="user", + name="rezo_rez_uid", field=models.PositiveIntegerField(blank=True, null=True, unique=True), ), migrations.AlterField( - model_name='user', - name='uid_number', - field=models.PositiveIntegerField(default=users.models.get_fresh_user_uid, unique=True), + model_name="user", + name="uid_number", + field=models.PositiveIntegerField( + default=users.models.get_fresh_user_uid, unique=True + ), ), ] diff --git a/users/migrations/0057_auto_20171023_0301.py b/users/migrations/0057_auto_20171023_0301.py index d8a02c54..f174ff0e 100644 --- a/users/migrations/0057_auto_20171023_0301.py +++ b/users/migrations/0057_auto_20171023_0301.py @@ -9,40 +9,52 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('users', '0056_auto_20171015_2033'), - ] + dependencies = [("users", "0056_auto_20171015_2033")] operations = [ migrations.CreateModel( - name='Adherent', + name="Adherent", fields=[ - ('user_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)), - ('usname', models.CharField(max_length=255)), + ( + "user_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to=settings.AUTH_USER_MODEL, + ), + ), + ("usname", models.CharField(max_length=255)), ], - options={ - 'abstract': False, - }, - bases=('users.user',), + options={"abstract": False}, + bases=("users.user",), ), migrations.CreateModel( - name='Club', + name="Club", fields=[ - ('user_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)), + ( + "user_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to=settings.AUTH_USER_MODEL, + ), + ) ], - options={ - 'abstract': False, - }, - bases=('users.user',), + options={"abstract": False}, + bases=("users.user",), ), - migrations.RunSQL("insert into users_adherent (user_ptr_id, usname) select id, name from users_user", reverse_sql="insert into users_user (name) select usname from users_adherent"), - migrations.RemoveField( - model_name='user', - name='name', + migrations.RunSQL( + "insert into users_adherent (user_ptr_id, usname) select id, name from users_user", + reverse_sql="insert into users_user (name) select usname from users_adherent", ), + migrations.RemoveField(model_name="user", name="name"), migrations.RenameField( - model_name='adherent', - old_name='usname', - new_name='name', + model_name="adherent", old_name="usname", new_name="name" ), -] + ] diff --git a/users/migrations/0058_auto_20171025_0154.py b/users/migrations/0058_auto_20171025_0154.py index 01e64fbc..9ddd97c0 100644 --- a/users/migrations/0058_auto_20171025_0154.py +++ b/users/migrations/0058_auto_20171025_0154.py @@ -5,10 +5,11 @@ from __future__ import unicode_literals from django.db import migrations, models import django.db.models.deletion + def create_move_room(apps, schema_editor): - User = apps.get_model('users', 'User') - Adherent = apps.get_model('users', 'Adherent') - Club = apps.get_model('users', 'Club') + User = apps.get_model("users", "User") + Adherent = apps.get_model("users", "Adherent") + Club = apps.get_model("users", "Club") db_alias = schema_editor.connection.alias users = Adherent.objects.using(db_alias).all() clubs = Club.objects.using(db_alias).all() @@ -21,9 +22,9 @@ def create_move_room(apps, schema_editor): def delete_move_room(apps, schema_editor): - User = apps.get_model('users', 'User') - Adherent = apps.get_model('users', 'Adherent') - Club = apps.get_model('users', 'Club') + User = apps.get_model("users", "User") + Adherent = apps.get_model("users", "Adherent") + Club = apps.get_model("users", "Club") db_alias = schema_editor.connection.alias users = Adherent.objects.using(db_alias).all() clubs = Club.objects.using(db_alias).all() @@ -38,24 +39,31 @@ def delete_move_room(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ('topologie', '0031_auto_20171015_2033'), - ('users', '0057_auto_20171023_0301'), + ("topologie", "0031_auto_20171015_2033"), + ("users", "0057_auto_20171023_0301"), ] operations = [ migrations.AddField( - model_name='adherent', - name='room_adherent', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='topologie.Room'), + model_name="adherent", + name="room_adherent", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="topologie.Room", + ), ), migrations.AddField( - model_name='club', - name='room_club', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='topologie.Room'), + model_name="club", + name="room_club", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="topologie.Room", + ), ), migrations.RunPython(create_move_room, delete_move_room), - migrations.RemoveField( - model_name='user', - name='room', - ), + migrations.RemoveField(model_name="user", name="room"), ] diff --git a/users/migrations/0059_auto_20171025_1854.py b/users/migrations/0059_auto_20171025_1854.py index 0ab2f9c4..ef3dc6be 100644 --- a/users/migrations/0059_auto_20171025_1854.py +++ b/users/migrations/0059_auto_20171025_1854.py @@ -7,19 +7,13 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('users', '0058_auto_20171025_0154'), - ] + dependencies = [("users", "0058_auto_20171025_0154")] operations = [ migrations.RenameField( - model_name='adherent', - old_name='room_adherent', - new_name='room', + model_name="adherent", old_name="room_adherent", new_name="room" ), migrations.RenameField( - model_name='club', - old_name='room_club', - new_name='room', + model_name="club", old_name="room_club", new_name="room" ), ] diff --git a/users/migrations/0060_auto_20171120_0317.py b/users/migrations/0060_auto_20171120_0317.py index a77b24db..ac001931 100644 --- a/users/migrations/0060_auto_20171120_0317.py +++ b/users/migrations/0060_auto_20171120_0317.py @@ -7,19 +7,21 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0059_auto_20171025_1854'), - ] + dependencies = [("users", "0059_auto_20171025_1854")] operations = [ migrations.AddField( - model_name='club', - name='administrators', - field=models.ManyToManyField(blank=True, related_name='club_administrator', to='users.Adherent'), + model_name="club", + name="administrators", + field=models.ManyToManyField( + blank=True, related_name="club_administrator", to="users.Adherent" + ), ), migrations.AddField( - model_name='club', - name='members', - field=models.ManyToManyField(blank=True, related_name='club_members', to='users.Adherent'), + model_name="club", + name="members", + field=models.ManyToManyField( + blank=True, related_name="club_members", to="users.Adherent" + ), ), ] diff --git a/users/migrations/0061_auto_20171230_2033.py b/users/migrations/0061_auto_20171230_2033.py index f2751fe2..198c5718 100644 --- a/users/migrations/0061_auto_20171230_2033.py +++ b/users/migrations/0061_auto_20171230_2033.py @@ -8,24 +8,42 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('auth', '0008_alter_user_username_max_length'), - ('users', '0060_auto_20171120_0317'), + ("auth", "0008_alter_user_username_max_length"), + ("users", "0060_auto_20171120_0317"), ] operations = [ migrations.AddField( - model_name='user', - name='groups', - field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups'), + model_name="user", + name="groups", + field=models.ManyToManyField( + blank=True, + help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", + related_name="user_set", + related_query_name="user", + to="auth.Group", + verbose_name="groups", + ), ), migrations.AddField( - model_name='user', - name='is_superuser', - field=models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status'), + model_name="user", + name="is_superuser", + field=models.BooleanField( + default=False, + help_text="Designates that this user has all permissions without explicitly assigning them.", + verbose_name="superuser status", + ), ), migrations.AddField( - model_name='user', - name='user_permissions', - field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'), + model_name="user", + name="user_permissions", + field=models.ManyToManyField( + blank=True, + help_text="Specific permissions for this user.", + related_name="user_set", + related_query_name="user", + to="auth.Permission", + verbose_name="user permissions", + ), ), ] diff --git a/users/migrations/0062_auto_20171231_0056.py b/users/migrations/0062_auto_20171231_0056.py index 2eaedb50..1815efd3 100644 --- a/users/migrations/0062_auto_20171231_0056.py +++ b/users/migrations/0062_auto_20171231_0056.py @@ -9,8 +9,8 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('auth', '0008_alter_user_username_max_length'), - ('users', '0061_auto_20171230_2033'), + ("auth", "0008_alter_user_username_max_length"), + ("users", "0061_auto_20171230_2033"), ] def create_groups(apps, schema_editor): @@ -19,26 +19,31 @@ class Migration(migrations.Migration): db_alias = schema_editor.connection.alias for gr in listrights.objects.using(db_alias).all(): grp = group() - grp.name=gr.unix_name + grp.name = gr.unix_name grp.save() - gr.group_ptr=grp + gr.group_ptr = grp gr.save() def delete_groups(apps, schema_editor): group = apps.get_model("auth", "Group") - db_alias = schema_editor.connection.alias + db_alias = schema_editor.connection.alias group.objects.using(db_alias).all().delete() operations = [ migrations.RenameField( - model_name='listright', - old_name='listright', - new_name='unix_name', + model_name="listright", old_name="listright", new_name="unix_name" ), migrations.AddField( - model_name='listright', - name='group_ptr', - field=models.OneToOneField(blank=True, null=True, auto_created=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='auth.Group'), + model_name="listright", + name="group_ptr", + field=models.OneToOneField( + blank=True, + null=True, + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + serialize=False, + to="auth.Group", + ), preserve_default=False, ), migrations.RunPython(create_groups, delete_groups), diff --git a/users/migrations/0063_auto_20171231_0140.py b/users/migrations/0063_auto_20171231_0140.py index 56762014..66d78a4a 100644 --- a/users/migrations/0063_auto_20171231_0140.py +++ b/users/migrations/0063_auto_20171231_0140.py @@ -8,22 +8,18 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('users', '0062_auto_20171231_0056'), - ] + dependencies = [("users", "0062_auto_20171231_0056")] def transfer_right(apps, schema_editor): rights = apps.get_model("users", "Right") db_alias = schema_editor.connection.alias for rg in rights.objects.using(db_alias).all(): group = rg.right - u=rg.user + u = rg.user u.groups.add(group.group_ptr) u.save() def untransfer_right(apps, schema_editor): return - operations = [ - migrations.RunPython(transfer_right, untransfer_right), - ] + operations = [migrations.RunPython(transfer_right, untransfer_right)] diff --git a/users/migrations/0064_auto_20171231_0150.py b/users/migrations/0064_auto_20171231_0150.py index a08561eb..daf0ecc0 100644 --- a/users/migrations/0064_auto_20171231_0150.py +++ b/users/migrations/0064_auto_20171231_0150.py @@ -8,34 +8,24 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('users', '0063_auto_20171231_0140'), - ] + dependencies = [("users", "0063_auto_20171231_0140")] operations = [ - migrations.AlterUniqueTogether( - name='right', - unique_together=set([]), - ), - migrations.RemoveField( - model_name='right', - name='right', - ), - migrations.RemoveField( - model_name='right', - name='user', - ), - migrations.DeleteModel( - name='Right', - ), - migrations.RemoveField( - model_name='listright', - name='id', - ), + migrations.AlterUniqueTogether(name="right", unique_together=set([])), + migrations.RemoveField(model_name="right", name="right"), + migrations.RemoveField(model_name="right", name="user"), + migrations.DeleteModel(name="Right"), + migrations.RemoveField(model_name="listright", name="id"), migrations.AlterField( - model_name='listright', - name='group_ptr', - field=models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='auth.Group'), + model_name="listright", + name="group_ptr", + field=models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="auth.Group", + ), ), - ] diff --git a/users/migrations/0065_auto_20171231_2053.py b/users/migrations/0065_auto_20171231_2053.py index 7f2d135b..4df4dcae 100644 --- a/users/migrations/0065_auto_20171231_2053.py +++ b/users/migrations/0065_auto_20171231_2053.py @@ -7,33 +7,53 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('users', '0064_auto_20171231_0150'), - ] + dependencies = [("users", "0064_auto_20171231_0150")] operations = [ migrations.AlterModelOptions( - name='ban', - options={'permissions': (('view_ban', "Peut voir un objet ban quelqu'il soit"),)}, + name="ban", + options={ + "permissions": (("view_ban", "Peut voir un objet ban quelqu'il soit"),) + }, ), migrations.AlterModelOptions( - name='listright', - options={'permissions': (('view_listright', 'Peut voir un objet Group/ListRight'),)}, + name="listright", + options={ + "permissions": ( + ("view_listright", "Peut voir un objet Group/ListRight"), + ) + }, ), migrations.AlterModelOptions( - name='school', - options={'permissions': (('view_school', 'Peut voir un objet school'),)}, + name="school", + options={"permissions": (("view_school", "Peut voir un objet school"),)}, ), migrations.AlterModelOptions( - name='serviceuser', - options={'permissions': (('view_serviceuser', 'Peut voir un objet serviceuser'),)}, + name="serviceuser", + options={ + "permissions": (("view_serviceuser", "Peut voir un objet serviceuser"),) + }, ), migrations.AlterModelOptions( - name='user', - options={'permissions': (('change_user_password', "Peut changer le mot de passe d'un user"), ('change_user_state', "Peut éditer l'etat d'un user"), ('change_user_force', 'Peut forcer un déménagement'), ('change_user_shell', "Peut éditer le shell d'un user"), ('change_user_groups', "Peut éditer les groupes d'un user ! Permission critique"), ('view_user', 'Peut voir un objet user quelquonque'))}, + name="user", + options={ + "permissions": ( + ("change_user_password", "Peut changer le mot de passe d'un user"), + ("change_user_state", "Peut éditer l'etat d'un user"), + ("change_user_force", "Peut forcer un déménagement"), + ("change_user_shell", "Peut éditer le shell d'un user"), + ( + "change_user_groups", + "Peut éditer les groupes d'un user ! Permission critique", + ), + ("view_user", "Peut voir un objet user quelquonque"), + ) + }, ), migrations.AlterModelOptions( - name='whitelist', - options={'permissions': (('view_whitelist', 'Peut voir un objet whitelist'),)}, + name="whitelist", + options={ + "permissions": (("view_whitelist", "Peut voir un objet whitelist"),) + }, ), ] diff --git a/users/migrations/0066_grouppermissions.py b/users/migrations/0066_grouppermissions.py index 090bb82c..c0e86b16 100644 --- a/users/migrations/0066_grouppermissions.py +++ b/users/migrations/0066_grouppermissions.py @@ -8,333 +8,347 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('users', '0065_auto_20171231_2053'), - ('cotisations', '0028_auto_20171231_0007'), - ('machines', '0071_auto_20171231_2100'), - ('preferences', '0025_auto_20171231_2142'), - ('topologie', '0033_auto_20171231_1743'), + ("users", "0065_auto_20171231_2053"), + ("cotisations", "0028_auto_20171231_0007"), + ("machines", "0071_auto_20171231_2100"), + ("preferences", "0025_auto_20171231_2142"), + ("topologie", "0033_auto_20171231_1743"), ] - def transfer_permissions(apps, schema_editor): - permission_groups = {'bofh': ['add_ban', - 'change_ban', - 'delete_ban', - 'view_ban', - 'add_club', - 'change_club', - 'delete_club', - 'add_user', - 'change_user', - 'change_user_force', - 'change_user_password', - 'change_user_shell', - 'view_user', - 'add_whitelist', - 'change_whitelist', - 'delete_whitelist', - 'view_whitelist'], - 'bureau': ['add_logentry', - 'change_logentry', - 'delete_logentry', - 'add_group', - 'change_group', - 'delete_group', - 'add_permission', - 'change_permission', - 'delete_permission', - 'add_adherent', - 'change_adherent', - 'delete_adherent', - 'add_ban', - 'change_ban', - 'delete_ban', - 'view_ban', - 'add_club', - 'change_club', - 'delete_club', - 'add_listright', - 'change_listright', - 'delete_listright', - 'view_listright', - 'add_school', - 'change_school', - 'delete_school', - 'view_school', - 'add_user', - 'change_user', - 'change_user_force', - 'change_user_groups', - 'change_user_password', - 'change_user_shell', - 'change_user_state', - 'delete_user', - 'view_user', - 'add_whitelist', - 'change_whitelist', - 'delete_whitelist', - 'view_whitelist'], - 'cableur': ['add_logentry', - 'view_article', - 'add_banque', - 'change_banque', - 'delete_banque', - 'view_banque', - 'add_cotisation', - 'change_cotisation', - 'delete_cotisation', - 'view_cotisation', - 'add_facture', - 'can_create', - 'can_delete', - 'can_edit', - 'can_view', - 'can_view_all', - 'change_facture', - 'delete_facture', - 'view_facture', - 'view_paiement', - 'add_vente', - 'change_vente', - 'delete_vente', - 'view_vente', - 'add_domain', - 'change_domain', - 'delete_domain', - 'view_domain', - 'use_all_extension', - 'view_extension', - 'add_interface', - 'change_interface', - 'delete_interface', - 'view_interface', - 'view_iplist', - 'view_iptype', - 'add_machine', - 'change_machine', - 'view_machine', - 'view_machinetype', - 'view_mx', - 'view_nas', - 'view_ns', - 'view_ouvertureportlist', - 'view_service', - 'view_soa', - 'view_soa', - 'view_txt', - 'view_vlan', - 'view_assooption', - 'view_generaloption', - 'view_mailmessageoption', - 'view_optionalmachine', - 'view_optionaltopologie', - 'view_optionaluser', - 'view_service', - 'view_constructorswitch', - 'view_modelswitch', - 'view_port', - 'view_room', - 'view_stack', - 'view_switch', - 'add_adherent', - 'change_adherent', - 'view_ban', - 'add_club', - 'change_club', - 'view_listright', - 'add_school', - 'change_school', - 'delete_school', - 'view_school', - 'view_serviceuser', - 'add_user', - 'change_user', - 'change_user_force', - 'change_user_password', - 'view_user', - 'add_whitelist', - 'change_whitelist', - 'delete_whitelist', - 'view_whitelist'], - 'tresorier': ['add_article', - 'change_article', - 'delete_article', - 'view_article', - 'add_banque', - 'change_banque', - 'delete_banque', - 'view_banque', - 'add_cotisation', - 'change_all_cotisation', - 'change_cotisation', - 'delete_cotisation', - 'view_cotisation', - 'add_facture', - 'can_change_control', - 'can_change_pdf', - 'can_create', - 'can_delete', - 'can_edit', - 'can_view', - 'can_view_all', - 'change_all_facture', - 'change_facture', - 'change_facture_control', - 'change_facture_pdf', - 'delete_facture', - 'view_facture', - 'add_paiement', - 'change_paiement', - 'delete_paiement', - 'view_paiement', - 'add_vente', - 'change_all_vente', - 'change_vente', - 'delete_vente', - 'view_vente'], - 'admin': ['add_logentry', - 'change_logentry', - 'delete_logentry', - 'add_assooption', - 'change_assooption', - 'delete_assooption', - 'view_assooption', - 'add_generaloption', - 'change_generaloption', - 'delete_generaloption', - 'view_generaloption', - 'add_mailmessageoption', - 'change_mailmessageoption', - 'delete_mailmessageoption', - 'view_mailmessageoption', - 'add_optionalmachine', - 'change_optionalmachine', - 'delete_optionalmachine', - 'view_optionalmachine', - 'add_optionaltopologie', - 'change_optionaltopologie', - 'delete_optionaltopologie', - 'view_optionaltopologie', - 'add_optionaluser', - 'change_optionaluser', - 'delete_optionaluser', - 'view_optionaluser', - 'add_service', - 'add_services', - 'change_service', - 'change_services', - 'delete_service', - 'delete_services', - 'view_service'], - 'infra': ['add_domain', - 'change_domain', - 'delete_domain', - 'view_domain', - 'add_extension', - 'change_extension', - 'delete_extension', - 'use_all_extension', - 'view_extension', - 'add_interface', - 'change_interface', - 'delete_interface', - 'view_interface', - 'add_iplist', - 'change_iplist', - 'delete_iplist', - 'view_iplist', - 'add_iptype', - 'change_iptype', - 'delete_iptype', - 'use_all_iptype', - 'view_iptype', - 'add_machine', - 'change_machine', - 'change_machine_user', - 'delete_machine', - 'view_machine', - 'add_machinetype', - 'change_machinetype', - 'delete_machinetype', - 'use_all_machinetype', - 'view_machinetype', - 'add_mx', - 'change_mx', - 'delete_mx', - 'view_mx', - 'add_nas', - 'change_nas', - 'delete_nas', - 'view_nas', - 'add_ns', - 'change_ns', - 'delete_ns', - 'view_ns', - 'add_ouvertureport', - 'change_ouvertureport', - 'delete_ouvertureport', - 'add_ouvertureportlist', - 'change_ouvertureportlist', - 'delete_ouvertureportlist', - 'view_ouvertureportlist', - 'add_service', - 'change_service', - 'delete_service', - 'view_service', - 'add_service_link', - 'change_service_link', - 'delete_service_link', - 'add_soa', - 'change_soa', - 'delete_soa', - 'view_soa', - 'add_srv', - 'change_srv', - 'delete_srv', - 'view_soa', - 'add_text', - 'add_txt', - 'change_text', - 'change_txt', - 'delete_text', - 'delete_txt', - 'view_txt', - 'add_vlan', - 'change_vlan', - 'delete_vlan', - 'view_vlan', - 'add_constructorswitch', - 'change_constructorswitch', - 'delete_constructorswitch', - 'view_constructorswitch', - 'add_modelswitch', - 'change_modelswitch', - 'delete_modelswitch', - 'view_modelswitch', - 'add_port', - 'change_port', - 'delete_port', - 'view_port', - 'add_room', - 'change_room', - 'delete_room', - 'view_room', - 'add_stack', - 'change_stack', - 'delete_stack', - 'view_stack', - 'add_switch', - 'change_switch', - 'delete_switch', - 'view_switch', - 'add_listshell', - 'change_listshell', - 'delete_listshell', - 'add_serviceuser', - 'change_serviceuser', - 'delete_serviceuser', - 'view_serviceuser', - 'change_user', - 'view_user']} + def transfer_permissions(apps, schema_editor): + permission_groups = { + "bofh": [ + "add_ban", + "change_ban", + "delete_ban", + "view_ban", + "add_club", + "change_club", + "delete_club", + "add_user", + "change_user", + "change_user_force", + "change_user_password", + "change_user_shell", + "view_user", + "add_whitelist", + "change_whitelist", + "delete_whitelist", + "view_whitelist", + ], + "bureau": [ + "add_logentry", + "change_logentry", + "delete_logentry", + "add_group", + "change_group", + "delete_group", + "add_permission", + "change_permission", + "delete_permission", + "add_adherent", + "change_adherent", + "delete_adherent", + "add_ban", + "change_ban", + "delete_ban", + "view_ban", + "add_club", + "change_club", + "delete_club", + "add_listright", + "change_listright", + "delete_listright", + "view_listright", + "add_school", + "change_school", + "delete_school", + "view_school", + "add_user", + "change_user", + "change_user_force", + "change_user_groups", + "change_user_password", + "change_user_shell", + "change_user_state", + "delete_user", + "view_user", + "add_whitelist", + "change_whitelist", + "delete_whitelist", + "view_whitelist", + ], + "cableur": [ + "add_logentry", + "view_article", + "add_banque", + "change_banque", + "delete_banque", + "view_banque", + "add_cotisation", + "change_cotisation", + "delete_cotisation", + "view_cotisation", + "add_facture", + "can_create", + "can_delete", + "can_edit", + "can_view", + "can_view_all", + "change_facture", + "delete_facture", + "view_facture", + "view_paiement", + "add_vente", + "change_vente", + "delete_vente", + "view_vente", + "add_domain", + "change_domain", + "delete_domain", + "view_domain", + "use_all_extension", + "view_extension", + "add_interface", + "change_interface", + "delete_interface", + "view_interface", + "view_iplist", + "view_iptype", + "add_machine", + "change_machine", + "view_machine", + "view_machinetype", + "view_mx", + "view_nas", + "view_ns", + "view_ouvertureportlist", + "view_service", + "view_soa", + "view_soa", + "view_txt", + "view_vlan", + "view_assooption", + "view_generaloption", + "view_mailmessageoption", + "view_optionalmachine", + "view_optionaltopologie", + "view_optionaluser", + "view_service", + "view_constructorswitch", + "view_modelswitch", + "view_port", + "view_room", + "view_stack", + "view_switch", + "add_adherent", + "change_adherent", + "view_ban", + "add_club", + "change_club", + "view_listright", + "add_school", + "change_school", + "delete_school", + "view_school", + "view_serviceuser", + "add_user", + "change_user", + "change_user_force", + "change_user_password", + "view_user", + "add_whitelist", + "change_whitelist", + "delete_whitelist", + "view_whitelist", + ], + "tresorier": [ + "add_article", + "change_article", + "delete_article", + "view_article", + "add_banque", + "change_banque", + "delete_banque", + "view_banque", + "add_cotisation", + "change_all_cotisation", + "change_cotisation", + "delete_cotisation", + "view_cotisation", + "add_facture", + "can_change_control", + "can_change_pdf", + "can_create", + "can_delete", + "can_edit", + "can_view", + "can_view_all", + "change_all_facture", + "change_facture", + "change_facture_control", + "change_facture_pdf", + "delete_facture", + "view_facture", + "add_paiement", + "change_paiement", + "delete_paiement", + "view_paiement", + "add_vente", + "change_all_vente", + "change_vente", + "delete_vente", + "view_vente", + ], + "admin": [ + "add_logentry", + "change_logentry", + "delete_logentry", + "add_assooption", + "change_assooption", + "delete_assooption", + "view_assooption", + "add_generaloption", + "change_generaloption", + "delete_generaloption", + "view_generaloption", + "add_mailmessageoption", + "change_mailmessageoption", + "delete_mailmessageoption", + "view_mailmessageoption", + "add_optionalmachine", + "change_optionalmachine", + "delete_optionalmachine", + "view_optionalmachine", + "add_optionaltopologie", + "change_optionaltopologie", + "delete_optionaltopologie", + "view_optionaltopologie", + "add_optionaluser", + "change_optionaluser", + "delete_optionaluser", + "view_optionaluser", + "add_service", + "add_services", + "change_service", + "change_services", + "delete_service", + "delete_services", + "view_service", + ], + "infra": [ + "add_domain", + "change_domain", + "delete_domain", + "view_domain", + "add_extension", + "change_extension", + "delete_extension", + "use_all_extension", + "view_extension", + "add_interface", + "change_interface", + "delete_interface", + "view_interface", + "add_iplist", + "change_iplist", + "delete_iplist", + "view_iplist", + "add_iptype", + "change_iptype", + "delete_iptype", + "use_all_iptype", + "view_iptype", + "add_machine", + "change_machine", + "change_machine_user", + "delete_machine", + "view_machine", + "add_machinetype", + "change_machinetype", + "delete_machinetype", + "use_all_machinetype", + "view_machinetype", + "add_mx", + "change_mx", + "delete_mx", + "view_mx", + "add_nas", + "change_nas", + "delete_nas", + "view_nas", + "add_ns", + "change_ns", + "delete_ns", + "view_ns", + "add_ouvertureport", + "change_ouvertureport", + "delete_ouvertureport", + "add_ouvertureportlist", + "change_ouvertureportlist", + "delete_ouvertureportlist", + "view_ouvertureportlist", + "add_service", + "change_service", + "delete_service", + "view_service", + "add_service_link", + "change_service_link", + "delete_service_link", + "add_soa", + "change_soa", + "delete_soa", + "view_soa", + "add_srv", + "change_srv", + "delete_srv", + "view_soa", + "add_text", + "add_txt", + "change_text", + "change_txt", + "delete_text", + "delete_txt", + "view_txt", + "add_vlan", + "change_vlan", + "delete_vlan", + "view_vlan", + "add_constructorswitch", + "change_constructorswitch", + "delete_constructorswitch", + "view_constructorswitch", + "add_modelswitch", + "change_modelswitch", + "delete_modelswitch", + "view_modelswitch", + "add_port", + "change_port", + "delete_port", + "view_port", + "add_room", + "change_room", + "delete_room", + "view_room", + "add_stack", + "change_stack", + "delete_stack", + "view_stack", + "add_switch", + "change_switch", + "delete_switch", + "view_switch", + "add_listshell", + "change_listshell", + "delete_listshell", + "add_serviceuser", + "change_serviceuser", + "delete_serviceuser", + "view_serviceuser", + "change_user", + "view_user", + ], + } rights = apps.get_model("users", "ListRight") permissions = apps.get_model("auth", "Permission") @@ -343,16 +357,22 @@ class Migration(migrations.Migration): for group in permission_groups: lr_object = rights.objects.using(db_alias).filter(unix_name=group).first() if not lr_object: - last = rights.objects.using(db_alias).all().order_by('gid').last() + last = rights.objects.using(db_alias).all().order_by("gid").last() if last: gid = last.gid + 1 else: gid = 501 group_object = groups.objects.using(db_alias).create(name=group) - lr_object = rights.objects.using(db_alias).create(unix_name=group, gid=gid, group_ptr=group_object) + lr_object = rights.objects.using(db_alias).create( + unix_name=group, gid=gid, group_ptr=group_object + ) lr_object = lr_object.group_ptr for permission in permission_groups[group]: - perm = permissions.objects.using(db_alias).filter(codename=permission).first() + perm = ( + permissions.objects.using(db_alias) + .filter(codename=permission) + .first() + ) if perm: lr_object.permissions.add(perm) lr_object.save() @@ -360,6 +380,4 @@ class Migration(migrations.Migration): def untransfer_permissions(apps, schema_editor): return - operations = [ - migrations.RunPython(transfer_permissions, untransfer_permissions), - ] + operations = [migrations.RunPython(transfer_permissions, untransfer_permissions)] diff --git a/users/migrations/0067_serveurpermission.py b/users/migrations/0067_serveurpermission.py index a5ea8387..d1c73347 100644 --- a/users/migrations/0067_serveurpermission.py +++ b/users/migrations/0067_serveurpermission.py @@ -7,30 +7,32 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('users', '0066_grouppermissions'), - ] + dependencies = [("users", "0066_grouppermissions")] def transfer_permissions(apps, schema_editor): db_alias = schema_editor.connection.alias - contenttype = apps.get_model("contenttypes", "ContentType") + contenttype = apps.get_model("contenttypes", "ContentType") rights = apps.get_model("users", "ListRight") permissions = apps.get_model("auth", "Permission") groups = apps.get_model("auth", "Group") machine = apps.get_model("machines", "Machine") - perm = permissions.objects.using(db_alias).filter(codename='serveur').first() + perm = permissions.objects.using(db_alias).filter(codename="serveur").first() if not perm: perm = permissions.objects.using(db_alias).create( - codename='serveur', - name='Serveur', - content_type=contenttype.objects.get_for_model(machine) + codename="serveur", + name="Serveur", + content_type=contenttype.objects.get_for_model(machine), ) - group_object = rights.objects.using(db_alias).filter(unix_name='serveur').first() + group_object = ( + rights.objects.using(db_alias).filter(unix_name="serveur").first() + ) if not group_object: - last_gid = rights.objects.using(db_alias).all().order_by('gid').last().gid + last_gid = rights.objects.using(db_alias).all().order_by("gid").last().gid gid = last_gid + 1 - abstract_group = groups.objects.using(db_alias).create(name='serveur') - group_object = rights.objects.using(db_alias).create(group_ptr=abstract_group, unix_name='serveur', gid=gid) + abstract_group = groups.objects.using(db_alias).create(name="serveur") + group_object = rights.objects.using(db_alias).create( + group_ptr=abstract_group, unix_name="serveur", gid=gid + ) group_object = group_object.group_ptr group_object.permissions.add(perm) group_object.save() @@ -38,6 +40,4 @@ class Migration(migrations.Migration): def untransfer_permissions(apps, schema_editor): return - operations = [ - migrations.RunPython(transfer_permissions, untransfer_permissions), - ] + operations = [migrations.RunPython(transfer_permissions, untransfer_permissions)] diff --git a/users/migrations/0068_auto_20180107_2245.py b/users/migrations/0068_auto_20180107_2245.py index 27c4f446..a1dcee4f 100644 --- a/users/migrations/0068_auto_20180107_2245.py +++ b/users/migrations/0068_auto_20180107_2245.py @@ -7,18 +7,24 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0067_serveurpermission'), - ] + dependencies = [("users", "0067_serveurpermission")] def transfer_permissions(apps, schema_editor): - critical_rights = ['adm', 'admin', 'bureau', 'infra', 'tresorier', 'serveur', 'bofh'] + critical_rights = [ + "adm", + "admin", + "bureau", + "infra", + "tresorier", + "serveur", + "bofh", + ] db_alias = schema_editor.connection.alias rights = apps.get_model("users", "ListRight") for right in critical_rights: rg = rights.objects.using(db_alias).filter(unix_name=right).first() if rg: - rg.critical=True + rg.critical = True rg.save() def untransfer_permissions(apps, schema_editor): @@ -26,12 +32,28 @@ class Migration(migrations.Migration): operations = [ migrations.AlterModelOptions( - name='user', - options={'permissions': (('change_user_password', "Peut changer le mot de passe d'un user"), ('change_user_state', "Peut éditer l'etat d'un user"), ('change_user_force', 'Peut forcer un déménagement'), ('change_user_shell', "Peut éditer le shell d'un user"), ('change_user_groups', "Peut éditer les groupes d'un user ! Permission critique"), ('change_all_users', 'Peut éditer tous les users, y compris ceux dotés de droits. Superdroit'), ('view_user', 'Peut voir un objet user quelquonque'))}, + name="user", + options={ + "permissions": ( + ("change_user_password", "Peut changer le mot de passe d'un user"), + ("change_user_state", "Peut éditer l'etat d'un user"), + ("change_user_force", "Peut forcer un déménagement"), + ("change_user_shell", "Peut éditer le shell d'un user"), + ( + "change_user_groups", + "Peut éditer les groupes d'un user ! Permission critique", + ), + ( + "change_all_users", + "Peut éditer tous les users, y compris ceux dotés de droits. Superdroit", + ), + ("view_user", "Peut voir un objet user quelquonque"), + ) + }, ), migrations.AddField( - model_name='listright', - name='critical', + model_name="listright", + name="critical", field=models.BooleanField(default=False), ), migrations.RunPython(transfer_permissions, untransfer_permissions), diff --git a/users/migrations/0069_club_mailing.py b/users/migrations/0069_club_mailing.py index 17970494..4d72df8d 100644 --- a/users/migrations/0069_club_mailing.py +++ b/users/migrations/0069_club_mailing.py @@ -7,14 +7,10 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0068_auto_20180107_2245'), - ] + dependencies = [("users", "0068_auto_20180107_2245")] operations = [ migrations.AddField( - model_name='club', - name='mailing', - field=models.BooleanField(default=False), - ), + model_name="club", name="mailing", field=models.BooleanField(default=False) + ) ] diff --git a/users/migrations/0070_auto_20180324_1906.py b/users/migrations/0070_auto_20180324_1906.py index fe6ae790..dc44344e 100644 --- a/users/migrations/0070_auto_20180324_1906.py +++ b/users/migrations/0070_auto_20180324_1906.py @@ -7,13 +7,15 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('users', '0069_club_mailing'), - ] + dependencies = [("users", "0069_club_mailing")] operations = [ migrations.AlterModelOptions( - name='listshell', - options={'permissions': (('view_listshell', "Peut voir un objet shell quelqu'il soit"),)}, - ), + name="listshell", + options={ + "permissions": ( + ("view_listshell", "Peut voir un objet shell quelqu'il soit"), + ) + }, + ) ] diff --git a/users/migrations/0071_auto_20180415_1252.py b/users/migrations/0071_auto_20180415_1252.py index faf56a5b..ddaf1a65 100644 --- a/users/migrations/0071_auto_20180415_1252.py +++ b/users/migrations/0071_auto_20180415_1252.py @@ -8,14 +8,21 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0070_auto_20180324_1906'), - ] + dependencies = [("users", "0070_auto_20180324_1906")] operations = [ migrations.AlterField( - model_name='listright', - name='unix_name', - field=models.CharField(max_length=255, unique=True, validators=[django.core.validators.RegexValidator('^[a-z]+$', message='Les groupes unix ne peuvent contenir que des lettres minuscules')]), - ), + model_name="listright", + name="unix_name", + field=models.CharField( + max_length=255, + unique=True, + validators=[ + django.core.validators.RegexValidator( + "^[a-z]+$", + message="Les groupes unix ne peuvent contenir que des lettres minuscules", + ) + ], + ), + ) ] diff --git a/users/migrations/0072_auto_20180426_2021.py b/users/migrations/0072_auto_20180426_2021.py index f60103e0..be4c8aab 100644 --- a/users/migrations/0072_auto_20180426_2021.py +++ b/users/migrations/0072_auto_20180426_2021.py @@ -7,19 +7,13 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0071_auto_20180415_1252'), - ] + dependencies = [("users", "0071_auto_20180415_1252")] operations = [ migrations.AlterField( - model_name='ban', - name='date_end', - field=models.DateTimeField(), + model_name="ban", name="date_end", field=models.DateTimeField() ), migrations.AlterField( - model_name='whitelist', - name='date_end', - field=models.DateTimeField(), + model_name="whitelist", name="date_end", field=models.DateTimeField() ), ] diff --git a/users/migrations/0073_auto_20180629_1614.py b/users/migrations/0073_auto_20180629_1614.py index 72ebffe6..ebcc1bd0 100644 --- a/users/migrations/0073_auto_20180629_1614.py +++ b/users/migrations/0073_auto_20180629_1614.py @@ -9,7 +9,6 @@ import re2o.mixins class Migration(migrations.Migration): - def create_initial_email_address(apps, schema_editor): db_alias = schema_editor.connection.alias User = apps.get_model("users", "User") @@ -17,8 +16,7 @@ class Migration(migrations.Migration): users = User.objects.using(db_alias).all() for user in users: EMailAddress.objects.using(db_alias).create( - local_part=user.pseudo, - user=user + local_part=user.pseudo, user=user ) def delete_all_email_address(apps, schema_editor): @@ -26,32 +24,62 @@ class Migration(migrations.Migration): EMailAddress = apps.get_model("users", "EMailAddress") EMailAddress.objects.using(db_alias).delete() - dependencies = [ - ('users', '0072_auto_20180426_2021'), - ] + dependencies = [("users", "0072_auto_20180426_2021")] operations = [ migrations.CreateModel( - name='EMailAddress', + name="EMailAddress", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('local_part', models.CharField(help_text="Local part of the email address", max_length=128, unique=True)), - ('user', models.ForeignKey(help_text='User of the local email', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "local_part", + models.CharField( + help_text="Local part of the email address", + max_length=128, + unique=True, + ), + ), + ( + "user", + models.ForeignKey( + help_text="User of the local email", + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), - options={'permissions': (('view_emailaddress', 'Can see a local email account object'),), 'verbose_name': 'Local email account', 'verbose_name_plural': 'Local email accounts'}, + options={ + "permissions": ( + ("view_emailaddress", "Can see a local email account object"), + ), + "verbose_name": "Local email account", + "verbose_name_plural": "Local email accounts", + }, ), migrations.AddField( - model_name='user', - name='local_email_enabled', - field=models.BooleanField(default=False, help_text="Wether or not to enable the local email account."), + model_name="user", + name="local_email_enabled", + field=models.BooleanField( + default=False, + help_text="Wether or not to enable the local email account.", + ), ), migrations.AddField( - model_name='user', - name='local_email_redirect', - field=models.BooleanField(default=False, help_text='Whether or not to redirect the local email messages to the main email.'), + model_name="user", + name="local_email_redirect", + field=models.BooleanField( + default=False, + help_text="Whether or not to redirect the local email messages to the main email.", + ), ), - migrations.RunPython(create_initial_email_address, - delete_all_email_address), + migrations.RunPython(create_initial_email_address, delete_all_email_address), ] - diff --git a/users/migrations/0074_auto_20180810_2104.py b/users/migrations/0074_auto_20180810_2104.py index bc32a266..35fdc0cd 100644 --- a/users/migrations/0074_auto_20180810_2104.py +++ b/users/migrations/0074_auto_20180810_2104.py @@ -9,14 +9,22 @@ import users.models class Migration(migrations.Migration): - dependencies = [ - ('users', '0073_auto_20180629_1614'), - ] + dependencies = [("users", "0073_auto_20180629_1614")] operations = [ migrations.AddField( - model_name='adherent', - name='gpg_fingerprint', - field=models.CharField(blank=True, max_length=40, null=True, validators=[django.core.validators.RegexValidator('^[0-9A-F]{40}$', message='Une fingerprint GPG doit contenir 40 caractères hexadécimaux')]), - ), + model_name="adherent", + name="gpg_fingerprint", + field=models.CharField( + blank=True, + max_length=40, + null=True, + validators=[ + django.core.validators.RegexValidator( + "^[0-9A-F]{40}$", + message="Une fingerprint GPG doit contenir 40 caractères hexadécimaux", + ) + ], + ), + ) ] diff --git a/users/migrations/0074_auto_20180814_1059.py b/users/migrations/0074_auto_20180814_1059.py index e3e8527f..4bac3095 100644 --- a/users/migrations/0074_auto_20180814_1059.py +++ b/users/migrations/0074_auto_20180814_1059.py @@ -7,24 +7,32 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0073_auto_20180629_1614'), - ] + dependencies = [("users", "0073_auto_20180629_1614")] operations = [ migrations.AlterField( - model_name='user', - name='email', - field=models.EmailField(blank=True, null=True, help_text='External email address allowing us to contact you.', max_length=254), + model_name="user", + name="email", + field=models.EmailField( + blank=True, + null=True, + help_text="External email address allowing us to contact you.", + max_length=254, + ), ), migrations.AlterField( - model_name='user', - name='local_email_enabled', - field=models.BooleanField(default=False, help_text='Enable the local email account.'), + model_name="user", + name="local_email_enabled", + field=models.BooleanField( + default=False, help_text="Enable the local email account." + ), ), migrations.AlterField( - model_name='user', - name='local_email_redirect', - field=models.BooleanField(default=False, help_text='Enable redirection of the local email messages to the main email.'), + model_name="user", + name="local_email_redirect", + field=models.BooleanField( + default=False, + help_text="Enable redirection of the local email messages to the main email.", + ), ), ] diff --git a/users/migrations/0075_merge_20180815_2202.py b/users/migrations/0075_merge_20180815_2202.py index c24ebf4e..6d22d0c9 100644 --- a/users/migrations/0075_merge_20180815_2202.py +++ b/users/migrations/0075_merge_20180815_2202.py @@ -8,9 +8,8 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('users', '0074_auto_20180814_1059'), - ('users', '0074_auto_20180810_2104'), + ("users", "0074_auto_20180814_1059"), + ("users", "0074_auto_20180810_2104"), ] - operations = [ - ] + operations = [] diff --git a/users/migrations/0076_auto_20180818_1321.py b/users/migrations/0076_auto_20180818_1321.py index 95af1422..f310f715 100644 --- a/users/migrations/0076_auto_20180818_1321.py +++ b/users/migrations/0076_auto_20180818_1321.py @@ -11,99 +11,196 @@ import users.models class Migration(migrations.Migration): - dependencies = [ - ('users', '0075_merge_20180815_2202'), - ] + dependencies = [("users", "0075_merge_20180815_2202")] operations = [ migrations.AlterModelOptions( - name='adherent', - options={'verbose_name': 'member', 'verbose_name_plural': 'members'}, + name="adherent", + options={"verbose_name": "member", "verbose_name_plural": "members"}, ), migrations.AlterModelOptions( - name='ban', - options={'permissions': (('view_ban', 'Can view a ban object'),), 'verbose_name': 'ban', 'verbose_name_plural': 'bans'}, + name="ban", + options={ + "permissions": (("view_ban", "Can view a ban object"),), + "verbose_name": "ban", + "verbose_name_plural": "bans", + }, ), migrations.AlterModelOptions( - name='club', - options={'verbose_name': 'club', 'verbose_name_plural': 'clubs'}, + name="club", + options={"verbose_name": "club", "verbose_name_plural": "clubs"}, ), migrations.AlterModelOptions( - name='emailaddress', - options={'permissions': (('view_emailaddress', 'Can view a local email account object'),), 'verbose_name': 'local email account', 'verbose_name_plural': 'local email accounts'}, + name="emailaddress", + options={ + "permissions": ( + ("view_emailaddress", "Can view a local email account object"), + ), + "verbose_name": "local email account", + "verbose_name_plural": "local email accounts", + }, ), migrations.AlterModelOptions( - name='listright', - options={'permissions': (('view_listright', 'Can view a group of rights object'),), 'verbose_name': 'group of rights', 'verbose_name_plural': 'groups of rights'}, + name="listright", + options={ + "permissions": ( + ("view_listright", "Can view a group of rights object"), + ), + "verbose_name": "group of rights", + "verbose_name_plural": "groups of rights", + }, ), migrations.AlterModelOptions( - name='listshell', - options={'permissions': (('view_listshell', 'Can view a shell object'),), 'verbose_name': 'shell', 'verbose_name_plural': 'shells'}, + name="listshell", + options={ + "permissions": (("view_listshell", "Can view a shell object"),), + "verbose_name": "shell", + "verbose_name_plural": "shells", + }, ), migrations.AlterModelOptions( - name='school', - options={'permissions': (('view_school', 'Can view a school object'),), 'verbose_name': 'school', 'verbose_name_plural': 'schools'}, + name="school", + options={ + "permissions": (("view_school", "Can view a school object"),), + "verbose_name": "school", + "verbose_name_plural": "schools", + }, ), migrations.AlterModelOptions( - name='serviceuser', - options={'permissions': (('view_serviceuser', 'Can view a service user object'),), 'verbose_name': 'service user', 'verbose_name_plural': 'service users'}, + name="serviceuser", + options={ + "permissions": ( + ("view_serviceuser", "Can view a service user object"), + ), + "verbose_name": "service user", + "verbose_name_plural": "service users", + }, ), migrations.AlterModelOptions( - name='user', - options={'permissions': (('change_user_password', 'Can change the password of a user'), ('change_user_state', 'Can edit the state of a user'), ('change_user_force', 'Can force the move'), ('change_user_shell', 'Can edit the shell of a user'), ('change_user_groups', 'Can edit the groups of rights of a user (critical permission)'), ('change_all_users', 'Can edit all users, including those with rights.'), ('view_user', 'Can view a user object')), 'verbose_name': 'user (member or club)', 'verbose_name_plural': 'users (members or clubs)'}, + name="user", + options={ + "permissions": ( + ("change_user_password", "Can change the password of a user"), + ("change_user_state", "Can edit the state of a user"), + ("change_user_force", "Can force the move"), + ("change_user_shell", "Can edit the shell of a user"), + ( + "change_user_groups", + "Can edit the groups of rights of a user (critical permission)", + ), + ( + "change_all_users", + "Can edit all users, including those with rights.", + ), + ("view_user", "Can view a user object"), + ), + "verbose_name": "user (member or club)", + "verbose_name_plural": "users (members or clubs)", + }, ), migrations.AlterModelOptions( - name='whitelist', - options={'permissions': (('view_whitelist', 'Can view a whitelist object'),), 'verbose_name': 'whitelist (free of charge access)', 'verbose_name_plural': 'whitelists (free of charge access)'}, + name="whitelist", + options={ + "permissions": (("view_whitelist", "Can view a whitelist object"),), + "verbose_name": "whitelist (free of charge access)", + "verbose_name_plural": "whitelists (free of charge access)", + }, ), migrations.AlterField( - model_name='adherent', - name='gpg_fingerprint', - field=models.CharField(blank=True, max_length=40, null=True, validators=[django.core.validators.RegexValidator('^[0-9A-F]{40}$', message='A GPG fingerprint must contain 40 hexadecimal characters.')]), + model_name="adherent", + name="gpg_fingerprint", + field=models.CharField( + blank=True, + max_length=40, + null=True, + validators=[ + django.core.validators.RegexValidator( + "^[0-9A-F]{40}$", + message="A GPG fingerprint must contain 40 hexadecimal characters.", + ) + ], + ), ), migrations.AlterField( - model_name='ban', - name='state', - field=models.IntegerField(choices=[(0, 'HARD (no access)'), (1, 'SOFT (local access only)'), (2, 'RESTRICTED (speed limitation)')], default=0), + model_name="ban", + name="state", + field=models.IntegerField( + choices=[ + (0, "HARD (no access)"), + (1, "SOFT (local access only)"), + (2, "RESTRICTED (speed limitation)"), + ], + default=0, + ), ), migrations.AlterField( - model_name='emailaddress', - name='user', - field=models.ForeignKey(help_text='User of the local email account', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + model_name="emailaddress", + name="user", + field=models.ForeignKey( + help_text="User of the local email account", + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterField( - model_name='listright', - name='unix_name', - field=models.CharField(max_length=255, unique=True, validators=[django.core.validators.RegexValidator('^[a-z]+$', message='UNIX groups can only contain lower case letters.')]), + model_name="listright", + name="unix_name", + field=models.CharField( + max_length=255, + unique=True, + validators=[ + django.core.validators.RegexValidator( + "^[a-z]+$", + message="UNIX groups can only contain lower case letters.", + ) + ], + ), ), migrations.AlterField( - model_name='request', - name='type', - field=models.CharField(choices=[('PW', 'Password'), ('EM', 'Email address')], max_length=2), + model_name="request", + name="type", + field=models.CharField( + choices=[("PW", "Password"), ("EM", "Email address")], max_length=2 + ), ), migrations.AlterField( - model_name='serviceuser', - name='comment', - field=models.CharField(blank=True, help_text='Comment', max_length=255), + model_name="serviceuser", + name="comment", + field=models.CharField(blank=True, help_text="Comment", max_length=255), ), migrations.AlterField( - model_name='serviceuser', - name='pseudo', - field=models.CharField(help_text='Must only contain letters, numerals or dashes.', max_length=32, unique=True, validators=[users.models.linux_user_validator]), + model_name="serviceuser", + name="pseudo", + field=models.CharField( + help_text="Must only contain letters, numerals or dashes.", + max_length=32, + unique=True, + validators=[users.models.linux_user_validator], + ), ), migrations.AlterField( - model_name='user', - name='comment', - field=models.CharField(blank=True, help_text='Comment, school year', max_length=255), + model_name="user", + name="comment", + field=models.CharField( + blank=True, help_text="Comment, school year", max_length=255 + ), ), migrations.AlterField( - model_name='user', - name='local_email_redirect', - field=models.BooleanField(default=False, help_text='Enable redirection of the local email messages to the main email address.'), + model_name="user", + name="local_email_redirect", + field=models.BooleanField( + default=False, + help_text="Enable redirection of the local email messages to the main email address.", + ), ), migrations.AlterField( - model_name='user', - name='pseudo', - field=models.CharField(help_text='Must only contain letters, numerals or dashes.', max_length=32, unique=True, validators=[users.models.linux_user_validator]), + model_name="user", + name="pseudo", + field=models.CharField( + help_text="Must only contain letters, numerals or dashes.", + max_length=32, + unique=True, + validators=[users.models.linux_user_validator], + ), ), ] diff --git a/users/migrations/0077_auto_20180824_1750.py b/users/migrations/0077_auto_20180824_1750.py index b9510257..194bc247 100644 --- a/users/migrations/0077_auto_20180824_1750.py +++ b/users/migrations/0077_auto_20180824_1750.py @@ -7,14 +7,20 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0076_auto_20180818_1321'), - ] + dependencies = [("users", "0076_auto_20180818_1321")] operations = [ migrations.AlterField( - model_name='user', - name='state', - field=models.IntegerField(choices=[(0, 'STATE_ACTIVE'), (1, 'STATE_DISABLED'), (2, 'STATE_ARCHIVE'), (3, 'STATE_NOT_YET_ACTIVE')], default=3), - ), + model_name="user", + name="state", + field=models.IntegerField( + choices=[ + (0, "STATE_ACTIVE"), + (1, "STATE_DISABLED"), + (2, "STATE_ARCHIVE"), + (3, "STATE_NOT_YET_ACTIVE"), + ], + default=3, + ), + ) ] diff --git a/users/migrations/0078_auto_20181011_1405.py b/users/migrations/0078_auto_20181011_1405.py index 6046d2e4..cde58630 100644 --- a/users/migrations/0078_auto_20181011_1405.py +++ b/users/migrations/0078_auto_20181011_1405.py @@ -9,14 +9,14 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('users', '0077_auto_20180824_1750'), - ] + dependencies = [("users", "0077_auto_20180824_1750")] operations = [ migrations.AlterField( - model_name='request', - name='user', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - ), + model_name="request", + name="user", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL + ), + ) ] diff --git a/users/migrations/0079_auto_20181228_2039.py b/users/migrations/0079_auto_20181228_2039.py index 79ab56d9..c8335b6e 100644 --- a/users/migrations/0079_auto_20181228_2039.py +++ b/users/migrations/0079_auto_20181228_2039.py @@ -7,14 +7,12 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0078_auto_20181011_1405'), - ] + dependencies = [("users", "0078_auto_20181011_1405")] operations = [ migrations.AlterField( - model_name='adherent', - name='gpg_fingerprint', + model_name="adherent", + name="gpg_fingerprint", field=models.CharField(blank=True, max_length=49, null=True), - ), + ) ] diff --git a/users/migrations/0080_auto_20190108_1726.py b/users/migrations/0080_auto_20190108_1726.py index f3f872bc..f7831be8 100644 --- a/users/migrations/0080_auto_20190108_1726.py +++ b/users/migrations/0080_auto_20190108_1726.py @@ -7,14 +7,20 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('users', '0079_auto_20181228_2039'), - ] + dependencies = [("users", "0079_auto_20181228_2039")] operations = [ migrations.AlterField( - model_name='user', - name='state', - field=models.IntegerField(choices=[(0, 'Active'), (1, 'Disabled'), (2, 'Archived'), (3, 'Not yet active')], default=3), - ), + model_name="user", + name="state", + field=models.IntegerField( + choices=[ + (0, "Active"), + (1, "Disabled"), + (2, "Archived"), + (3, "Not yet active"), + ], + default=3, + ), + ) ] diff --git a/users/migrations/0081_auto_20190317_0302.py b/users/migrations/0081_auto_20190317_0302.py new file mode 100644 index 00000000..2e511bb8 --- /dev/null +++ b/users/migrations/0081_auto_20190317_0302.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2019-03-17 02:02 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [("users", "0080_auto_20190108_1726")] + + operations = [ + migrations.AlterField( + model_name="user", + name="state", + field=models.IntegerField( + choices=[ + (0, "Active"), + (1, "Disabled"), + (2, "Archived"), + (3, "Not yet active"), + (4, "Full Archived"), + ], + default=3, + ), + ) + ] diff --git a/users/migrations/0082_auto_20190908_1338.py b/users/migrations/0082_auto_20190908_1338.py new file mode 100644 index 00000000..ecaf8c77 --- /dev/null +++ b/users/migrations/0082_auto_20190908_1338.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.23 on 2019-09-08 11:38 +from __future__ import unicode_literals + +from django.db import migrations +import ldapdb.models.fields + + +class Migration(migrations.Migration): + + dependencies = [("users", "0081_auto_20190317_0302")] + + operations = [ + migrations.SeparateDatabaseAndState( + state_operations=[ + migrations.AlterField( + model_name="ldapserviceuser", + name="dn", + field=ldapdb.models.fields.CharField( + max_length=200, primary_key=True, serialize=False + ), + ), + migrations.AlterField( + model_name="ldapserviceusergroup", + name="dn", + field=ldapdb.models.fields.CharField( + max_length=200, primary_key=True, serialize=False + ), + ), + migrations.AlterField( + model_name="ldapuser", + name="dn", + field=ldapdb.models.fields.CharField( + max_length=200, primary_key=True, serialize=False + ), + ), + migrations.AlterField( + model_name="ldapusergroup", + name="dn", + field=ldapdb.models.fields.CharField( + max_length=200, primary_key=True, serialize=False + ), + ), + ] + ) + ] diff --git a/users/migrations/0083_user_shortcuts_enabled.py b/users/migrations/0083_user_shortcuts_enabled.py new file mode 100644 index 00000000..cb225f59 --- /dev/null +++ b/users/migrations/0083_user_shortcuts_enabled.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.23 on 2019-09-20 12:38 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [("users", "0082_auto_20190908_1338")] + + operations = [ + migrations.AddField( + model_name="user", + name="shortcuts_enabled", + field=models.BooleanField( + default=True, verbose_name="Enable shortcuts on Re2o website" + ), + ) + ] diff --git a/users/migrations/0084_auto_20191120_0159.py b/users/migrations/0084_auto_20191120_0159.py new file mode 100644 index 00000000..f4acc90d --- /dev/null +++ b/users/migrations/0084_auto_20191120_0159.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.23 on 2019-11-20 00:59 +from __future__ import unicode_literals + +from django.conf import settings +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0083_user_shortcuts_enabled'), + ] + + operations = [ + migrations.AlterModelOptions( + name='user', + options={'permissions': (('change_user_password', 'Can change the password of a user'), ('change_user_state', 'Can edit the state of a user'), ('change_user_force', 'Can force the move'), ('change_user_shell', 'Can edit the shell of a user'), ('change_user_groups', 'Can edit the groups of rights of a user (critical permission)'), ('change_all_users', 'Can edit all users, including those with rights'), ('view_user', 'Can view a user object')), 'verbose_name': 'user (member or club)', 'verbose_name_plural': 'users (members or clubs)'}, + ), + migrations.AlterField( + model_name='emailaddress', + name='local_part', + field=models.CharField(help_text='Local part of the email address.', max_length=128, unique=True), + ), + migrations.AlterField( + model_name='emailaddress', + name='user', + field=models.ForeignKey(help_text='User of the local email account.', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='listright', + name='details', + field=models.CharField(blank=True, help_text='Description.', max_length=255), + ), + migrations.AlterField( + model_name='listright', + name='unix_name', + field=models.CharField(max_length=255, unique=True, validators=[django.core.validators.RegexValidator('^[a-z]+$', message='UNIX group names can only contain lower case letters.')]), + ), + migrations.AlterField( + model_name='serviceuser', + name='comment', + field=models.CharField(blank=True, help_text='Comment.', max_length=255), + ), + migrations.AlterField( + model_name='user', + name='comment', + field=models.CharField(blank=True, help_text='Comment, school year.', max_length=255), + ), + migrations.AlterField( + model_name='user', + name='shortcuts_enabled', + field=models.BooleanField(default=True, verbose_name='enable shortcuts on Re2o website'), + ), + migrations.AlterField( + model_name='user', + name='state', + field=models.IntegerField(choices=[(0, 'Active'), (1, 'Disabled'), (2, 'Archived'), (3, 'Not yet active'), (4, 'Fully archived')], default=3), + ), + ] diff --git a/users/migrations/__init__.py b/users/migrations/__init__.py index 20abb0d2..b409e525 100644 --- a/users/migrations/__init__.py +++ b/users/migrations/__init__.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -19,4 +19,3 @@ # 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. - diff --git a/users/models.py b/users/models.py index c1d0789a..3d21548a 100755 --- a/users/models.py +++ b/users/models.py @@ -4,7 +4,7 @@ # en quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -57,7 +57,7 @@ from django.forms import ValidationError from django.db.models.signals import post_save, post_delete, m2m_changed from django.dispatch import receiver from django.utils.functional import cached_property -from django.template import Context, loader +from django.template import loader from django.core.mail import send_mail from django.core.urlresolvers import reverse from django.db import transaction @@ -66,7 +66,7 @@ from django.contrib.auth.models import ( AbstractBaseUser, BaseUserManager, PermissionsMixin, - Group + Group, ) from django.core.validators import RegexValidator import traceback @@ -78,7 +78,6 @@ import ldapdb.models import ldapdb.models.fields from re2o.settings import LDAP, GID_RANGES, UID_RANGES -from re2o.login import hashNT from re2o.field_permissions import FieldPermissionModelMixin from re2o.mixins import AclMixin, RevMixin from re2o.base import smtp_check @@ -103,19 +102,16 @@ def linux_user_validator(login): pas les contraintes unix (maj, min, chiffres ou tiret)""" if not linux_user_check(login): raise forms.ValidationError( - _("The username '%(label)s' contains forbidden characters."), - params={'label': login}, + _("The username \"%(label)s\" contains forbidden characters."), + params={"label": login}, ) def get_fresh_user_uid(): """ Renvoie le plus petit uid non pris. Fonction très paresseuse """ - uids = list(range( - int(min(UID_RANGES['users'])), - int(max(UID_RANGES['users'])) - )) + uids = list(range(int(min(UID_RANGES["users"])), int(max(UID_RANGES["users"])))) try: - used_uids = list(User.objects.values_list('uid_number', flat=True)) + used_uids = list(User.objects.values_list("uid_number", flat=True)) except: used_uids = [] free_uids = [id for id in uids if id not in used_uids] @@ -124,11 +120,8 @@ def get_fresh_user_uid(): def get_fresh_gid(): """ Renvoie le plus petit gid libre """ - gids = list(range( - int(min(GID_RANGES['posix'])), - int(max(GID_RANGES['posix'])) - )) - used_gids = list(ListRight.objects.values_list('gid', flat=True)) + gids = list(range(int(min(GID_RANGES["posix"])), int(max(GID_RANGES["posix"])))) + used_gids = list(ListRight.objects.values_list("gid", flat=True)) free_gids = [id for id in gids if id not in used_gids] return min(free_gids) @@ -136,14 +129,7 @@ def get_fresh_gid(): class UserManager(BaseUserManager): """User manager basique de django""" - def _create_user( - self, - pseudo, - surname, - email, - password=None, - su=False - ): + def _create_user(self, pseudo, surname, email, password=None, su=False): if not pseudo: raise ValueError(_("Users must have an username.")) @@ -178,8 +164,9 @@ class UserManager(BaseUserManager): return self._create_user(pseudo, surname, email, password, True) -class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, - PermissionsMixin, AclMixin): +class User( + RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin, AclMixin +): """ Definition de l'utilisateur de base. Champs principaux : name, surnname, pseudo, email, room, password Herite du django BaseUser et du système d'auth django""" @@ -188,11 +175,13 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, STATE_DISABLED = 1 STATE_ARCHIVE = 2 STATE_NOT_YET_ACTIVE = 3 + STATE_FULL_ARCHIVE = 4 STATES = ( (0, _("Active")), (1, _("Disabled")), (2, _("Archived")), (3, _("Not yet active")), + (4, _("Fully archived")), ) surname = models.CharField(max_length=255) @@ -200,72 +189,59 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, max_length=32, unique=True, help_text=_("Must only contain letters, numerals or dashes."), - validators=[linux_user_validator] + validators=[linux_user_validator], ) email = models.EmailField( blank=True, null=True, - help_text=_("External email address allowing us to contact you.") + help_text=_("External email address allowing us to contact you."), ) local_email_redirect = models.BooleanField( default=False, - help_text=_("Enable redirection of the local email messages to the" - " main email address.") + help_text=_( + "Enable redirection of the local email messages to the" + " main email address." + ), ) local_email_enabled = models.BooleanField( - default=False, - help_text=_("Enable the local email account.") + default=False, help_text=_("Enable the local email account.") ) school = models.ForeignKey( - 'School', - on_delete=models.PROTECT, - null=True, - blank=True + "School", on_delete=models.PROTECT, null=True, blank=True ) shell = models.ForeignKey( - 'ListShell', - on_delete=models.PROTECT, - null=True, - blank=True + "ListShell", on_delete=models.PROTECT, null=True, blank=True ) comment = models.CharField( - help_text=_("Comment, school year"), - max_length=255, - blank=True + help_text=_("Comment, school year."), max_length=255, blank=True ) pwd_ntlm = models.CharField(max_length=255) state = models.IntegerField(choices=STATES, default=STATE_NOT_YET_ACTIVE) registered = models.DateTimeField(auto_now_add=True) telephone = models.CharField(max_length=15, blank=True, null=True) - uid_number = models.PositiveIntegerField( - default=get_fresh_user_uid, - unique=True - ) - rezo_rez_uid = models.PositiveIntegerField( - unique=True, - blank=True, - null=True + uid_number = models.PositiveIntegerField(default=get_fresh_user_uid, unique=True) + rezo_rez_uid = models.PositiveIntegerField(unique=True, blank=True, null=True) + shortcuts_enabled = models.BooleanField( + verbose_name=_("enable shortcuts on Re2o website"), default=True ) - USERNAME_FIELD = 'pseudo' - REQUIRED_FIELDS = ['surname', 'email'] + USERNAME_FIELD = "pseudo" + REQUIRED_FIELDS = ["surname", "email"] objects = UserManager() class Meta: permissions = ( - ("change_user_password", - _("Can change the password of a user")), + ("change_user_password", _("Can change the password of a user")), ("change_user_state", _("Can edit the state of a user")), ("change_user_force", _("Can force the move")), ("change_user_shell", _("Can edit the shell of a user")), - ("change_user_groups", - _("Can edit the groups of rights of a user (critical" - " permission)")), - ("change_all_users", - _("Can edit all users, including those with rights.")), - ("view_user", - _("Can view a user object")), + ( + "change_user_groups", + _("Can edit the groups of rights of a user (critical permission)"), + ), + ("change_all_users", _("Can edit all users, including those with rights")), + ("view_user", _("Can view a user object")), ) verbose_name = _("user (member or club)") verbose_name_plural = _("users (members or clubs)") @@ -276,7 +252,7 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, if self.is_class_adherent: return self.adherent.name else: - return '' + return "" @cached_property def room(self): @@ -297,17 +273,31 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, @cached_property def get_mail(self): """Return the mail address choosen by the user""" - if not OptionalUser.get_cached_value('local_email_accounts_enabled') or not self.local_email_enabled or self.local_email_redirect: + if ( + not OptionalUser.get_cached_value("local_email_accounts_enabled") + or not self.local_email_enabled + or self.local_email_redirect + ): return str(self.email) else: return str(self.emailaddress_set.get(local_part=self.pseudo.lower())) @cached_property - def class_name(self): - """Renvoie si il s'agit d'un adhérent ou d'un club""" - if hasattr(self, 'adherent'): + def class_type(self): + """Returns the type of that user; returns database keyname""" + if hasattr(self, "adherent"): + return "Adherent" + elif hasattr(self, "club"): + return "Club" + else: + raise NotImplementedError(_("Unknown type.")) + + @cached_property + def class_display(self): + """Returns the typename of that user to display for user interface""" + if hasattr(self, "adherent"): return _("Member") - elif hasattr(self, 'club'): + elif hasattr(self, "club"): return _("Club") else: raise NotImplementedError(_("Unknown type.")) @@ -315,31 +305,47 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, @cached_property def gid_number(self): """renvoie le gid par défaut des users""" - return int(LDAP['user_gid']) + return int(LDAP["user_gid"]) @cached_property def is_class_club(self): """ Returns True if the object is a Club (subclassing User) """ # TODO : change to isinstance (cleaner) - return hasattr(self, 'club') + return hasattr(self, "club") @cached_property def is_class_adherent(self): """ Returns True if the object is a Adherent (subclassing User) """ # TODO : change to isinstance (cleaner) - return hasattr(self, 'adherent') + return hasattr(self, "adherent") @property def is_active(self): """ Renvoie si l'user est à l'état actif""" - return self.state == self.STATE_ACTIVE or self.state == self.STATE_NOT_YET_ACTIVE + allow_archived = OptionalUser.get_cached_value("allow_archived_connexion") + return ( + self.state == self.STATE_ACTIVE + or self.state == self.STATE_NOT_YET_ACTIVE + or ( + allow_archived + and self.state in (self.STATE_ARCHIVE, self.STATE_FULL_ARCHIVE) + ) + ) def set_active(self): - """Enable this user if he subscribed successfully one time before""" + """Enable this user if he subscribed successfully one time before + Reenable it if it was archived + Do nothing if disabed""" if self.state == self.STATE_NOT_YET_ACTIVE: - if self.facture_set.filter(valid=True).filter(Q(vente__type_cotisation='All') | Q(vente__type_cotisation='Adhesion')).exists() or OptionalUser.get_cached_value('all_users_active'): + if self.facture_set.filter(valid=True).filter( + Q(vente__type_cotisation="All") | Q(vente__type_cotisation="Adhesion") + ).exists() or OptionalUser.get_cached_value("all_users_active"): self.state = self.STATE_ACTIVE self.save() + if self.state == self.STATE_ARCHIVE or self.state == self.STATE_FULL_ARCHIVE: + self.state = self.STATE_ACTIVE + self.unarchive() + self.save() @property def is_staff(self): @@ -367,17 +373,17 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, @cached_property def gid(self): """return the default gid of user""" - return LDAP['user_gid'] + return LDAP["user_gid"] @property def get_shell(self): """ A utiliser de préférence, prend le shell par défaut si il n'est pas défini""" - return self.shell or OptionalUser.get_cached_value('shell_default') + return self.shell or OptionalUser.get_cached_value("shell_default") @cached_property def home_directory(self): - return '/home/' + self.pseudo + return "/home/" + self.pseudo @cached_property def get_shadow_expire(self): @@ -390,29 +396,29 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, def end_adhesion(self): """ Renvoie la date de fin d'adhésion d'un user. Examine les objets cotisation""" - date_max = Cotisation.objects.filter( - vente__in=Vente.objects.filter( - facture__in=Facture.objects.filter( - user=self - ).exclude(valid=False) + date_max = ( + Cotisation.objects.filter( + vente__in=Vente.objects.filter( + facture__in=Facture.objects.filter(user=self).exclude(valid=False) + ) ) - ).filter( - Q(type_cotisation='All') | Q(type_cotisation='Adhesion') - ).aggregate(models.Max('date_end'))['date_end__max'] + .filter(Q(type_cotisation="All") | Q(type_cotisation="Adhesion")) + .aggregate(models.Max("date_end"))["date_end__max"] + ) return date_max def end_connexion(self): """ Renvoie la date de fin de connexion d'un user. Examine les objets cotisation""" - date_max = Cotisation.objects.filter( - vente__in=Vente.objects.filter( - facture__in=Facture.objects.filter( - user=self - ).exclude(valid=False) + date_max = ( + Cotisation.objects.filter( + vente__in=Vente.objects.filter( + facture__in=Facture.objects.filter(user=self).exclude(valid=False) + ) ) - ).filter( - Q(type_cotisation='All') | Q(type_cotisation='Connexion') - ).aggregate(models.Max('date_end'))['date_end__max'] + .filter(Q(type_cotisation="All") | Q(type_cotisation="Connexion")) + .aggregate(models.Max("date_end"))["date_end__max"] + ) return date_max def is_adherent(self): @@ -439,16 +445,16 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, def end_ban(self): """ Renvoie la date de fin de ban d'un user, False sinon """ - date_max = Ban.objects.filter( - user=self - ).aggregate(models.Max('date_end'))['date_end__max'] + date_max = Ban.objects.filter(user=self).aggregate(models.Max("date_end"))[ + "date_end__max" + ] return date_max def end_whitelist(self): """ Renvoie la date de fin de whitelist d'un user, False sinon """ - date_max = Whitelist.objects.filter( - user=self - ).aggregate(models.Max('date_end'))['date_end__max'] + date_max = Whitelist.objects.filter(user=self).aggregate( + models.Max("date_end") + )["date_end__max"] return date_max def is_ban(self): @@ -473,10 +479,11 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, def has_access(self): """ Renvoie si un utilisateur a accès à internet """ - return (self.state == User.STATE_ACTIVE and - not self.is_ban() and - (self.is_connected() or self.is_whitelisted())) \ - or self == AssoOption.get_cached_value('utilisateur_asso') + return ( + self.state == User.STATE_ACTIVE + and not self.is_ban() + and (self.is_connected() or self.is_whitelisted()) + ) or self == AssoOption.get_cached_value("utilisateur_asso") def end_access(self): """ Renvoie la date de fin normale d'accès (adhésion ou whiteliste)""" @@ -496,79 +503,157 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, """ Renvoie le solde d'un user. Somme les crédits de solde et retire les débit payés par solde""" solde_objects = Paiement.objects.filter(is_balance=True) - somme_debit = Vente.objects.filter( - facture__in=Facture.objects.filter( - user=self, - paiement__in=solde_objects, - valid=True - ) - ).aggregate( - total=models.Sum( - models.F('prix')*models.F('number'), - output_field=models.DecimalField() - ) - )['total'] or 0 - somme_credit = Vente.objects.filter( - facture__in=Facture.objects.filter(user=self, valid=True), - name='solde' - ).aggregate( - total=models.Sum( - models.F('prix')*models.F('number'), - output_field=models.DecimalField() - ) - )['total'] or 0 + somme_debit = ( + Vente.objects.filter( + facture__in=Facture.objects.filter( + user=self, paiement__in=solde_objects, valid=True + ) + ).aggregate( + total=models.Sum( + models.F("prix") * models.F("number"), + output_field=models.DecimalField(), + ) + )[ + "total" + ] + or 0 + ) + somme_credit = ( + Vente.objects.filter( + facture__in=Facture.objects.filter(user=self, valid=True), name="solde" + ).aggregate( + total=models.Sum( + models.F("prix") * models.F("number"), + output_field=models.DecimalField(), + ) + )[ + "total" + ] + or 0 + ) return somme_credit - somme_debit - def user_interfaces(self, active=True): + @classmethod + def users_interfaces(cls, users, active=True, all_interfaces=False): """ Renvoie toutes les interfaces dont les machines appartiennent à self. Par defaut ne prend que les interfaces actives""" - return Interface.objects.filter( - machine__in=Machine.objects.filter(user=self, active=active) - ).select_related('domain__extension') + if all_interfaces: + return Interface.objects.filter( + machine__in=Machine.objects.filter(user__in=users) + ).select_related("domain__extension") + else: + return Interface.objects.filter( + machine__in=Machine.objects.filter(user__in=users, active=active) + ).select_related("domain__extension") + + def user_interfaces(self, active=True, all_interfaces=False): + """ Renvoie toutes les interfaces dont les machines appartiennent à + self. Par defaut ne prend que les interfaces actives""" + return self.users_interfaces( + [self], active=active, all_interfaces=all_interfaces + ) def assign_ips(self): """ Assign une ipv4 aux machines d'un user """ interfaces = self.user_interfaces() - for interface in interfaces: - if not interface.ipv4: - with transaction.atomic(), reversion.create_revision(): - interface.assign_ipv4() - reversion.set_comment(_("IPv4 assigning")) - interface.save() + with transaction.atomic(), reversion.create_revision(): + Interface.mass_assign_ipv4(interfaces) + reversion.set_comment("IPv4 assignment") def unassign_ips(self): """ Désassigne les ipv4 aux machines de l'user""" interfaces = self.user_interfaces() - for interface in interfaces: - with transaction.atomic(), reversion.create_revision(): - interface.unassign_ipv4() - reversion.set_comment(_("IPv4 unassigning")) - interface.save() + with transaction.atomic(), reversion.create_revision(): + Interface.mass_unassign_ipv4(interfaces) + reversion.set_comment("IPv4 unassignment") + + @classmethod + def mass_unassign_ips(cls, users_list): + interfaces = cls.users_interfaces(users_list) + with transaction.atomic(), reversion.create_revision(): + Interface.mass_unassign_ipv4(interfaces) + reversion.set_comment("IPv4 assignment") + + @classmethod + def mass_disable_email(cls, queryset_users): + """Disable email account and redirection""" + queryset_users.update(local_email_enabled=False) + queryset_users.update(local_email_redirect=False) + + @classmethod + def mass_delete_data(cls, queryset_users): + """This users will be completely archived, so only keep mandatory data""" + cls.mass_disable_email(queryset_users) + Machine.mass_delete(Machine.objects.filter(user__in=queryset_users)) + cls.ldap_delete_users(queryset_users) def disable_email(self): """Disable email account and redirection""" - self.email = "" self.local_email_enabled = False self.local_email_redirect = False + def delete_data(self): + """This user will be completely archived, so only keep mandatory data""" + self.disable_email() + self.machine_set.all().delete() + + @classmethod + def mass_archive(cls, users_list): + """Mass Archive several users, take a queryset + Copy Queryset to avoid eval problem with queryset update""" + # Force eval of queryset + bool(users_list) + users_list = users_list.all() + cls.mass_unassign_ips(users_list) + users_list.update(state=User.STATE_ARCHIVE) + + @classmethod + def mass_full_archive(cls, users_list): + """Mass Archive several users, take a queryset + Copy Queryset to avoid eval problem with queryset update""" + # Force eval of queryset + bool(users_list) + users_list = users_list.all() + cls.mass_unassign_ips(users_list) + cls.mass_delete_data(users_list) + users_list.update(state=User.STATE_FULL_ARCHIVE) + def archive(self): """ Filling the user; no more active""" self.unassign_ips() - self.disable_email() + + def full_archive(self): + """Full Archive = Archive + Service access complete deletion""" + self.archive() + self.delete_data() + self.ldap_del() def unarchive(self): """Unfilling the user""" self.assign_ips() + self.ldap_sync() def state_sync(self): """Archive, or unarchive, if the user was not active/or archived before""" - if self.__original_state != self.STATE_ACTIVE and self.state == self.STATE_ACTIVE: + if ( + self.__original_state != self.STATE_ACTIVE + and self.state == self.STATE_ACTIVE + ): self.unarchive() - elif self.__original_state != self.STATE_ARCHIVE and self.state == self.STATE_ARCHIVE: + elif ( + self.__original_state != self.STATE_ARCHIVE + and self.state == self.STATE_ARCHIVE + ): self.archive() + elif ( + self.__original_state != self.STATE_FULL_ARCHIVE + and self.state == self.STATE_FULL_ARCHIVE + ): + self.full_archive() - def ldap_sync(self, base=True, access_refresh=True, mac_refresh=True, - group_refresh=False): + def ldap_sync( + self, base=True, access_refresh=True, mac_refresh=True, group_refresh=False + ): """ Synchronisation du ldap. Synchronise dans le ldap les attributs de self Options : base : synchronise tous les attributs de base - nom, prenom, @@ -578,15 +663,15 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, mac_refresh : synchronise les machines de l'user group_refresh : synchronise les group de l'user Si l'instance n'existe pas, on crée le ldapuser correspondant""" - if sys.version_info[0] >= 3 and self.state != self.STATE_ARCHIVE and\ - self.state != self.STATE_DISABLED: + if sys.version_info[0] >= 3 and ( + self.state == self.STATE_ACTIVE + or self.state == self.STATE_ARCHIVE + or self.state == self.STATE_DISABLED + ): self.refresh_from_db() try: user_ldap = LdapUser.objects.get(uidNumber=self.uid_number) except LdapUser.DoesNotExist: - # Freshly created users are NOT synced in ldap base - if self.state == self.STATE_NOT_YET_ACTIVE: - return user_ldap = LdapUser(uidNumber=self.uid_number) base = True access_refresh = True @@ -597,20 +682,19 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, user_ldap.dialupAccess = str(self.has_access()) user_ldap.home_directory = self.home_directory user_ldap.mail = self.get_mail - user_ldap.given_name = self.surname.lower() + '_'\ - + self.name.lower()[:3] - user_ldap.gid = LDAP['user_gid'] - if '{SSHA}' in self.password or '{SMD5}' in self.password: + user_ldap.given_name = ( + self.surname.lower() + "_" + self.name.lower()[:3] + ) + user_ldap.gid = LDAP["user_gid"] + if "{SSHA}" in self.password or "{SMD5}" in self.password: # We remove the extra $ added at import from ldap - user_ldap.user_password = self.password[:6] + \ - self.password[7:] - elif '{crypt}' in self.password: + user_ldap.user_password = self.password[:6] + self.password[7:] + elif "{crypt}" in self.password: # depending on the length, we need to remove or not a $ if len(self.password) == 41: user_ldap.user_password = self.password else: - user_ldap.user_password = self.password[:7] + \ - self.password[8:] + user_ldap.user_password = self.password[:7] + self.password[8:] user_ldap.sambat_nt_password = self.pwd_ntlm.upper() if self.get_shell: @@ -619,15 +703,18 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, if access_refresh: user_ldap.dialupAccess = str(self.has_access()) if mac_refresh: - user_ldap.macs = [str(mac) for mac in Interface.objects.filter( - machine__user=self - ).values_list('mac_address', flat=True).distinct()] + user_ldap.macs = [ + str(mac) + for mac in Interface.objects.filter(machine__user=self) + .values_list("mac_address", flat=True) + .distinct() + ] if group_refresh: # Need to refresh all groups because we don't know which groups # were updated during edition of groups and the user may no longer # be part of the updated group (case of group removal) for group in Group.objects.all(): - if hasattr(group, 'listright'): + if hasattr(group, "listright"): group.listright.ldap_sync() user_ldap.save() @@ -639,27 +726,32 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, except LdapUser.DoesNotExist: pass + @classmethod + def ldap_delete_users(cls, queryset_users): + """Delete multiple users in ldap""" + LdapUser.objects.filter( + name__in=list(queryset_users.values_list("pseudo", flat=True)) + ) + def notif_inscription(self): """ Prend en argument un objet user, envoie un mail de bienvenue """ - template = loader.get_template('users/email_welcome') - mailmessageoptions, _created = MailMessageOption\ - .objects.get_or_create() - context = Context({ - 'nom': self.get_full_name(), - 'asso_name': AssoOption.get_cached_value('name'), - 'asso_email': AssoOption.get_cached_value('contact'), - 'welcome_mail_fr': mailmessageoptions.welcome_mail_fr, - 'welcome_mail_en': mailmessageoptions.welcome_mail_en, - 'pseudo': self.pseudo, - }) + template = loader.get_template("users/email_welcome") + mailmessageoptions, _created = MailMessageOption.objects.get_or_create() + context = { + "nom": self.get_full_name(), + "asso_name": AssoOption.get_cached_value("name"), + "asso_email": AssoOption.get_cached_value("contact"), + "welcome_mail_fr": mailmessageoptions.welcome_mail_fr, + "welcome_mail_en": mailmessageoptions.welcome_mail_en, + "pseudo": self.pseudo, + } send_mail( - 'Bienvenue au %(name)s / Welcome to %(name)s' % { - 'name': AssoOption.get_cached_value('name') - }, - '', - GeneralOption.get_cached_value('email_from'), + "Bienvenue au %(name)s / Welcome to %(name)s" + % {"name": AssoOption.get_cached_value("name")}, + "", + GeneralOption.get_cached_value("email_from"), [self.email], - html_message=template.render(context) + html_message=template.render(context), ) return @@ -670,33 +762,31 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, req.type = Request.PASSWD req.user = self req.save() - template = loader.get_template('users/email_passwd_request') + template = loader.get_template("users/email_passwd_request") context = { - 'name': req.user.get_full_name(), - 'asso': AssoOption.get_cached_value('name'), - 'asso_mail': AssoOption.get_cached_value('contact'), - 'site_name': GeneralOption.get_cached_value('site_name'), - 'url': request.build_absolute_uri( - reverse('users:process', kwargs={'token': req.token}) + "name": req.user.get_full_name(), + "asso": AssoOption.get_cached_value("name"), + "asso_mail": AssoOption.get_cached_value("contact"), + "site_name": GeneralOption.get_cached_value("site_name"), + "url": request.build_absolute_uri( + reverse("users:process", kwargs={"token": req.token}) ), - 'expire_in': str( - GeneralOption.get_cached_value('req_expire_hrs') - ) + ' hours', + "expire_in": str(GeneralOption.get_cached_value("req_expire_hrs")), } send_mail( - 'Changement de mot de passe du %(name)s / Password renewal for ' - '%(name)s' % {'name': AssoOption.get_cached_value('name')}, + "Changement de mot de passe de %(name)s / Password change for " + "%(name)s" % {"name": AssoOption.get_cached_value("name")}, template.render(context), - GeneralOption.get_cached_value('email_from'), + GeneralOption.get_cached_value("email_from"), [req.user.email], - fail_silently=False + fail_silently=False, ) return def autoregister_machine(self, mac_address, nas_type): """ Fonction appellée par freeradius. Enregistre la mac pour une machine inconnue sur le compte de l'user""" - allowed, _message = Machine.can_create(self, self.id) + allowed, _message, _rights = Machine.can_create(self, self.id) if not allowed: return False, _("Maximum number of registered machines reached.") if not nas_type: @@ -707,7 +797,7 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, machine_parent.user = self interface_cible = Interface() interface_cible.mac_address = mac_address - interface_cible.type = machine_type_cible + interface_cible.machine_type = machine_type_cible interface_cible.clean() machine_parent.clean() domain = Domain() @@ -722,41 +812,45 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, domain.save() self.notif_auto_newmachine(interface_cible) except Exception as error: - return False, traceback.format_exc() + return False, traceback.format_exc() return interface_cible, _("OK") def notif_auto_newmachine(self, interface): """Notification mail lorsque une machine est automatiquement ajoutée par le radius""" - template = loader.get_template('users/email_auto_newmachine') - context = Context({ - 'nom': self.get_full_name(), - 'mac_address': interface.mac_address, - 'asso_name': AssoOption.get_cached_value('name'), - 'interface_name': interface.domain, - 'asso_email': AssoOption.get_cached_value('contact'), - 'pseudo': self.pseudo, - }) + template = loader.get_template("users/email_auto_newmachine") + context = { + "nom": self.get_full_name(), + "mac_address": interface.mac_address, + "asso_name": AssoOption.get_cached_value("name"), + "interface_name": interface.domain, + "asso_email": AssoOption.get_cached_value("contact"), + "pseudo": self.pseudo, + } send_mail( "Ajout automatique d'une machine / New machine autoregistered", - '', - GeneralOption.get_cached_value('email_from'), + "", + GeneralOption.get_cached_value("email_from"), [self.email], - html_message=template.render(context) + html_message=template.render(context), ) return def set_password(self, password): """ A utiliser de préférence, set le password en hash courrant et dans la version ntlm""" + from re2o.login import hashNT + super().set_password(password) self.pwd_ntlm = hashNT(password) return @cached_property def email_address(self): - if (OptionalUser.get_cached_value('local_email_accounts_enabled') - and self.local_email_enabled): + if ( + OptionalUser.get_cached_value("local_email_accounts_enabled") + and self.local_email_enabled + ): return self.emailaddress_set.all() return EMailAddress.objects.none() @@ -770,7 +864,7 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, def simple_pseudo(): """Renvoie le pseudo sans underscore (compat dns)""" - return self.pseudo.replace('_', '-').lower() + return self.pseudo.replace("_", "-").lower() def composed_pseudo(name): """Renvoie le resultat de simplepseudo et rajoute le nom""" @@ -790,33 +884,55 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, user_request one of its member, or if user_request is self, or if user_request has the 'cableur' right. """ + if self.state in (self.STATE_ARCHIVE, self.STATE_FULL_ARCHIVE): + warning_message = _("This user is archived.") + else: + warning_message = None + if self.is_class_club and user_request.is_class_adherent: - if (self == user_request or - user_request.has_perm('users.change_user') or - user_request.adherent in self.club.administrators.all()): - return True, None + if ( + self == user_request + or user_request.has_perm("users.change_user") + or user_request.adherent in self.club.administrators.all() + ): + return True, warning_message, None else: - return False, _("You don't have the right to edit this club.") + return ( + False, + _("You don't have the right to edit this club."), + ("users.change_user",), + ) else: if self == user_request: - return True, None - elif user_request.has_perm('users.change_all_users'): - return True, None - elif user_request.has_perm('users.change_user'): + return True, warning_message, None + elif user_request.has_perm("users.change_all_users"): + return True, warning_message, None + elif user_request.has_perm("users.change_user"): if self.groups.filter(listright__critical=True): - return False, (_("User with critical rights, can't be" - " edited.")) - elif self == AssoOption.get_cached_value('utilisateur_asso'): - return False, (_("Impossible to edit the organisation's" - " user without the 'change_all_users'" - " right.")) + return ( + False, + _("User with critical rights, can't be edited."), + ("users.change_all_users",), + ) + elif self == AssoOption.get_cached_value("utilisateur_asso"): + return ( + False, + _( + "Impossible to edit the organisation's" + " user without the \"change_all_users\" right." + ), + ("users.change_all_users",), + ) else: - return True, None - elif user_request.has_perm('users.change_all_users'): - return True, None + return True, warning_message, None + elif user_request.has_perm("users.change_all_users"): + return True, warning_message, None else: - return False, (_("You don't have the right to edit another" - " user.")) + return ( + False, + _("You don't have the right to edit another user."), + ("users.change_user", "users.change_all_users"), + ) def can_change_password(self, user_request, *_args, **_kwargs): """Check if a user can change a user's password @@ -828,30 +944,39 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, or if user_request has the right to change other's password """ if self.is_class_club and user_request.is_class_adherent: - if (self == user_request or - user_request.has_perm('users.change_user_password') or - user_request.adherent in self.club.administrators.all()): - return True, None + if ( + self == user_request + or user_request.has_perm("users.change_user_password") + or user_request.adherent in self.club.administrators.all() + ): + return True, None, None else: - return False, _("You don't have the right to edit this club.") + return ( + False, + _("You don't have the right to edit this club."), + ("users.change_user_password",), + ) else: - if (self == user_request or - user_request.has_perm('users.change_user_groups')): + if self == user_request or user_request.has_perm( + "users.change_user_groups" + ): # Peut éditer les groupes d'un user, # c'est un privilège élevé, True - return True, None - elif (user_request.has_perm('users.change_user') and - not self.groups.all()): - return True, None + return True, None, None + elif user_request.has_perm("users.change_user") and not self.groups.all(): + return True, None, None else: - return False, (_("You don't have the right to edit another" - " user.")) + return ( + False, + _("You don't have the right to edit another user."), + ("users.change_user_groups", "users.change_user"), + ) def check_selfpasswd(self, user_request, *_args, **_kwargs): - """ Returns (True, None) if user_request is self, else returns - (False, None) + """ Returns (True, None, None) if user_request is self, else returns + (False, None, None) """ - return user_request == self, None + return user_request == self, None, None def can_change_room(self, user_request, *_args, **_kwargs): """ Check if a user can change a room @@ -860,11 +985,20 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, :returns: a message and a boolean which is True if the user has the right to change a state """ - if not ((self.pk == user_request.pk and OptionalUser.get_cached_value('self_change_room')) - or user_request.has_perm('users.change_user')): - return False, _("Permission required to change the room.") + if not ( + ( + self.pk == user_request.pk + and OptionalUser.get_cached_value("self_change_room") + ) + or user_request.has_perm("users.change_user") + ): + return ( + False, + _("You don't have the right to change the room."), + ("users.change_user",), + ) else: - return True, None + return True, None, None @staticmethod def can_change_state(user_request, *_args, **_kwargs): @@ -874,9 +1008,11 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, :returns: a message and a boolean which is True if the user has the right to change a state """ + can = user_request.has_perm("users.change_user_state") return ( - user_request.has_perm('users.change_user_state'), - _("Permission required to change the state.") + can, + _("You don't have the right to change the state.") if not can else None, + ("users.change_user_state",), ) def can_change_shell(self, user_request, *_args, **_kwargs): @@ -886,11 +1022,20 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, :returns: a message and a boolean which is True if the user has the right to change a shell """ - if not ((self.pk == user_request.pk and OptionalUser.get_cached_value('self_change_shell')) - or user_request.has_perm('users.change_user_shell')): - return False, _("Permission required to change the shell.") + if not ( + ( + self.pk == user_request.pk + and OptionalUser.get_cached_value("self_change_shell") + ) + or user_request.has_perm("users.change_user_shell") + ): + return ( + False, + _("You don't have the right to change the shell."), + ("users.change_user_shell",), + ) else: - return True, None + return True, None, None @staticmethod def can_change_local_email_redirect(user_request, *_args, **_kwargs): @@ -900,9 +1045,11 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, :returns: a message and a boolean which is True if the user has the right to change a redirection """ + can = OptionalUser.get_cached_value("local_email_accounts_enabled") return ( - OptionalUser.get_cached_value('local_email_accounts_enabled'), - _("Local email accounts must be enabled.") + can, + _("Local email accounts must be enabled.") if not can else None, + None, ) @staticmethod @@ -913,9 +1060,11 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, :returns: a message and a boolean which is True if the user has the right to change internal address """ + can = OptionalUser.get_cached_value("local_email_accounts_enabled") return ( - OptionalUser.get_cached_value('local_email_accounts_enabled'), - _("Local email accounts must be enabled.") + can, + _("Local email accounts must be enabled.") if not can else None, + None, ) @staticmethod @@ -926,9 +1075,11 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, :returns: a message and a boolean which is True if the user has the right to change a force """ + can = user_request.has_perm("users.change_user_force") return ( - user_request.has_perm('users.change_user_force'), - _("Permission required to force the move.") + can, + _("You don't have the right to force the move.") if not can else None, + ("users.change_user_force",), ) @staticmethod @@ -939,9 +1090,13 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, :returns: a message and a boolean which is True if the user has the right to change a group """ + can = user_request.has_perm("users.change_user_grou") return ( - user_request.has_perm('users.change_user_groups'), - _("Permission required to edit the user's groups of rights.") + can, + _("You don't have the right to edit the user's groups of rights.") + if not can + else None, + ("users.change_user_groups"), ) @staticmethod @@ -951,9 +1106,13 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, :param user_request: The user who request :returns: a message and a boolean which is True if permission is granted. """ + can = user_request.is_superuser return ( - user_request.is_superuser, - _("'superuser' right required to edit the superuser flag.") + can, + _("\"superuser\" right required to edit the superuser flag.") + if not can + else None, + [], ) def can_view(self, user_request, *_args, **_kwargs): @@ -965,20 +1124,28 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, text """ if self.is_class_club and user_request.is_class_adherent: - if (self == user_request or - user_request.has_perm('users.view_user') or - user_request.adherent in self.club.administrators.all() or - user_request.adherent in self.club.members.all()): - return True, None + if ( + self == user_request + or user_request.has_perm("users.view_user") + or user_request.adherent in self.club.administrators.all() + or user_request.adherent in self.club.members.all() + ): + return True, None, None else: - return False, _("You don't have the right to view this club.") + return ( + False, + _("You don't have the right to view this club."), + ("users.view_user",), + ) else: - if (self == user_request or - user_request.has_perm('users.view_user')): - return True, None + if self == user_request or user_request.has_perm("users.view_user"): + return True, None, None else: - return False, (_("You don't have the right to view another" - " user.")) + return ( + False, + _("You don't have the right to view another user."), + ("users.view_user",), + ) @staticmethod def can_view_all(user_request, *_args, **_kwargs): @@ -988,9 +1155,13 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, :return: True if the user can view the list and an explanation message. """ + can = user_request.has_perm("users.view_user") return ( - user_request.has_perm('users.view_user'), + can, _("You don't have the right to view the list of users.") + if not can + else None, + ("users.view_user",), ) def can_delete(self, user_request, *_args, **_kwargs): @@ -1001,37 +1172,49 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, :return: True if user_request has the right 'bureau', and a message. """ + can = user_request.has_perm("users.delete_user") return ( - user_request.has_perm('users.delete_user'), - _("You don't have the right to delete this user.") + can, + _("You don't have the right to delete this user.") if not can else None, + ("users.delete_user",), ) def __init__(self, *args, **kwargs): super(User, self).__init__(*args, **kwargs) self.field_permissions = { - 'shell': self.can_change_shell, - 'force': self.can_change_force, - 'selfpasswd': self.check_selfpasswd, - 'local_email_redirect': self.can_change_local_email_redirect, - 'local_email_enabled': self.can_change_local_email_enabled, - 'room': self.can_change_room, + "shell": self.can_change_shell, + "force": self.can_change_force, + "selfpasswd": self.check_selfpasswd, + "local_email_redirect": self.can_change_local_email_redirect, + "local_email_enabled": self.can_change_local_email_enabled, + "room": self.can_change_room, } self.__original_state = self.state def clean(self, *args, **kwargs): """Check if this pseudo is already used by any mailalias. Better than raising an error in post-save and catching it""" - if (EMailAddress.objects - .filter(local_part=self.pseudo.lower()).exclude(user_id=self.id) - ): + if EMailAddress.objects.filter(local_part=self.pseudo.lower()).exclude( + user_id=self.id + ): raise ValidationError(_("This username is already used.")) - if not self.local_email_enabled and not self.email and not (self.state == self.STATE_ARCHIVE): - raise ValidationError(_("There is neither a local email address nor an external" - " email address for this user.") + if ( + not self.local_email_enabled + and not self.email + and not (self.state == self.STATE_FULL_ARCHIVE) + ): + raise ValidationError( + _( + "There is neither a local email address nor an external" + " email address for this user." + ) ) if self.local_email_redirect and not self.email: - raise ValidationError(_("You can't redirect your local emails if no external email" - " address has been set.") + raise ValidationError( + _( + "You can't redirect your local emails if no external email" + " address has been set." + ) ) def __str__(self): @@ -1044,16 +1227,9 @@ class Adherent(User): name = models.CharField(max_length=255) room = models.OneToOneField( - 'topologie.Room', - on_delete=models.PROTECT, - blank=True, - null=True - ) - gpg_fingerprint = models.CharField( - max_length=49, - blank=True, - null=True, + "topologie.Room", on_delete=models.PROTECT, blank=True, null=True ) + gpg_fingerprint = models.CharField(max_length=49, blank=True, null=True) class Meta(User.Meta): verbose_name = _("member") @@ -1061,14 +1237,21 @@ class Adherent(User): def format_gpgfp(self): """Format gpg finger print as AAAA BBBB... from a string AAAABBBB....""" - self.gpg_fingerprint = ' '.join([self.gpg_fingerprint[i:i + 4] for i in range(0, len(self.gpg_fingerprint), 4)]) + self.gpg_fingerprint = " ".join( + [ + self.gpg_fingerprint[i : i + 4] + for i in range(0, len(self.gpg_fingerprint), 4) + ] + ) def validate_gpgfp(self): """Validate from raw entry if is it a valid gpg fp""" if self.gpg_fingerprint: - gpg_fingerprint = self.gpg_fingerprint.replace(' ', '').upper() + gpg_fingerprint = self.gpg_fingerprint.replace(" ", "").upper() if not re.match("^[0-9A-F]{40}$", gpg_fingerprint): - raise ValidationError(_("A GPG fingerprint must contain 40 hexadecimal characters")) + raise ValidationError( + _("A GPG fingerprint must contain 40 hexadecimal characters.") + ) self.gpg_fingerprint = gpg_fingerprint @classmethod @@ -1088,17 +1271,23 @@ class Adherent(User): :return: a message and a boolean which is True if the user can create a user or if the `options.all_can_create` is set. """ - if (not user_request.is_authenticated and - not OptionalUser.get_cached_value('self_adhesion')): - return False, None + if not user_request.is_authenticated and not OptionalUser.get_cached_value( + "self_adhesion" + ): + return False, _("Self registration is disabled."), None else: - if (OptionalUser.get_cached_value('all_can_create_adherent') or - OptionalUser.get_cached_value('self_adhesion')): - return True, None + if OptionalUser.get_cached_value( + "all_can_create_adherent" + ) or OptionalUser.get_cached_value("self_adhesion"): + return True, None, None else: + can = user_request.has_perm("users.add_user") return ( - user_request.has_perm('users.add_user'), + can, _("You don't have the right to create a user.") + if not can + else None, + ("users.add_user",), ) def clean(self, *args, **kwargs): @@ -1114,24 +1303,15 @@ class Club(User): with special informations) """ room = models.ForeignKey( - 'topologie.Room', - on_delete=models.PROTECT, - blank=True, - null=True + "topologie.Room", on_delete=models.PROTECT, blank=True, null=True ) administrators = models.ManyToManyField( - blank=True, - to='users.Adherent', - related_name='club_administrator' + blank=True, to="users.Adherent", related_name="club_administrator" ) members = models.ManyToManyField( - blank=True, - to='users.Adherent', - related_name='club_members' - ) - mailing = models.BooleanField( - default=False + blank=True, to="users.Adherent", related_name="club_members" ) + mailing = models.BooleanField(default=False) class Meta(User.Meta): verbose_name = _("club") @@ -1146,14 +1326,18 @@ class Club(User): an user or if the `options.all_can_create` is set. """ if not user_request.is_authenticated: - return False, None + return False, _("You must be authenticated."), None else: - if OptionalUser.get_cached_value('all_can_create_club'): - return True, None + if OptionalUser.get_cached_value("all_can_create_club"): + return True, None, None else: + can = user_request.has_perm("users.add_user") return ( - user_request.has_perm('users.add_user'), + can, _("You don't have the right to create a club.") + if not can + else None, + ("users.add_user",), ) @staticmethod @@ -1164,14 +1348,22 @@ class Club(User): :return: True if the user can view the list and an explanation message. """ - if user_request.has_perm('users.view_user'): - return True, None - if (hasattr(user_request, 'is_class_adherent') and - user_request.is_class_adherent): - if (user_request.adherent.club_administrator.all() or - user_request.adherent.club_members.all()): - return True, None - return False, _("You don't have the right to view the list of users.") + if user_request.has_perm("users.view_user"): + return True, None, None + if ( + hasattr(user_request, "is_class_adherent") + and user_request.is_class_adherent + ): + if ( + user_request.adherent.club_administrator.all() + or user_request.adherent.club_members.all() + ): + return True, None, None + return ( + False, + _("You don't have the right to view the list of users."), + ("users.view_user",), + ) @classmethod def get_instance(cls, clubid, *_args, **_kwargs): @@ -1190,31 +1382,26 @@ def user_post_save(**kwargs): """ Synchronisation post_save : envoie le mail de bienvenue si creation Synchronise le pseudo, en créant un alias mail correspondant Synchronise le ldap""" - is_created = kwargs['created'] - user = kwargs['instance'] - EMailAddress.objects.get_or_create( - local_part=user.pseudo.lower(), user=user) + is_created = kwargs["created"] + user = kwargs["instance"] + EMailAddress.objects.get_or_create(local_part=user.pseudo.lower(), user=user) if is_created: user.notif_inscription() user.state_sync() user.ldap_sync( - base=True, - access_refresh=True, - mac_refresh=False, - group_refresh=True + base=True, access_refresh=True, mac_refresh=False, group_refresh=True ) - regen('mailing') + regen("mailing") @receiver(m2m_changed, sender=User.groups.through) def user_group_relation_changed(**kwargs): - action = kwargs['action'] - if action in ('post_add', 'post_remove', 'post_clear'): - user = kwargs['instance'] - user.ldap_sync(base=False, - access_refresh=False, - mac_refresh=False, - group_refresh=True) + action = kwargs["action"] + if action in ("post_add", "post_remove", "post_clear"): + user = kwargs["instance"] + user.ldap_sync( + base=False, access_refresh=False, mac_refresh=False, group_refresh=True + ) @receiver(post_delete, sender=Adherent) @@ -1222,44 +1409,31 @@ def user_group_relation_changed(**kwargs): @receiver(post_delete, sender=User) def user_post_delete(**kwargs): """Post delete d'un user, on supprime son instance ldap""" - user = kwargs['instance'] + user = kwargs["instance"] user.ldap_del() - regen('mailing') + regen("mailing") class ServiceUser(RevMixin, AclMixin, AbstractBaseUser): """ Classe des users daemons, règle leurs accès au ldap""" - readonly = 'readonly' - ACCESS = ( - ('auth', 'auth'), - ('readonly', 'readonly'), - ('usermgmt', 'usermgmt'), - ) + + readonly = "readonly" + ACCESS = (("auth", "auth"), ("readonly", "readonly"), ("usermgmt", "usermgmt")) pseudo = models.CharField( max_length=32, unique=True, help_text=_("Must only contain letters, numerals or dashes."), - validators=[linux_user_validator] - ) - access_group = models.CharField( - choices=ACCESS, - default=readonly, - max_length=32 - ) - comment = models.CharField( - help_text=_("Comment"), - max_length=255, - blank=True + validators=[linux_user_validator], ) + access_group = models.CharField(choices=ACCESS, default=readonly, max_length=32) + comment = models.CharField(help_text=_("Comment."), max_length=255, blank=True) - USERNAME_FIELD = 'pseudo' + USERNAME_FIELD = "pseudo" objects = UserManager() class Meta: - permissions = ( - ("view_serviceuser", _("Can view a service user object")), - ) + permissions = (("view_serviceuser", _("Can view a service user object")),) verbose_name = _("service user") verbose_name_plural = _("service users") @@ -1296,10 +1470,16 @@ class ServiceUser(RevMixin, AclMixin, AbstractBaseUser): group = LdapServiceUserGroup.objects.get(name=self.access_group) except: group = LdapServiceUserGroup(name=self.access_group) - group.members = list(LdapServiceUser.objects.filter( - name__in=[user.pseudo for user in ServiceUser.objects.filter( - access_group=self.access_group - )]).values_list('dn', flat=True)) + group.members = list( + LdapServiceUser.objects.filter( + name__in=[ + user.pseudo + for user in ServiceUser.objects.filter( + access_group=self.access_group + ) + ] + ).values_list("dn", flat=True) + ) group.save() def __str__(self): @@ -1309,14 +1489,14 @@ class ServiceUser(RevMixin, AclMixin, AbstractBaseUser): @receiver(post_save, sender=ServiceUser) def service_user_post_save(**kwargs): """ Synchronise un service user ldap après modification django""" - service_user = kwargs['instance'] + service_user = kwargs["instance"] service_user.ldap_sync() @receiver(post_delete, sender=ServiceUser) def service_user_post_delete(**kwargs): """ Supprime un service user ldap après suppression django""" - service_user = kwargs['instance'] + service_user = kwargs["instance"] service_user.ldap_del() @@ -1326,9 +1506,7 @@ class School(RevMixin, AclMixin, models.Model): name = models.CharField(max_length=255) class Meta: - permissions = ( - ("view_school", _("Can view a school object")), - ) + permissions = (("view_school", _("Can view a school object")),) verbose_name = _("school") verbose_name_plural = _("schools") @@ -1346,23 +1524,19 @@ class ListRight(RevMixin, AclMixin, Group): unix_name = models.CharField( max_length=255, unique=True, - validators=[RegexValidator( - '^[a-z]+$', - message=(_("UNIX groups can only contain lower case letters.")) - )] + validators=[ + RegexValidator( + "^[a-z]+$", + message=(_("UNIX group names can only contain lower case letters.")), + ) + ], ) gid = models.PositiveIntegerField(unique=True, null=True) critical = models.BooleanField(default=False) - details = models.CharField( - help_text=_("Description"), - max_length=255, - blank=True - ) + details = models.CharField(help_text=_("Description."), max_length=255, blank=True) class Meta: - permissions = ( - ("view_listright", _("Can view a group of rights object")), - ) + permissions = (("view_listright", _("Can view a group of rights object")),) verbose_name = _("group of rights") verbose_name_plural = _("groups of rights") @@ -1376,8 +1550,7 @@ class ListRight(RevMixin, AclMixin, Group): except LdapUserGroup.DoesNotExist: group_ldap = LdapUserGroup(gid=self.gid) group_ldap.name = self.unix_name - group_ldap.members = [user.pseudo for user - in self.user_set.all()] + group_ldap.members = [user.pseudo for user in self.user_set.all()] group_ldap.save() def ldap_del(self): @@ -1392,14 +1565,14 @@ class ListRight(RevMixin, AclMixin, Group): @receiver(post_save, sender=ListRight) def listright_post_save(**kwargs): """ Synchronise le droit ldap quand il est modifié""" - right = kwargs['instance'] + right = kwargs["instance"] right.ldap_sync() @receiver(post_delete, sender=ListRight) def listright_post_delete(**kwargs): """Suppression d'un groupe ldap après suppression coté django""" - right = kwargs['instance'] + right = kwargs["instance"] right.ldap_del() @@ -1410,13 +1583,10 @@ class ListShell(RevMixin, AclMixin, models.Model): shell = models.CharField(max_length=255, unique=True) class Meta: - permissions = ( - ("view_listshell", _("Can view a shell object")), - ) + permissions = (("view_listshell", _("Can view a shell object")),) verbose_name = _("shell") verbose_name_plural = _("shells") - def get_pretty_name(self): """Return the canonical name of the shell""" return self.shell.split("/")[-1] @@ -1438,34 +1608,32 @@ class Ban(RevMixin, AclMixin, models.Model): (2, _("RESTRICTED (speed limitation)")), ) - user = models.ForeignKey('User', on_delete=models.PROTECT) + user = models.ForeignKey("User", on_delete=models.PROTECT) raison = models.CharField(max_length=255) date_start = models.DateTimeField(auto_now_add=True) date_end = models.DateTimeField() state = models.IntegerField(choices=STATES, default=STATE_HARD) class Meta: - permissions = ( - ("view_ban", _("Can view a ban object")), - ) + permissions = (("view_ban", _("Can view a ban object")),) verbose_name = _("ban") verbose_name_plural = _("bans") def notif_ban(self): """ Prend en argument un objet ban, envoie un mail de notification """ - template = loader.get_template('users/email_ban_notif') - context = Context({ - 'name': self.user.get_full_name(), - 'raison': self.raison, - 'date_end': self.date_end, - 'asso_name': AssoOption.get_cached_value('name'), - }) + template = loader.get_template("users/email_ban_notif") + context = { + "name": self.user.get_full_name(), + "raison": self.raison, + "date_end": self.date_end, + "asso_name": AssoOption.get_cached_value("name"), + } send_mail( - 'Déconnexion disciplinaire / Disciplinary disconnection', + "Déconnexion disciplinaire / Disciplinary disconnection", template.render(context), - GeneralOption.get_cached_value('email_from'), + GeneralOption.get_cached_value("email_from"), [self.user.email], - fail_silently=False + fail_silently=False, ) return @@ -1481,42 +1649,44 @@ class Ban(RevMixin, AclMixin, models.Model): :return: A boolean telling if the acces is granted and an explanation text """ - if (not user_request.has_perm('users.view_ban') and - self.user != user_request): - return False, (_("You don't have the right to view bans other" - " than yours.")) + if not user_request.has_perm("users.view_ban") and self.user != user_request: + return ( + False, + _("You don't have the right to view other bans than yours."), + ("users.view_ban",), + ) else: - return True, None + return True, None, None def __str__(self): - return str(self.user) + ' ' + str(self.raison) + return str(self.user) + " " + str(self.raison) @receiver(post_save, sender=Ban) def ban_post_save(**kwargs): """ Regeneration de tous les services après modification d'un ban""" - ban = kwargs['instance'] - is_created = kwargs['created'] + ban = kwargs["instance"] + is_created = kwargs["created"] user = ban.user user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) - regen('mailing') + regen("mailing") if is_created: ban.notif_ban() - regen('dhcp') - regen('mac_ip_list') + regen("dhcp") + regen("mac_ip_list") if user.has_access(): - regen('dhcp') - regen('mac_ip_list') + regen("dhcp") + regen("mac_ip_list") @receiver(post_delete, sender=Ban) def ban_post_delete(**kwargs): """ Regen de tous les services après suppression d'un ban""" - user = kwargs['instance'].user + user = kwargs["instance"].user user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) - regen('mailing') - regen('dhcp') - regen('mac_ip_list') + regen("mailing") + regen("dhcp") + regen("mac_ip_list") class Whitelist(RevMixin, AclMixin, models.Model): @@ -1524,15 +1694,13 @@ class Whitelist(RevMixin, AclMixin, models.Model): accorder un accès internet pour une durée défini. Moins fort qu'un ban quel qu'il soit""" - user = models.ForeignKey('User', on_delete=models.PROTECT) + user = models.ForeignKey("User", on_delete=models.PROTECT) raison = models.CharField(max_length=255) date_start = models.DateTimeField(auto_now_add=True) date_end = models.DateTimeField() class Meta: - permissions = ( - ("view_whitelist", _("Can view a whitelist object")), - ) + permissions = (("view_whitelist", _("Can view a whitelist object")),) verbose_name = _("whitelist (free of charge access)") verbose_name_plural = _("whitelists (free of charge access)") @@ -1548,71 +1716,71 @@ class Whitelist(RevMixin, AclMixin, models.Model): :return: A boolean telling if the acces is granted and an explanation text """ - if (not user_request.has_perm('users.view_whitelist') and - self.user != user_request): - return False, (_("You don't have the right to view whitelists" - " other than yours.")) + if ( + not user_request.has_perm("users.view_whitelist") + and self.user != user_request + ): + return ( + False, + _("You don't have the right to view other whitelists than yours."), + ("users.view_whitelist",), + ) else: - return True, None + return True, None, None def __str__(self): - return str(self.user) + ' ' + str(self.raison) + return str(self.user) + " " + str(self.raison) @receiver(post_save, sender=Whitelist) def whitelist_post_save(**kwargs): """Après modification d'une whitelist, on synchronise les services et on lui permet d'avoir internet""" - whitelist = kwargs['instance'] + whitelist = kwargs["instance"] user = whitelist.user user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) - is_created = kwargs['created'] - regen('mailing') + is_created = kwargs["created"] + regen("mailing") if is_created: - regen('dhcp') - regen('mac_ip_list') + regen("dhcp") + regen("mac_ip_list") if user.has_access(): - regen('dhcp') - regen('mac_ip_list') + regen("dhcp") + regen("mac_ip_list") @receiver(post_delete, sender=Whitelist) def whitelist_post_delete(**kwargs): """Après suppression d'une whitelist, on supprime l'accès internet en forçant la régénration""" - user = kwargs['instance'].user + user = kwargs["instance"].user user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) - regen('mailing') - regen('dhcp') - regen('mac_ip_list') + regen("mailing") + regen("dhcp") + regen("mac_ip_list") class Request(models.Model): """ Objet request, générant une url unique de validation. Utilisé par exemple pour la generation du mot de passe et sa réinitialisation""" - PASSWD = 'PW' - EMAIL = 'EM' - TYPE_CHOICES = ( - (PASSWD, _("Password")), - (EMAIL, _("Email address")), - ) + + PASSWD = "PW" + EMAIL = "EM" + TYPE_CHOICES = ((PASSWD, _("Password")), (EMAIL, _("Email address"))) type = models.CharField(max_length=2, choices=TYPE_CHOICES) token = models.CharField(max_length=32) - user = models.ForeignKey('User', on_delete=models.CASCADE) + user = models.ForeignKey("User", on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True, editable=False) expires_at = models.DateTimeField() def save(self): if not self.expires_at: - self.expires_at = (timezone.now() + - datetime.timedelta( - hours=GeneralOption.get_cached_value( - 'req_expire_hrs' - ) - )) + self.expires_at = timezone.now() + datetime.timedelta( + hours=GeneralOption.get_cached_value("req_expire_hrs") + ) if not self.token: - self.token = str(uuid.uuid4()).replace('-', '') # remove hyphens + self.token = str(uuid.uuid4()).replace("-", "") # remove hyphens super(Request, self).save() @@ -1620,73 +1788,50 @@ class LdapUser(ldapdb.models.Model): """ Class for representing an LDAP user entry. """ + # LDAP meta-data - base_dn = LDAP['base_user_dn'] - object_classes = ['inetOrgPerson', 'top', 'posixAccount', - 'sambaSamAccount', 'radiusprofile', - 'shadowAccount'] + base_dn = LDAP["base_user_dn"] + object_classes = [ + "inetOrgPerson", + "top", + "posixAccount", + "sambaSamAccount", + "radiusprofile", + "shadowAccount", + ] # attributes - gid = ldapdb.models.fields.IntegerField(db_column='gidNumber') + gid = ldapdb.models.fields.IntegerField(db_column="gidNumber") name = ldapdb.models.fields.CharField( - db_column='cn', - max_length=200, - primary_key=True + db_column="cn", max_length=200, primary_key=True ) - uid = ldapdb.models.fields.CharField(db_column='uid', max_length=200) - uidNumber = ldapdb.models.fields.IntegerField( - db_column='uidNumber', - unique=True - ) - sn = ldapdb.models.fields.CharField(db_column='sn', max_length=200) + uid = ldapdb.models.fields.CharField(db_column="uid", max_length=200) + uidNumber = ldapdb.models.fields.IntegerField(db_column="uidNumber", unique=True) + sn = ldapdb.models.fields.CharField(db_column="sn", max_length=200) login_shell = ldapdb.models.fields.CharField( - db_column='loginShell', - max_length=200, - blank=True, - null=True - ) - mail = ldapdb.models.fields.CharField(db_column='mail', max_length=200) - given_name = ldapdb.models.fields.CharField( - db_column='givenName', - max_length=200 + db_column="loginShell", max_length=200, blank=True, null=True ) + mail = ldapdb.models.fields.CharField(db_column="mail", max_length=200) + given_name = ldapdb.models.fields.CharField(db_column="givenName", max_length=200) home_directory = ldapdb.models.fields.CharField( - db_column='homeDirectory', - max_length=200 + db_column="homeDirectory", max_length=200 ) display_name = ldapdb.models.fields.CharField( - db_column='displayName', - max_length=200, - blank=True, - null=True - ) - dialupAccess = ldapdb.models.fields.CharField(db_column='dialupAccess') - sambaSID = ldapdb.models.fields.IntegerField( - db_column='sambaSID', - unique=True + db_column="displayName", max_length=200, blank=True, null=True ) + dialupAccess = ldapdb.models.fields.CharField(db_column="dialupAccess") + sambaSID = ldapdb.models.fields.IntegerField(db_column="sambaSID", unique=True) user_password = ldapdb.models.fields.CharField( - db_column='userPassword', - max_length=200, - blank=True, - null=True + db_column="userPassword", max_length=200, blank=True, null=True ) sambat_nt_password = ldapdb.models.fields.CharField( - db_column='sambaNTPassword', - max_length=200, - blank=True, - null=True + db_column="sambaNTPassword", max_length=200, blank=True, null=True ) macs = ldapdb.models.fields.ListField( - db_column='radiusCallingStationId', - max_length=200, - blank=True, - null=True + db_column="radiusCallingStationId", max_length=200, blank=True, null=True ) shadowexpire = ldapdb.models.fields.CharField( - db_column='shadowExpire', - blank=True, - null=True + db_column="shadowExpire", blank=True, null=True ) def __str__(self): @@ -1708,20 +1853,16 @@ class LdapUserGroup(ldapdb.models.Model): Un groupe ldap """ + # LDAP meta-data - base_dn = LDAP['base_usergroup_dn'] - object_classes = ['posixGroup'] + base_dn = LDAP["base_usergroup_dn"] + object_classes = ["posixGroup"] # attributes - gid = ldapdb.models.fields.IntegerField(db_column='gidNumber') - members = ldapdb.models.fields.ListField( - db_column='memberUid', - blank=True - ) + gid = ldapdb.models.fields.IntegerField(db_column="gidNumber") + members = ldapdb.models.fields.ListField(db_column="memberUid", blank=True) name = ldapdb.models.fields.CharField( - db_column='cn', - max_length=200, - primary_key=True + db_column="cn", max_length=200, primary_key=True ) def __str__(self): @@ -1734,21 +1875,17 @@ class LdapServiceUser(ldapdb.models.Model): Un user de service coté ldap """ + # LDAP meta-data - base_dn = LDAP['base_userservice_dn'] - object_classes = ['applicationProcess', 'simpleSecurityObject'] + base_dn = LDAP["base_userservice_dn"] + object_classes = ["applicationProcess", "simpleSecurityObject"] # attributes name = ldapdb.models.fields.CharField( - db_column='cn', - max_length=200, - primary_key=True + db_column="cn", max_length=200, primary_key=True ) user_password = ldapdb.models.fields.CharField( - db_column='userPassword', - max_length=200, - blank=True, - null=True + db_column="userPassword", max_length=200, blank=True, null=True ) def __str__(self): @@ -1762,20 +1899,16 @@ class LdapServiceUserGroup(ldapdb.models.Model): Un group user de service coté ldap. Dans userservicegroupdn (voir dans settings_local.py) """ + # LDAP meta-data - base_dn = LDAP['base_userservicegroup_dn'] - object_classes = ['groupOfNames'] + base_dn = LDAP["base_userservicegroup_dn"] + object_classes = ["groupOfNames"] # attributes name = ldapdb.models.fields.CharField( - db_column='cn', - max_length=200, - primary_key=True - ) - members = ldapdb.models.fields.ListField( - db_column='member', - blank=True + db_column="cn", max_length=200, primary_key=True ) + members = ldapdb.models.fields.ListField(db_column="member", blank=True) def __str__(self): return self.name @@ -1784,15 +1917,12 @@ class LdapServiceUserGroup(ldapdb.models.Model): class EMailAddress(RevMixin, AclMixin, models.Model): """Defines a local email account for a user """ + user = models.ForeignKey( - User, - on_delete=models.CASCADE, - help_text=_("User of the local email account") + User, on_delete=models.CASCADE, help_text=_("User of the local email account.") ) local_part = models.CharField( - unique=True, - max_length=128, - help_text=_("Local part of the email address") + unique=True, max_length=128, help_text=_("Local part of the email address.") ) class Meta: @@ -1803,11 +1933,15 @@ class EMailAddress(RevMixin, AclMixin, models.Model): verbose_name_plural = _("local email accounts") def __str__(self): - return str(self.local_part) + OptionalUser.get_cached_value('local_email_domain') + return str(self.local_part) + OptionalUser.get_cached_value( + "local_email_domain" + ) @cached_property def complete_email_address(self): - return str(self.local_part) + OptionalUser.get_cached_value('local_email_domain') + return str(self.local_part) + OptionalUser.get_cached_value( + "local_email_domain" + ) @staticmethod def can_create(user_request, userid, *_args, **_kwargs): @@ -1821,18 +1955,30 @@ class EMailAddress(RevMixin, AclMixin, models.Model): a message and a boolean which is True if the user can create a local email account. """ - if user_request.has_perm('users.add_emailaddress'): - return True, None - if not OptionalUser.get_cached_value('local_email_accounts_enabled'): - return False, _("The local email accounts are not enabled.") + if user_request.has_perm("users.add_emailaddress"): + return True, None, None + if not OptionalUser.get_cached_value("local_email_accounts_enabled"): + return (False, _("The local email accounts are not enabled."), None) if int(user_request.id) != int(userid): - return False, _("You don't have the right to add a local email" - " account to another user.") - elif user_request.email_address.count() >= OptionalUser.get_cached_value('max_email_address'): - return False, _("You reached the limit of {} local email accounts.").format( - OptionalUser.get_cached_value('max_email_address') + return ( + False, + _( + "You don't have the right to add a local email" + " account to another user." + ), + ("users.add_emailaddress",), ) - return True, None + elif user_request.email_address.count() >= OptionalUser.get_cached_value( + "max_email_address" + ): + return ( + False, + _("You reached the limit of {} local email accounts.").format( + OptionalUser.get_cached_value("max_email_address") + ), + None, + ) + return True, None, None def can_view(self, user_request, *_args, **_kwargs): """Check if a user can view the local email account @@ -1844,14 +1990,20 @@ class EMailAddress(RevMixin, AclMixin, models.Model): a message and a boolean which is True if the user can see the local email account. """ - if user_request.has_perm('users.view_emailaddress'): - return True, None - if not OptionalUser.get_cached_value('local_email_accounts_enabled'): - return False, _("The local email accounts are not enabled.") + if user_request.has_perm("users.view_emailaddress"): + return True, None, None + if not OptionalUser.get_cached_value("local_email_accounts_enabled"): + return (False, _("The local email accounts are not enabled."), None) if user_request == self.user: - return True, None - return False, _("You don't have the right to edit another user's local" - " email account.") + return True, None, None + return ( + False, + _( + "You don't have the right to view another user's local" + " email account." + ), + ("users.view_emailaddress",), + ) def can_delete(self, user_request, *_args, **_kwargs): """Check if a user can delete the alias @@ -1864,16 +2016,28 @@ class EMailAddress(RevMixin, AclMixin, models.Model): the local email account. """ if self.local_part == self.user.pseudo.lower(): - return False, _("You can't delete a local email account whose" - " local part is the same as the username.") - if user_request.has_perm('users.delete_emailaddress'): - return True, None - if not OptionalUser.get_cached_value('local_email_accounts_enabled'): - return False, _("The local email accounts are not enabled.") + return ( + False, + _( + "You can't delete a local email account whose" + " local part is the same as the username." + ), + None, + ) + if user_request.has_perm("users.delete_emailaddress"): + return True, None, None + if not OptionalUser.get_cached_value("local_email_accounts_enabled"): + return False, _("The local email accounts are not enabled."), None if user_request == self.user: - return True, None - return False, _("You don't have the right to delete another user's" - " local email account") + return True, None, None + return ( + False, + _( + "You don't have the right to delete another user's" + " local email account." + ), + ("users.delete_emailaddress",), + ) def can_edit(self, user_request, *_args, **_kwargs): """Check if a user can edit the alias @@ -1886,16 +2050,28 @@ class EMailAddress(RevMixin, AclMixin, models.Model): the local email account. """ if self.local_part == self.user.pseudo.lower(): - return False, _("You can't edit a local email account whose local" - " part is the same as the username.") - if user_request.has_perm('users.change_emailaddress'): - return True, None - if not OptionalUser.get_cached_value('local_email_accounts_enabled'): - return False, _("The local email accounts are not enabled.") + return ( + False, + _( + "You can't edit a local email account whose local" + " part is the same as the username." + ), + None, + ) + if user_request.has_perm("users.change_emailaddress"): + return True, None, None + if not OptionalUser.get_cached_value("local_email_accounts_enabled"): + return False, _("The local email accounts are not enabled."), None if user_request == self.user: - return True, None - return False, _("You don't have the right to edit another user's local" - " email account.") + return True, None, None + return ( + False, + _( + "You don't have the right to edit another user's local" + " email account." + ), + ("users.change_emailaddress",), + ) def clean(self, *args, **kwargs): self.local_part = self.local_part.lower() diff --git a/users/serializers.py b/users/serializers.py index be925881..65ac7ef1 100644 --- a/users/serializers.py +++ b/users/serializers.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -33,11 +33,11 @@ from users.models import Club, Adherent class MailingSerializer(serializers.ModelSerializer): """ Serializer to build Mailing objects """ - name = serializers.CharField(source='pseudo') + name = serializers.CharField(source="pseudo") class Meta: model = Club - fields = ('name',) + fields = ("name",) class MailingMemberSerializer(serializers.ModelSerializer): @@ -46,4 +46,4 @@ class MailingMemberSerializer(serializers.ModelSerializer): class Meta: model = Adherent - fields = ('email',) + fields = ("email",) diff --git a/users/templates/users/aff_bans.html b/users/templates/users/aff_bans.html index 30256747..f4f973f7 100644 --- a/users/templates/users/aff_bans.html +++ b/users/templates/users/aff_bans.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/users/templates/users/aff_clubs.html b/users/templates/users/aff_clubs.html index 080dc031..e4ea9857 100644 --- a/users/templates/users/aff_clubs.html +++ b/users/templates/users/aff_clubs.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/users/templates/users/aff_emailaddress.html b/users/templates/users/aff_emailaddress.html index f5d5dd1d..9bd3c8d0 100644 --- a/users/templates/users/aff_emailaddress.html +++ b/users/templates/users/aff_emailaddress.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/users/templates/users/aff_listright.html b/users/templates/users/aff_listright.html index 8a069529..489a713a 100644 --- a/users/templates/users/aff_listright.html +++ b/users/templates/users/aff_listright.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -37,7 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Superuser" %} - {% trans "Django's specific pre-defined right that supersed any other rights." %} + {% trans "Django's specific pre-defined right that supersedes any other rights." %}
    diff --git a/users/templates/users/aff_rights.html b/users/templates/users/aff_rights.html index 710c1f14..bb3bdec9 100644 --- a/users/templates/users/aff_rights.html +++ b/users/templates/users/aff_rights.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/users/templates/users/aff_schools.html b/users/templates/users/aff_schools.html index 9ec3b443..586c657c 100644 --- a/users/templates/users/aff_schools.html +++ b/users/templates/users/aff_schools.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/users/templates/users/aff_serviceusers.html b/users/templates/users/aff_serviceusers.html index b5e0ae62..68de1d99 100644 --- a/users/templates/users/aff_serviceusers.html +++ b/users/templates/users/aff_serviceusers.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Name" %} - {% trans "Role" %} + {% trans "Access group" %} {% trans "Comment" %} diff --git a/users/templates/users/aff_shell.html b/users/templates/users/aff_shell.html index b422ab52..e95d79f0 100644 --- a/users/templates/users/aff_shell.html +++ b/users/templates/users/aff_shell.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/users/templates/users/aff_users.html b/users/templates/users/aff_users.html index fe6e431a..8365e259 100644 --- a/users/templates/users/aff_users.html +++ b/users/templates/users/aff_users.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/users/templates/users/aff_whitelists.html b/users/templates/users/aff_whitelists.html index ea2d1d4f..e93f3ecf 100644 --- a/users/templates/users/aff_whitelists.html +++ b/users/templates/users/aff_whitelists.html @@ -4,7 +4,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/users/templates/users/delete.html b/users/templates/users/delete.html index 9490c0c4..3e923617 100644 --- a/users/templates/users/delete.html +++ b/users/templates/users/delete.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/users/templates/users/email_auto_newmachine b/users/templates/users/email_auto_newmachine index d98cc160..69ae6dec 100644 --- a/users/templates/users/email_auto_newmachine +++ b/users/templates/users/email_auto_newmachine @@ -1,27 +1,31 @@ -

    Bonjour {{nom}}

    +

    Bonjour {{ nom }},

    Une nouvelle machine a automatiquement été inscrite sur votre compte.

    -

    Si vous êtes à l'origine de la connexion de cet appareil en filaire ou wifi, ne tenez pas compte de cette notification

    +

    Si vous êtes à l'origine de la connexion de cet appareil en filaire ou Wi-Fi, ne tenez pas compte de ce mail.

    -

    La nouvelle machine possède l'adresse mac {{ mac_address }}, et s'est vu assigner le nom suivant : {{ interface_name }}

    +

    La nouvelle machine possède l'adresse MAC {{ mac_address }}, et s'est vu assigner le nom suivant : {{ interface_name }}.

    -

    Vous pouvez à tout moment modifier ces informations sur votre compte en ligne

    +

    Vous pouvez à tout moment modifier ces informations sur votre compte en ligne.

    -

    Si vous n'êtes pas à l'origine de cette connexion, merci de le signaler rapidement à {{asso_email}}

    +

    Si vous n'êtes pas à l'origine de cette connexion, veuillez le signaler rapidement à {{ asso_email }}.

    -

    À bientôt,
    -L'équipe de {{asso_name}}.

    +

    Respectueusement,
    +L'équipe de {{ asso_name }}.

    ---

    -

    A new device has been automatically added on your account.

    +

    Hello {{ nom }},

    -

    If you connected a new device recently, please don't take this mail into account

    +

    A new machine has been automatically added on your account.

    -

    The new device has this mac address : {{ mac_address }}, and this name : {{ interface_name }}

    +

    If you are the one who connected this machine with wire of Wi-Fi, don't take this mail into account.

    -

    Please contact us if you didn't connect a new device with this mail address {{asso_email}}.

    +

    The new machine has this MAC address: {{ mac_address }}, and has been assigned this name: {{ interface_name }}

    + +

    At any time, you can edit this information on your online account.

    + +

    If you didn't connect this machine, please report it quickly to {{ asso_email }}.

    Regards,
    -The {{asso_name}} team.

    +The {{ asso_name }} team.

    diff --git a/users/templates/users/email_ban_notif b/users/templates/users/email_ban_notif index f61cd840..294bb1e5 100644 --- a/users/templates/users/email_ban_notif +++ b/users/templates/users/email_ban_notif @@ -1,8 +1,19 @@ -Bonjour {{name}}, +

    Bonjour {{ name }},

    -Vous avez été banni par un administrateur de {{ asso_name }} en raison de {{raison}}. Vous n'avez plus accès au réseau jusqu'au {{date_end}}. +

    Vous avez été banni par un administrateur de {{ asso_name }} pour la raison suivante : {{ raison }}. Vous n'avez plus accès au réseau jusqu'au {{ date_end }}.

    -Pour de plus amples informations, rendez-vous à l'accueil de {{ asso_name }}. +

    Pour de plus amples renseignements, contactez le comité de direction de {{ asso_name }}.

    -Cordialement, - L'équipe de {{ asso_name }}. +

    Respectueusement,
    +L'équipe de {{ asso_name }}.

    + +

    ---

    + +

    Hello {{ name }},

    + +

    You have been banned by a administrator of {{ asso_name }} for the following reason: {{ raison }}. You don't have access to the network anymore until {{ date_end }}.

    + +

    For more information, contact the steering committee of {{ asso_name }}.

    + +

    Regards,
    +The {{ asso_name }} team.

    diff --git a/users/templates/users/email_passwd_request b/users/templates/users/email_passwd_request index 2a648d55..21049ae4 100644 --- a/users/templates/users/email_passwd_request +++ b/users/templates/users/email_passwd_request @@ -1,33 +1,33 @@ Bonjour {{ name }}, -Vous trouverez ci-dessous une url permetant d'initialiser ou de reinitialiser votre -compte {{ site_name }}. Celui-ci vous permet de gérer l'ensemble de vos équipements -connectés, votre compte, vos factures, et tous les services proposés sur le réseau. +Vous trouverez ci-dessous une URL permettant d'initialiser ou de réinitialiser votre +mot de passe pour votre compte {{ site_name }}. Celui-ci vous permet de gérer l'ensemble +de vos équipements, votre compte, vos factures, et tous les services proposés sur le réseau. {{ url }} Contactez les administrateurs si vous n'êtes pas à l'origine de cette requête. -Ce lien expirera dans {{ expire_in }}. +Ce lien expirera dans {{ expire_in }} heures. -Cordialement, +Respectueusement, L'équipe de {{ asso }} (contact : {{ asso_mail }}). ----------------------- +--- -Hi {{ name }}, +Hello {{ name }}, -You will find a link allowing you to change the password of your account on {{ site_name }}. -On this website you will then be able to manage your devices on the {{ asso }}. +You will find below an URL allowing you to set or reset your the password of your account +on {{ site_name }}. It enables you to manage your devices, your account, your invoices, and all +the services offered on the network. {{ url }} -This link will expire in {{ expire_in }}. +Contact the administrators if you didn't request this. -Send an email at {{ asso_mail }} if you didn't request this or if you have -any other question. +This link will expire in {{ expire_in }} hours. -Thanks +Regards, -The team of {{ asso }} (contact : {{ asso_mail }}). +The {{ asso }} team (contact: {{ asso_mail }}). diff --git a/users/templates/users/email_welcome b/users/templates/users/email_welcome index 27e92cc7..ba1b1756 100644 --- a/users/templates/users/email_welcome +++ b/users/templates/users/email_welcome @@ -2,7 +2,7 @@

    Il vous suffit maintenant de payer l'adhésion pour devenir adhérent de {{asso_name}} et bénéficier des services. Pour avoir accès à Internet, il vous faudra payer la connexion.

    -

    Pour ce faire, rien de plus simple. Une fois que vous aurez défini votre mot de passe, il vous suffira d'accéder à votre profil et de cliquer sur 'Payer la connexion'

    +

    Pour ce faire, rien de plus simple. Une fois que vous aurez défini votre mot de passe, il vous suffira d'accéder à votre profil et de cliquer sur « Payer une connexion »

    Votre pseudo est : {{pseudo}}

    @@ -10,21 +10,22 @@

    Pour nous faire part de toute remarque, suggestion ou problème vous pouvez nous envoyer un mail à {{asso_email}}.

    -

    À bientôt,
    +

    Respectueusement,
    L'équipe de {{asso_name}}.

    ---

    -

    You just need to pay membership to become a full member of {{asso_name}} and benefit from the services. To have Internet access, you will need to pay the connexion.

    +

    Hello {{nom}}!

    -

    Nothing could be simpler. Once your password is defined, you just need to visit your profile and click on 'Pay for a connexion'.

    +

    You just need to pay the membership fee to become a contributing member of {{asso_name}} and benefit from the services. To get Internet access, you will need to pay the connection.

    -

    Your username is : {{pseudo}}

    +

    Nothing could be simpler. Once your password is defined, you just need to access your profile and click on "Pay for a connection".

    + +

    Your username is: {{pseudo}}

    {{welcome_mail_en|safe}} -

    For any information, suggestion or problem, you can contact us via email at
    -{{asso_email}}.

    +

    To express any comment, suggestion or problem, you can send us an email to {{asso_email}}.

    Regards,
    The {{asso_name}} team.

    diff --git a/users/templates/users/index.html b/users/templates/users/index.html index 4691114f..048ed9ee 100644 --- a/users/templates/users/index.html +++ b/users/templates/users/index.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/users/templates/users/index_ban.html b/users/templates/users/index_ban.html index 1efa669f..07ad100f 100644 --- a/users/templates/users/index_ban.html +++ b/users/templates/users/index_ban.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/users/templates/users/index_clubs.html b/users/templates/users/index_clubs.html index 88a9b4fa..73493a19 100644 --- a/users/templates/users/index_clubs.html +++ b/users/templates/users/index_clubs.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/users/templates/users/index_emailaddress.html b/users/templates/users/index_emailaddress.html index 2b170f8c..e337d2ba 100644 --- a/users/templates/users/index_emailaddress.html +++ b/users/templates/users/index_emailaddress.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/users/templates/users/index_listright.html b/users/templates/users/index_listright.html index 35738413..32b18134 100644 --- a/users/templates/users/index_listright.html +++ b/users/templates/users/index_listright.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -32,9 +32,9 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block content %}

    {% trans "List of groups of rights" %}

    {% can_create ListRight %} - {% trans " Add a group of rights" %} + {% trans "Add a group of rights" %} {% acl_end %} - {% trans " Delete one or several groups of rights" %} + {% trans "Delete one or several groups of rights" %}

    {% include 'users/aff_listright.html' %} diff --git a/users/templates/users/index_rights.html b/users/templates/users/index_rights.html index a0fa5bc0..d89c454c 100644 --- a/users/templates/users/index_rights.html +++ b/users/templates/users/index_rights.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/users/templates/users/index_schools.html b/users/templates/users/index_schools.html index e27f63a9..6e98465b 100644 --- a/users/templates/users/index_schools.html +++ b/users/templates/users/index_schools.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -31,11 +31,11 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block content %}

    {% trans "List of schools" %}

    -
    {% trans "List of schools for created users" %}
    +
    {% trans "List of schools for registered users" %}
    {% can_create School %} - {% trans " Add a school" %} + {% trans "Add a school" %} {% acl_end %} - {% trans " Delete one or several schools" %} + {% trans "Delete one or several schools" %}
    {% include 'users/aff_schools.html' with school_list=school_list %}
    diff --git a/users/templates/users/index_serviceusers.html b/users/templates/users/index_serviceusers.html index 90aa8a63..0ab7db9b 100644 --- a/users/templates/users/index_serviceusers.html +++ b/users/templates/users/index_serviceusers.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    {% trans "List of LDAP service users" %}

    {% trans "The LDAP service users are special users having access only to the LDAP for authentication operations. It is recommended to create a service user with a login and a password for any concerned service." %}
    {% can_create ServiceUser %} - {% trans " Add a service user" %} + {% trans "Add a service user" %} {% acl_end %} {% include 'users/aff_serviceusers.html' with serviceusers_list=serviceusers_list %}
    diff --git a/users/templates/users/index_shell.html b/users/templates/users/index_shell.html index 9d75fdd8..e029b107 100644 --- a/users/templates/users/index_shell.html +++ b/users/templates/users/index_shell.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block content %}

    {% trans "List of shells" %}

    {% can_create ListShell %} - {% trans " Add a shell" %} + {% trans "Add a shell" %} {% acl_end %} {% include 'users/aff_shell.html' with shell_list=shell_list %}
    diff --git a/users/templates/users/index_whitelist.html b/users/templates/users/index_whitelist.html index 67164ffc..544e7db8 100644 --- a/users/templates/users/index_whitelist.html +++ b/users/templates/users/index_whitelist.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/users/templates/users/mass_archive.html b/users/templates/users/mass_archive.html index 54281945..5b44f6ec 100644 --- a/users/templates/users/mass_archive.html +++ b/users/templates/users/mass_archive.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -36,7 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc., -

    {% blocktrans %}The following users will be archived ({{ to_archive_list|length }}):{% endblocktrans %}

    +

    {% blocktrans %}The following users will be archived:{% endblocktrans %}

    {% include 'users/aff_users.html' with users_list=to_archive_list %}

    diff --git a/users/templates/users/plugin_out.html b/users/templates/users/plugin_out.html index a6261250..23e696fe 100644 --- a/users/templates/users/plugin_out.html +++ b/users/templates/users/plugin_out.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index 7f560d06..0bd25f75 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -43,7 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    {% if users.is_ban%}
    -
    {% trans "Your account has been banned" %}
    +
    {% trans "Your account has been banned." %}
    {% blocktrans with end_ban_date=users.end_ban|date:"SHORT_DATE_FORMAT" %}End of the ban: {{ end_ban_date }}{% endblocktrans %}
    @@ -57,7 +57,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Pay for a connection" %} {% acl_else %} - {% trans "Ask for someone with the appropriate rights to pay for a connection." %} + {% trans "Ask someone with the appropriate rights to pay for a connection." %} {% acl_end %}
    @@ -94,11 +94,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    - {% trans " Machines" %} {{ nb_machines }} + {% trans "Machines" %} {{ nb_machines }}
    @@ -110,7 +110,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    @@ -119,12 +119,13 @@ with this program; if not, write to the Free Software Foundation, Inc., +

    - {% trans " Detailed information" %} + {% trans "Detailed information" %}

    @@ -186,9 +187,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    {% trans "Room" %}
    - {{ users.room }} {% can_view_all Port %}{% if users.room.port_set.all %} / - {{ users.room.port_set.all|join:", " }} {% endif %}{% acl_end %} -
    + {{ users.room }} {% if users.room.port_set.all %}{% can_view_all Port %}/ + {{ users.room.port_set.all|join:", " }} {% acl_else %} + {% trans "Connected" %}{% acl_end %} + {% else %}{% if users.room %}{% trans "Pending connection..." %}{% endif %} + {% endif %} +
    @@ -252,7 +256,9 @@ with this program; if not, write to the Free Software Foundation, Inc., {% elif users.state == 2 %}
    {% trans "Archived" %}
    {% elif users.state == 3 %} -
    {% trans "Not yet member" %}
    +
    {% trans "Not yet active" %}
    + {% elif users.state == 4 %} +
    {% trans "Fully archived" %}
    {% endif %}
    @@ -302,23 +308,28 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    {{ users.shell }}
    {% endif %} +
    +
    {% trans "Shortcuts enabled" %}
    +
    {{ users.shortcuts_enabled | tick }}
    +
    {% if users.is_class_club %}
    -
    +

    - {% trans " Manage the club" %} + {% trans "Manage the club" %}

    - + - {% trans "Manage the admins and members" %} - + {% trans "Manage the admins and members" %} +

    {% trans "Club admins" %}

    @@ -473,14 +484,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    - {% trans " Email settings" %} + {% trans "Email settings" %}

    @@ -499,12 +510,12 @@ with this program; if not, write to the Free Software Foundation, Inc., {{ users.local_email_redirect | tick }} -

    {% trans "The contact email address is the email address where we send emails to contact you. If you would like to use your external email address for that, you can either disable your local email address or enable the local email redirection." %}

    +

    {% trans "The contact email address is the email address to which we send emails to contact you. If you would like to use your external email address for that, you can either disable your local email address or enable the local email redirection." %}

    {% if users.local_email_enabled %} {% can_create EMailAddress users.id %} - {% trans " Add an email address" %} + {% trans "Add an email address" %} {% acl_end %} {% if emailaddress_list %} @@ -524,6 +535,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    + + {% for template in optionnal_templates_list %} + {{ template }} + {% endfor %} +
    {% endblock %} diff --git a/users/templates/users/sidebar.html b/users/templates/users/sidebar.html index 7a7daae3..d2ee77ff 100644 --- a/users/templates/users/sidebar.html +++ b/users/templates/users/sidebar.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/users/templates/users/user.html b/users/templates/users/user.html index d7d4a0f0..86841c8f 100644 --- a/users/templates/users/user.html +++ b/users/templates/users/user.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify diff --git a/users/templates/users/user_autocapture.html b/users/templates/users/user_autocapture.html index 7ba0a6c3..469e2ec3 100644 --- a/users/templates/users/user_autocapture.html +++ b/users/templates/users/user_autocapture.html @@ -5,7 +5,7 @@ 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 Lara Kermarec Copyright © 2017 Augustin Lemesle This program is free software; you can redistribute it and/or modify @@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block content %} {% bootstrap_form_errors userform %} -

    {% blocktrans %}Device and room register form{% endblocktrans %}

    +

    {% trans "Device and room register form" %}

    {% csrf_token %} @@ -61,7 +61,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    -
    {% bootstrap_button _("OK") button_type="submit" icon='ok' button_class='btn-success' %}
    +{% trans "OK" as tr_ok %} +
    {% bootstrap_button tr_ok button_type="submit" icon='ok' button_class='btn-success' %}
    diff --git a/users/test_models.py b/users/test_models.py new file mode 100644 index 00000000..bb69cb79 --- /dev/null +++ b/users/test_models.py @@ -0,0 +1,44 @@ +from django.test import TestCase + +import datetime +from django.utils import timezone + +from users.models import User +from cotisations.models import Vente, Facture, Paiement + + +class UserModelTests(TestCase): + def setUp(self): + self.user = User.objects.create(pseudo="testUser") + + def tearDown(self): + self.user.facture_set.all().delete() + self.user.delete() + + def test_multiple_cotisations_are_taken_into_account(self): + paiement = Paiement.objects.create(moyen="test payment") + invoice = Facture.objects.create(user=self.user, paiement=paiement, valid=True) + date = timezone.now() + purchase1 = Vente.objects.create( + facture=invoice, + number=1, + name="Test purchase", + duration=0, + duration_days=1, + type_cotisation="All", + prix=0, + ) + purchase2 = Vente.objects.create( + facture=invoice, + number=1, + name="Test purchase", + duration=0, + duration_days=1, + type_cotisation="All", + prix=0, + ) + self.assertAlmostEqual( + self.user.end_connexion() - date, + datetime.timedelta(days=2), + delta=datetime.timedelta(seconds=1), + ) diff --git a/users/tests.py b/users/tests.py index 6b2bfb41..f0ae8d09 100644 --- a/users/tests.py +++ b/users/tests.py @@ -3,7 +3,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -62,26 +62,23 @@ class LdapUserTestCase(TestCase): user_password="{SSHA}aBcDeFgHiJkLmNoPqRsTuVwXyZ012345", sambat_nt_password="0123456789ABCDEF0123456789ABCDEF", macs=[], - shadowexpire="0" + shadowexpire="0", ) - self.assertEqual(g.name, 'users_test_ldapuser') + self.assertEqual(g.name, "users_test_ldapuser") class LdapUserGroupTestCase(TestCase): def test_create_ldap_user_group(self): g = models.LdapUserGroup.objects.create( - gid="501", - members=[], - name="users_test_ldapusergroup" + gid="501", members=[], name="users_test_ldapusergroup" ) - self.assertEqual(g.name, 'users_test_ldapusergroup') + self.assertEqual(g.name, "users_test_ldapusergroup") class LdapServiceUserTestCase(TestCase): def test_create_ldap_service_user(self): g = models.LdapServiceUser.objects.create( name="users_test_ldapserviceuser", - user_password="{SSHA}AbCdEfGhIjKlMnOpQrStUvWxYz987654" + user_password="{SSHA}AbCdEfGhIjKlMnOpQrStUvWxYz987654", ) - self.assertEqual(g.name, 'users_test_ldapserviceuser') - + self.assertEqual(g.name, "users_test_ldapserviceuser") diff --git a/users/urls.py b/users/urls.py index 1e6ffa8c..1cd303e6 100644 --- a/users/urls.py +++ b/users/urls.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -31,98 +31,114 @@ from django.conf.urls import url from . import views urlpatterns = [ - url(r'^new_user/$', views.new_user, name='new-user'), - url(r'^new_club/$', views.new_club, name='new-club'), - url(r'^edit_info/(?P[0-9]+)$', views.edit_info, name='edit-info'), - url(r'^edit_club_admin_members/(?P[0-9]+)$', + url(r"^new_user/$", views.new_user, name="new-user"), + url(r"^new_club/$", views.new_club, name="new-club"), + url(r"^edit_info/(?P[0-9]+)$", views.edit_info, name="edit-info"), + url( + r"^edit_club_admin_members/(?P[0-9]+)$", views.edit_club_admin_members, - name='edit-club-admin-members'), - url(r'^state/(?P[0-9]+)$', views.state, name='state'), - url(r'^groups/(?P[0-9]+)$', views.groups, name='groups'), - url(r'^password/(?P[0-9]+)$', views.password, name='password'), - url(r'^del_group/(?P[0-9]+)/(?P[0-9]+)$', + name="edit-club-admin-members", + ), + url(r"^state/(?P[0-9]+)$", views.state, name="state"), + url(r"^groups/(?P[0-9]+)$", views.groups, name="groups"), + url(r"^password/(?P[0-9]+)$", views.password, name="password"), + url( + r"^del_group/(?P[0-9]+)/(?P[0-9]+)$", views.del_group, - name='del-group'), - url(r'^del_superuser/(?P[0-9]+)$', - views.del_superuser, - name='del-superuser'), - url(r'^new_serviceuser/$', views.new_serviceuser, name='new-serviceuser'), - url(r'^edit_serviceuser/(?P[0-9]+)$', + name="del-group", + ), + url( + r"^del_superuser/(?P[0-9]+)$", views.del_superuser, name="del-superuser" + ), + url(r"^new_serviceuser/$", views.new_serviceuser, name="new-serviceuser"), + url( + r"^edit_serviceuser/(?P[0-9]+)$", views.edit_serviceuser, - name='edit-serviceuser'), - url(r'^del_serviceuser/(?P[0-9]+)$', + name="edit-serviceuser", + ), + url( + r"^del_serviceuser/(?P[0-9]+)$", views.del_serviceuser, - name='del-serviceuser'), - url(r'^add_ban/(?P[0-9]+)$', views.add_ban, name='add-ban'), - url(r'^edit_ban/(?P[0-9]+)$', views.edit_ban, name='edit-ban'), - url(r'^del-ban/(?P[0-9]+)$', views.del_ban, name='del-ban'), - url(r'^add_whitelist/(?P[0-9]+)$', - views.add_whitelist, - name='add-whitelist'), - url(r'^edit_whitelist/(?P[0-9]+)$', + name="del-serviceuser", + ), + url(r"^add_ban/(?P[0-9]+)$", views.add_ban, name="add-ban"), + url(r"^edit_ban/(?P[0-9]+)$", views.edit_ban, name="edit-ban"), + url(r"^del-ban/(?P[0-9]+)$", views.del_ban, name="del-ban"), + url( + r"^add_whitelist/(?P[0-9]+)$", views.add_whitelist, name="add-whitelist" + ), + url( + r"^edit_whitelist/(?P[0-9]+)$", views.edit_whitelist, - name='edit-whitelist'), - url(r'^del_whitelist/(?P[0-9]+)$', + name="edit-whitelist", + ), + url( + r"^del_whitelist/(?P[0-9]+)$", views.del_whitelist, - name='del-whitelist'), - url(r'^add_emailaddress/(?P[0-9]+)$', + name="del-whitelist", + ), + url( + r"^add_emailaddress/(?P[0-9]+)$", views.add_emailaddress, - name='add-emailaddress'), - url(r'^edit_emailaddress/(?P[0-9]+)$', + name="add-emailaddress", + ), + url( + r"^edit_emailaddress/(?P[0-9]+)$", views.edit_emailaddress, - name='edit-emailaddress'), - url(r'^del_emailaddress/(?P[0-9]+)$', + name="edit-emailaddress", + ), + url( + r"^del_emailaddress/(?P[0-9]+)$", views.del_emailaddress, - name='del-emailaddress'), - url(r'^edit_email_settings/(?P[0-9]+)$', + name="del-emailaddress", + ), + url( + r"^edit_email_settings/(?P[0-9]+)$", views.edit_email_settings, - name='edit-email-settings'), - url(r'^add_school/$', views.add_school, name='add-school'), - url(r'^edit_school/(?P[0-9]+)$', - views.edit_school, - name='edit-school'), - url(r'^del_school/$', views.del_school, name='del-school'), - url(r'^add_listright/$', views.add_listright, name='add-listright'), - url(r'^edit_listright/(?P[0-9]+)$', + name="edit-email-settings", + ), + url(r"^add_school/$", views.add_school, name="add-school"), + url(r"^edit_school/(?P[0-9]+)$", views.edit_school, name="edit-school"), + url(r"^del_school/$", views.del_school, name="del-school"), + url(r"^add_listright/$", views.add_listright, name="add-listright"), + url( + r"^edit_listright/(?P[0-9]+)$", views.edit_listright, - name='edit-listright'), - url(r'^del_listright/$', views.del_listright, name='del-listright'), - url(r'^add_shell/$', views.add_shell, name='add-shell'), - url(r'^edit_shell/(?P[0-9]+)$', - views.edit_shell, - name='edit-shell'), - url(r'^del_shell/(?P[0-9]+)$', - views.del_shell, - name='del-shell'), - url(r'^profil/(?P[0-9]+)$', views.profil, name='profil'), - url(r'^index_ban/$', views.index_ban, name='index-ban'), - url(r'^index_white/$', views.index_white, name='index-white'), - url(r'^index_school/$', views.index_school, name='index-school'), - url(r'^index_shell/$', views.index_shell, name='index-shell'), - url(r'^index_listright/$', views.index_listright, name='index-listright'), - url(r'^index_serviceusers/$', - views.index_serviceusers, - name='index-serviceusers'), - url(r'^mon_profil/$', views.mon_profil, name='mon-profil'), - url(r'^process/(?P[a-z0-9]{32})/$', views.process, name='process'), - url(r'^reset_password/$', views.reset_password, name='reset-password'), - url(r'^mass_archive/$', views.mass_archive, name='mass-archive'), - url(r'^$', views.index, name='index'), - url(r'^index_clubs/$', views.index_clubs, name='index-clubs'), - url(r'^initial_register/$', views.initial_register, name='initial-register'), - url(r'^rest/ml/std/$', - views.ml_std_list, - name='ml-std-list'), - url(r'^rest/ml/std/member/(?P\w+)/$', + name="edit-listright", + ), + url(r"^del_listright/$", views.del_listright, name="del-listright"), + url(r"^add_shell/$", views.add_shell, name="add-shell"), + url(r"^edit_shell/(?P[0-9]+)$", views.edit_shell, name="edit-shell"), + url(r"^del_shell/(?P[0-9]+)$", views.del_shell, name="del-shell"), + url(r"^profil/(?P[0-9]+)$", views.profil, name="profil"), + url(r"^index_ban/$", views.index_ban, name="index-ban"), + url(r"^index_white/$", views.index_white, name="index-white"), + url(r"^index_school/$", views.index_school, name="index-school"), + url(r"^index_shell/$", views.index_shell, name="index-shell"), + url(r"^index_listright/$", views.index_listright, name="index-listright"), + url(r"^index_serviceusers/$", views.index_serviceusers, name="index-serviceusers"), + url(r"^mon_profil/$", views.mon_profil, name="mon-profil"), + url(r"^process/(?P[a-z0-9]{32})/$", views.process, name="process"), + url(r"^reset_password/$", views.reset_password, name="reset-password"), + url(r"^mass_archive/$", views.mass_archive, name="mass-archive"), + url(r"^$", views.index, name="index"), + url(r"^index_clubs/$", views.index_clubs, name="index-clubs"), + url(r"^initial_register/$", views.initial_register, name="initial-register"), + url(r"^rest/ml/std/$", views.ml_std_list, name="ml-std-list"), + url( + r"^rest/ml/std/member/(?P\w+)/$", views.ml_std_members, - name='ml-std-members'), - url(r'^rest/ml/club/$', - views.ml_club_list, - name='ml-club-list'), - url(r'^rest/ml/club/admin/(?P\w+)/$', + name="ml-std-members", + ), + url(r"^rest/ml/club/$", views.ml_club_list, name="ml-club-list"), + url( + r"^rest/ml/club/admin/(?P\w+)/$", views.ml_club_admins, - name='ml-club-admins'), - url(r'^rest/ml/club/member/(?P\w+)/$', + name="ml-club-admins", + ), + url( + r"^rest/ml/club/member/(?P\w+)/$", views.ml_club_members, - name='ml-club-members'), + name="ml-club-members", + ), ] diff --git a/users/views.py b/users/views.py index ea22359a..3fb4d472 100644 --- a/users/views.py +++ b/users/views.py @@ -4,7 +4,7 @@ # quelques clics. # # Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle # # This program is free software; you can redistribute it and/or modify @@ -22,7 +22,7 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # App de gestion des users pour re2o -# Goulven Kermarec, Gabriel Détraz, Lemesle Augustin +# Lara Kermarec, Gabriel Détraz, Lemesle Augustin # Gplv2 """ Module des views. @@ -47,21 +47,20 @@ from django.http import HttpResponse from django.http import HttpResponseRedirect from django.views.decorators.csrf import csrf_exempt from django.utils.translation import ugettext as _ +from django.template import loader from rest_framework.renderers import JSONRenderer from reversion import revisions as reversion from cotisations.models import Facture, Paiement from machines.models import Machine + from preferences.models import OptionalUser, GeneralOption, AssoOption +from importlib import import_module +from re2o.settings_local import OPTIONNAL_APPS_RE2O from re2o.views import form -from re2o.utils import ( - all_has_access, -) -from re2o.base import ( - re2o_paginator, - SortTable -) +from re2o.utils import all_has_access +from re2o.base import re2o_paginator, SortTable from re2o.acl import ( can_create, can_edit, @@ -69,7 +68,7 @@ from re2o.acl import ( can_delete, can_view, can_view_all, - can_change + can_change, ) from cotisations.utils import find_payment_method from topologie.models import Port @@ -109,7 +108,7 @@ from .forms import ( ResetPasswordForm, ClubAdminandMembersForm, GroupForm, - InitialRegisterForm + InitialRegisterForm, ) @@ -118,27 +117,27 @@ def new_user(request): """ Vue de création d'un nouvel utilisateur, envoie un mail pour le mot de passe""" user = AdherentCreationForm(request.POST or None, user=request.user) - GTU_sum_up = GeneralOption.get_cached_value('GTU_sum_up') - GTU = GeneralOption.get_cached_value('GTU') + GTU_sum_up = GeneralOption.get_cached_value("GTU_sum_up") + GTU = GeneralOption.get_cached_value("GTU") if user.is_valid(): user = user.save() user.reset_passwd_mail(request) - messages.success(request, _("The user %s was created, an email to set" - " the password was sent.") % user.pseudo) - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(user.id)} - )) + messages.success( + request, + _("The user %s was created, an email to set the password was sent.") + % user.pseudo, + ) + return redirect(reverse("users:profil", kwargs={"userid": str(user.id)})) return form( { - 'userform': user, - 'GTU_sum_up': GTU_sum_up, - 'GTU': GTU, - 'showCGU': True, - 'action_name': _("Commit") + "userform": user, + "GTU_sum_up": GTU_sum_up, + "GTU": GTU, + "showCGU": True, + "action_name": _("Commit"), }, - 'users/user.html', - request + "users/user.html", + request, ) @@ -152,16 +151,16 @@ def new_club(request): club = club.save(commit=False) club.save() club.reset_passwd_mail(request) - messages.success(request, _("The club %s was created, an email to set" - " the password was sent.") % club.pseudo) - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(club.id)} - )) + messages.success( + request, + _("The club %s was created, an email to set the password was sent.") + % club.pseudo, + ) + return redirect(reverse("users:profil", kwargs={"userid": str(club.id)})) return form( - {'userform': club, 'showCGU': False, 'action_name': _("Create a club")}, - 'users/user.html', - request + {"userform": club, "showCGU": False, "action_name": _("Create a club")}, + "users/user.html", + request, ) @@ -170,26 +169,22 @@ def new_club(request): def edit_club_admin_members(request, club_instance, **_kwargs): """Vue d'edition de la liste des users administrateurs et membres d'un club""" - club = ClubAdminandMembersForm( - request.POST or None, - instance=club_instance - ) + club = ClubAdminandMembersForm(request.POST or None, instance=club_instance) if club.is_valid(): if club.changed_data: club.save() messages.success(request, _("The club was edited.")) - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(club_instance.id)} - )) + return redirect( + reverse("users:profil", kwargs={"userid": str(club_instance.id)}) + ) return form( { - 'userform': club, - 'showCGU': False, - 'action_name': _("Edit the admins and members") + "userform": club, + "showCGU": False, + "action_name": _("Edit"), }, - 'users/user.html', - request + "users/user.html", + request, ) @@ -201,33 +196,26 @@ def edit_info(request, user, userid): possession du droit cableur """ if user.is_class_adherent: user_form = AdherentEditForm( - request.POST or None, - instance=user.adherent, - user=request.user + request.POST or None, instance=user.adherent, user=request.user ) else: user_form = ClubForm( - request.POST or None, - instance=user.club, - user=request.user + request.POST or None, instance=user.club, user=request.user ) if user_form.is_valid(): if user_form.changed_data: user_form.save() messages.success(request, _("The user was edited.")) - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(userid)} - )) + return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) return form( - {'userform': user_form, 'action_name': _("Edit the user")}, - 'users/user.html', - request + {"userform": user_form, "action_name": _("Edit")}, + "users/user.html", + request, ) @login_required -@can_edit(User, 'state') +@can_edit(User, "state") def state(request, user, userid): """ Change the state (active/unactive/archived) of a user""" state_form = StateForm(request.POST or None, instance=user) @@ -235,40 +223,33 @@ def state(request, user, userid): if state_form.changed_data: state_form.save() messages.success(request, _("The state was edited.")) - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(userid)} - )) + return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) return form( - {'userform': state_form, 'action_name': _("Edit the state")}, - 'users/user.html', - request + {"userform": state_form, "action_name": _("Edit")}, + "users/user.html", + request, ) @login_required -@can_edit(User, 'groups') +@can_edit(User, "groups") def groups(request, user, userid): """ View to edit the groups of a user """ - group_form = GroupForm(request.POST or None, - instance=user, user=request.user) + group_form = GroupForm(request.POST or None, instance=user, user=request.user) if group_form.is_valid(): if group_form.changed_data: group_form.save() messages.success(request, _("The groups were edited.")) - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(userid)} - )) + return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) return form( - {'userform': group_form, 'action_name': _("Edit the groups")}, - 'users/user.html', - request + {"userform": group_form, "action_name": _("Edit")}, + "users/user.html", + request, ) @login_required -@can_edit(User, 'password') +@can_edit(User, "password") def password(request, user, userid): """ Reinitialisation d'un mot de passe à partir de l'userid, pour self par défaut, pour tous sans droit si droit cableur, @@ -278,35 +259,32 @@ def password(request, user, userid): if u_form.changed_data: u_form.save() messages.success(request, _("The password was changed.")) - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(userid)} - )) + return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) return form( - {'userform': u_form, 'action_name': _("Change the password")}, - 'users/user.html', - request + {"userform": u_form, "action_name": _("Change the password")}, + "users/user.html", + request, ) @login_required -@can_edit(User, 'groups') +@can_edit(User, "groups") def del_group(request, user, listrightid, **_kwargs): """ View used to delete a group """ user.groups.remove(ListRight.objects.get(id=listrightid)) user.save() messages.success(request, _("%s was removed from the group.") % user) - return HttpResponseRedirect(request.META.get('HTTP_REFERER')) + return HttpResponseRedirect(request.META.get("HTTP_REFERER")) @login_required -@can_edit(User, 'is_superuser') +@can_edit(User, "is_superuser") def del_superuser(request, user, **_kwargs): """Remove the superuser right of an user.""" user.is_superuser = False user.save() messages.success(request, _("%s is no longer superuser.") % user) - return HttpResponseRedirect(request.META.get('HTTP_REFERER')) + return HttpResponseRedirect(request.META.get("HTTP_REFERER")) @login_required @@ -316,15 +294,12 @@ def new_serviceuser(request): user = ServiceUserForm(request.POST or None) if user.is_valid(): user.save() - messages.success( - request, - _("The service user was created.") - ) - return redirect(reverse('users:index-serviceusers')) + messages.success(request, _("The service user was created.")) + return redirect(reverse("users:index-serviceusers")) return form( - {'userform': user, 'action_name': _("Create a service user")}, - 'users/user.html', - request + {"userform": user, "action_name": _("Add")}, + "users/user.html", + request, ) @@ -332,19 +307,16 @@ def new_serviceuser(request): @can_edit(ServiceUser) def edit_serviceuser(request, serviceuser, **_kwargs): """ Edit a ServiceUser """ - serviceuser = EditServiceUserForm( - request.POST or None, - instance=serviceuser - ) + serviceuser = EditServiceUserForm(request.POST or None, instance=serviceuser) if serviceuser.is_valid(): if serviceuser.changed_data: serviceuser.save() messages.success(request, _("The service user was edited.")) - return redirect(reverse('users:index-serviceusers')) + return redirect(reverse("users:index-serviceusers")) return form( - {'userform': serviceuser, 'action_name': _("Edit a service user")}, - 'users/user.html', - request + {"userform": serviceuser, "action_name": _("Edit")}, + "users/user.html", + request, ) @@ -355,11 +327,11 @@ def del_serviceuser(request, serviceuser, **_kwargs): if request.method == "POST": serviceuser.delete() messages.success(request, _("The service user was deleted.")) - return redirect(reverse('users:index-serviceusers')) + return redirect(reverse("users:index-serviceusers")) return form( - {'objet': serviceuser, 'objet_name': 'service user'}, - 'users/delete.html', - request + {"objet": serviceuser, "objet_name": _("service user")}, + "users/delete.html", + request, ) @@ -375,19 +347,11 @@ def add_ban(request, user, userid): if ban.is_valid(): ban.save() messages.success(request, _("The ban was added.")) - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(userid)} - )) + return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) if user.is_ban(): - messages.error( - request, - _("Warning: this user already has an active ban.") - ) + messages.error(request, _("Warning: this user already has an active ban.")) return form( - {'userform': ban, 'action_name': _("Add a ban")}, - 'users/user.html', - request + {"userform": ban, "action_name": _("Add")}, "users/user.html", request ) @@ -402,11 +366,9 @@ def edit_ban(request, ban_instance, **_kwargs): if ban.changed_data: ban.save() messages.success(request, _("The ban was edited.")) - return redirect(reverse('users:index')) + return redirect(reverse("users:index")) return form( - {'userform': ban, 'action_name': _("Edit a ban")}, - 'users/user.html', - request + {"userform": ban, "action_name": _("Edit")}, "users/user.html", request ) @@ -417,15 +379,8 @@ def del_ban(request, ban, **_kwargs): if request.method == "POST": ban.delete() messages.success(request, _("The ban was deleted.")) - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(ban.user.id)} - )) - return form( - {'objet': ban, 'objet_name': 'ban'}, - 'users/delete.html', - request - ) + return redirect(reverse("users:profil", kwargs={"userid": str(ban.user.id)})) + return form({"objet": ban, "objet_name": _("ban")}, "users/delete.html", request) @login_required @@ -437,26 +392,19 @@ def add_whitelist(request, user, userid): Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement, raison obligatoire""" whitelist_instance = Whitelist(user=user) - whitelist = WhitelistForm( - request.POST or None, - instance=whitelist_instance - ) + whitelist = WhitelistForm(request.POST or None, instance=whitelist_instance) if whitelist.is_valid(): whitelist.save() messages.success(request, _("The whitelist was added.")) - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(userid)} - )) + return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) if user.is_whitelisted(): messages.error( - request, - _("Warning: this user already has an active whitelist.") + request, _("Warning: this user already has an active whitelist.") ) return form( - {'userform': whitelist, 'action_name': _("Add a whitelist")}, - 'users/user.html', - request + {"userform": whitelist, "action_name": _("Add")}, + "users/user.html", + request, ) @@ -467,19 +415,16 @@ def edit_whitelist(request, whitelist_instance, **_kwargs): Need droit cableur Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement, raison obligatoire""" - whitelist = WhitelistForm( - request.POST or None, - instance=whitelist_instance - ) + whitelist = WhitelistForm(request.POST or None, instance=whitelist_instance) if whitelist.is_valid(): if whitelist.changed_data: whitelist.save() messages.success(request, _("The whitelist was edited.")) - return redirect(reverse('users:index')) + return redirect(reverse("users:index")) return form( - {'userform': whitelist, 'action_name': _("Edit a whitelist")}, - 'users/user.html', - request + {"userform": whitelist, "action_name": _("Edit")}, + "users/user.html", + request, ) @@ -490,14 +435,11 @@ def del_whitelist(request, whitelist, **_kwargs): if request.method == "POST": whitelist.delete() messages.success(request, _("The whitelist was deleted.")) - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(whitelist.user.id)} - )) + return redirect( + reverse("users:profil", kwargs={"userid": str(whitelist.user.id)}) + ) return form( - {'objet': whitelist, 'objet_name': 'whitelist'}, - 'users/delete.html', - request + {"objet": whitelist, "objet_name": _("whitelist")}, "users/delete.html", request ) @@ -508,22 +450,20 @@ def add_emailaddress(request, user, userid): """ Create a new local email account""" emailaddress_instance = EMailAddress(user=user) emailaddress = EMailAddressForm( - request.POST or None, - instance=emailaddress_instance + request.POST or None, instance=emailaddress_instance ) if emailaddress.is_valid(): emailaddress.save() messages.success(request, _("The local email account was created.")) - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(userid)} - )) + return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) return form( - {'userform': emailaddress, - 'showCGU': False, - 'action_name': _("Add a local email account")}, - 'users/user.html', - request + { + "userform": emailaddress, + "showCGU": False, + "action_name": _("Add"), + }, + "users/user.html", + request, ) @@ -532,23 +472,25 @@ def add_emailaddress(request, user, userid): def edit_emailaddress(request, emailaddress_instance, **_kwargs): """ Edit a local email account""" emailaddress = EMailAddressForm( - request.POST or None, - instance=emailaddress_instance + request.POST or None, instance=emailaddress_instance ) if emailaddress.is_valid(): if emailaddress.changed_data: emailaddress.save() messages.success(request, _("The local email account was edited.")) - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(emailaddress_instance.user.id)} - )) + return redirect( + reverse( + "users:profil", kwargs={"userid": str(emailaddress_instance.user.id)} + ) + ) return form( - {'userform': emailaddress, - 'showCGU': False, - 'action_name': _("Edit a local email account")}, - 'users/user.html', - request + { + "userform": emailaddress, + "showCGU": False, + "action_name": _("Edit"), + }, + "users/user.html", + request, ) @@ -559,14 +501,13 @@ def del_emailaddress(request, emailaddress, **_kwargs): if request.method == "POST": emailaddress.delete() messages.success(request, _("The local email account was deleted.")) - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(emailaddress.user.id)} - )) + return redirect( + reverse("users:profil", kwargs={"userid": str(emailaddress.user.id)}) + ) return form( - {'objet': emailaddress, 'objet_name': 'emailaddress'}, - 'users/delete.html', - request + {"objet": emailaddress, "objet_name": _("email address")}, + "users/delete.html", + request, ) @@ -575,25 +516,24 @@ def del_emailaddress(request, emailaddress, **_kwargs): def edit_email_settings(request, user_instance, **_kwargs): """Edit the email settings of a user""" email_settings = EmailSettingsForm( - request.POST or None, - instance=user_instance, - user=request.user + request.POST or None, instance=user_instance, user=request.user ) if email_settings.is_valid(): if email_settings.changed_data: email_settings.save() messages.success(request, _("The email settings were edited.")) - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(user_instance.id)} - )) + return redirect( + reverse("users:profil", kwargs={"userid": str(user_instance.id)}) + ) return form( - {'userform': email_settings, - 'showCGU': False, - 'load_js_file': '/static/js/email_address.js', - 'action_name': _("Edit the email settings")}, - 'users/user.html', - request + { + "userform": email_settings, + "showCGU": False, + "load_js_file": "/static/js/email_address.js", + "action_name": _("Edit"), + }, + "users/user.html", + request, ) @@ -606,11 +546,11 @@ def add_school(request): if school.is_valid(): school.save() messages.success(request, _("The school was added.")) - return redirect(reverse('users:index-school')) + return redirect(reverse("users:index-school")) return form( - {'userform': school, 'action_name': _("Add a school")}, - 'users/user.html', - request + {"userform": school, "action_name": _("Add")}, + "users/user.html", + request, ) @@ -624,11 +564,11 @@ def edit_school(request, school_instance, **_kwargs): if school.changed_data: school.save() messages.success(request, _("The school was edited.")) - return redirect(reverse('users:index-school')) + return redirect(reverse("users:index-school")) return form( - {'userform': school, 'action_name': _("Edit a school")}, - 'users/user.html', - request + {"userform": school, "action_name": _("Edit")}, + "users/user.html", + request, ) @@ -641,7 +581,7 @@ def del_school(request, instances): l'établissement """ school = DelSchoolForm(request.POST or None, instances=instances) if school.is_valid(): - school_dels = school.cleaned_data['schools'] + school_dels = school.cleaned_data["schools"] for school_del in school_dels: try: school_del.delete() @@ -649,13 +589,15 @@ def del_school(request, instances): except ProtectedError: messages.error( request, - _("The school %s is assigned to at least one user," - " impossible to delete it.") % school_del) - return redirect(reverse('users:index-school')) + _( + "The school %s is assigned to at least one user," + " impossible to delete it." + ) + % school_del, + ) + return redirect(reverse("users:index-school")) return form( - {'userform': school, 'action_name': _("Delete")}, - 'users/user.html', - request + {"userform": school, "action_name": _("Confirm")}, "users/user.html", request ) @@ -667,11 +609,9 @@ def add_shell(request): if shell.is_valid(): shell.save() messages.success(request, _("The shell was added.")) - return redirect(reverse('users:index-shell')) + return redirect(reverse("users:index-shell")) return form( - {'userform': shell, 'action_name': _("Add a shell")}, - 'users/user.html', - request + {"userform": shell, "action_name": _("Add")}, "users/user.html", request ) @@ -684,11 +624,11 @@ def edit_shell(request, shell_instance, **_kwargs): if shell.changed_data: shell.save() messages.success(request, _("The shell was edited.")) - return redirect(reverse('users:index-shell')) + return redirect(reverse("users:index-shell")) return form( - {'userform': shell, 'action_name': _("Edit a shell")}, - 'users/user.html', - request + {"userform": shell, "action_name": _("Edit")}, + "users/user.html", + request, ) @@ -699,12 +639,8 @@ def del_shell(request, shell, **_kwargs): if request.method == "POST": shell.delete() messages.success(request, _("The shell was deleted.")) - return redirect(reverse('users:index-shell')) - return form( - {'objet': shell, 'objet_name': 'shell'}, - 'users/delete.html', - request - ) + return redirect(reverse("users:index-shell")) + return form({"objet": shell, "objet_name": _("shell")}, "users/delete.html", request) @login_required @@ -716,11 +652,11 @@ def add_listright(request): if listright.is_valid(): listright.save() messages.success(request, _("The group of rights was added.")) - return redirect(reverse('users:index-listright')) + return redirect(reverse("users:index-listright")) return form( - {'userform': listright, 'action_name': _("Add a group of rights")}, - 'users/user.html', - request + {"userform": listright, "action_name": _("Add")}, + "users/user.html", + request, ) @@ -729,19 +665,16 @@ def add_listright(request): def edit_listright(request, listright_instance, **_kwargs): """ Editer un groupe/droit, necessite droit bureau, à partir du listright id """ - listright = ListRightForm( - request.POST or None, - instance=listright_instance - ) + listright = ListRightForm(request.POST or None, instance=listright_instance) if listright.is_valid(): if listright.changed_data: listright.save() messages.success(request, _("The group of rights was edited.")) - return redirect(reverse('users:index-listright')) + return redirect(reverse("users:index-listright")) return form( - {'userform': listright, 'action_name': _("Edit a group of rights")}, - 'users/user.html', - request + {"userform": listright, "action_name": _("Edit")}, + "users/user.html", + request, ) @@ -752,52 +685,59 @@ def del_listright(request, instances): bureau """ listright = DelListRightForm(request.POST or None, instances=instances) if listright.is_valid(): - listright_dels = listright.cleaned_data['listrights'] + listright_dels = listright.cleaned_data["listrights"] for listright_del in listright_dels: try: listright_del.delete() - messages.success(request, _("The group of rights was" - " deleted.")) + messages.success(request, _("The group of rights was deleted.")) except ProtectedError: messages.error( request, - _("The group of rights %s is assigned to at least one" - " user, impossible to delete it.") % listright_del) - return redirect(reverse('users:index-listright')) + _( + "The group of rights %s is assigned to at least one" + " user, impossible to delete it." + ) + % listright_del, + ) + return redirect(reverse("users:index-listright")) return form( - {'userform': listright, 'action_name': _("Delete")}, - 'users/user.html', - request + {"userform": listright, "action_name": _("Confirm")}, "users/user.html", request ) @login_required @can_view_all(User) -@can_change(User, 'state') +@can_change(User, "state") def mass_archive(request): """ Permet l'archivage massif""" - to_archive_date = MassArchiveForm(request.POST or None) + pagination_number = GeneralOption.get_cached_value("pagination_number") + to_archive_form = MassArchiveForm(request.POST or None) to_archive_list = [] - if to_archive_date.is_valid(): - date = to_archive_date.cleaned_data['date'] - to_archive_list = [user for user in - User.objects.exclude(state=User.STATE_ARCHIVE) - if not user.end_access() - or user.end_access() < date] + if to_archive_form.is_valid(): + date = to_archive_form.cleaned_data["date"] + full_archive = to_archive_form.cleaned_data["full_archive"] + to_archive_list = ( + User.objects.exclude(id__in=all_has_access()) + .exclude(id__in=all_has_access(search_time=date)) + .exclude(state=User.STATE_NOT_YET_ACTIVE) + .exclude(state=User.STATE_FULL_ARCHIVE) + ) + if not full_archive: + to_archive_list = to_archive_list.exclude(state=User.STATE_ARCHIVE) if "valider" in request.POST: - for user in to_archive_list: - with transaction.atomic(), reversion.create_revision(): - user.archive() - user.save() - reversion.set_comment(_("Archiving")) - messages.success(request, _("%s users were archived.") % len( - to_archive_list - )) - return redirect(reverse('users:index')) + if full_archive: + User.mass_full_archive(to_archive_list) + else: + User.mass_archive(to_archive_list) + messages.success( + request, _("%s users were archived.") % to_archive_list.count() + ) + return redirect(reverse("users:index")) + to_archive_list = re2o_paginator(request, to_archive_list, pagination_number) return form( - {'userform': to_archive_date, 'to_archive_list': to_archive_list}, - 'users/mass_archive.html', - request + {"userform": to_archive_form, "to_archive_list": to_archive_list}, + "users/mass_archive.html", + request, ) @@ -805,104 +745,88 @@ def mass_archive(request): @can_view_all(Adherent) def index(request): """ Affiche l'ensemble des adherents, need droit cableur """ - pagination_number = GeneralOption.get_cached_value('pagination_number') - users_list = Adherent.objects.select_related('room') + pagination_number = GeneralOption.get_cached_value("pagination_number") + users_list = Adherent.objects.select_related("room") users_list = SortTable.sort( users_list, - request.GET.get('col'), - request.GET.get('order'), - SortTable.USERS_INDEX + request.GET.get("col"), + request.GET.get("order"), + SortTable.USERS_INDEX, ) users_list = re2o_paginator(request, users_list, pagination_number) - return render(request, 'users/index.html', {'users_list': users_list}) + return render(request, "users/index.html", {"users_list": users_list}) @login_required @can_view_all(Club) def index_clubs(request): """ Affiche l'ensemble des clubs, need droit cableur """ - pagination_number = GeneralOption.get_cached_value('pagination_number') - clubs_list = Club.objects.select_related('room') + pagination_number = GeneralOption.get_cached_value("pagination_number") + clubs_list = Club.objects.select_related("room") clubs_list = SortTable.sort( clubs_list, - request.GET.get('col'), - request.GET.get('order'), - SortTable.USERS_INDEX + request.GET.get("col"), + request.GET.get("order"), + SortTable.USERS_INDEX, ) clubs_list = re2o_paginator(request, clubs_list, pagination_number) - return render( - request, - 'users/index_clubs.html', - {'clubs_list': clubs_list} - ) + return render(request, "users/index_clubs.html", {"clubs_list": clubs_list}) @login_required @can_view_all(Ban) def index_ban(request): """ Affiche l'ensemble des ban, need droit cableur """ - pagination_number = GeneralOption.get_cached_value('pagination_number') - ban_list = Ban.objects.select_related('user') + pagination_number = GeneralOption.get_cached_value("pagination_number") + ban_list = Ban.objects.select_related("user") ban_list = SortTable.sort( ban_list, - request.GET.get('col'), - request.GET.get('order'), - SortTable.USERS_INDEX_BAN + request.GET.get("col"), + request.GET.get("order"), + SortTable.USERS_INDEX_BAN, ) ban_list = re2o_paginator(request, ban_list, pagination_number) - return render(request, 'users/index_ban.html', {'ban_list': ban_list}) + return render(request, "users/index_ban.html", {"ban_list": ban_list}) @login_required @can_view_all(Whitelist) def index_white(request): """ Affiche l'ensemble des whitelist, need droit cableur """ - pagination_number = GeneralOption.get_cached_value('pagination_number') - white_list = Whitelist.objects.select_related('user') + pagination_number = GeneralOption.get_cached_value("pagination_number") + white_list = Whitelist.objects.select_related("user") white_list = SortTable.sort( white_list, - request.GET.get('col'), - request.GET.get('order'), - SortTable.USERS_INDEX_BAN + request.GET.get("col"), + request.GET.get("order"), + SortTable.USERS_INDEX_BAN, ) white_list = re2o_paginator(request, white_list, pagination_number) - return render( - request, - 'users/index_whitelist.html', - {'white_list': white_list} - ) + return render(request, "users/index_whitelist.html", {"white_list": white_list}) @login_required @can_view_all(School) def index_school(request): """ Affiche l'ensemble des établissement""" - school_list = School.objects.order_by('name') - pagination_number = GeneralOption.get_cached_value('pagination_number') + school_list = School.objects.order_by("name") + pagination_number = GeneralOption.get_cached_value("pagination_number") school_list = SortTable.sort( school_list, - request.GET.get('col'), - request.GET.get('order'), - SortTable.USERS_INDEX_SCHOOL + request.GET.get("col"), + request.GET.get("order"), + SortTable.USERS_INDEX_SCHOOL, ) school_list = re2o_paginator(request, school_list, pagination_number) - return render( - request, - 'users/index_schools.html', - {'school_list': school_list} - ) + return render(request, "users/index_schools.html", {"school_list": school_list}) @login_required @can_view_all(ListShell) def index_shell(request): """ Affiche l'ensemble des shells""" - shell_list = ListShell.objects.order_by('shell') - return render( - request, - 'users/index_shell.html', - {'shell_list': shell_list} - ) + shell_list = ListShell.objects.order_by("shell") + return render(request, "users/index_shell.html", {"shell_list": shell_list}) @login_required @@ -910,30 +834,21 @@ def index_shell(request): def index_listright(request): """ Affiche l'ensemble des droits""" rights = {} - for right in (ListRight.objects - .order_by('name') - .prefetch_related('permissions') - .prefetch_related('user_set') - .prefetch_related('user_set__facture_set__vente_set__cotisation') - ): - rights[right] = (right.user_set - .annotate(action_number=Count('revision'), - last_seen=Max('revision__date_created'), - end_adhesion=Max('facture__vente__cotisation__date_end')) - ) - superusers = (User.objects - .filter(is_superuser=True) - .annotate(action_number=Count('revision'), - last_seen=Max('revision__date_created'), - end_adhesion=Max('facture__vente__cotisation__date_end')) - ) + for right in ( + ListRight.objects.order_by("name") + .prefetch_related("permissions") + .prefetch_related("user_set") + ): + rights[right] = right.user_set.annotate( + action_number=Count("revision"), last_seen=Max("revision__date_created") + ) + superusers = User.objects.filter(is_superuser=True).annotate( + action_number=Count("revision"), last_seen=Max("revision__date_created") + ) return render( request, - 'users/index_listright.html', - { - 'rights': rights, - 'superusers' : superusers, - } + "users/index_listright.html", + {"rights": rights, "superusers": superusers}, ) @@ -941,91 +856,95 @@ def index_listright(request): @can_view_all(ServiceUser) def index_serviceusers(request): """ Affiche les users de services (pour les accès ldap)""" - serviceusers_list = ServiceUser.objects.order_by('pseudo') + serviceusers_list = ServiceUser.objects.order_by("pseudo") return render( request, - 'users/index_serviceusers.html', - {'serviceusers_list': serviceusers_list} + "users/index_serviceusers.html", + {"serviceusers_list": serviceusers_list}, ) @login_required def mon_profil(request): """ Lien vers profil, renvoie request.id à la fonction """ - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(request.user.id)} - )) + return redirect(reverse("users:profil", kwargs={"userid": str(request.user.id)})) @login_required @can_view(User) def profil(request, users, **_kwargs): """ Affiche un profil, self or cableur, prend un userid en argument """ - machines = Machine.objects.filter(user=users).select_related('user')\ - .prefetch_related('interface_set__domain__extension')\ - .prefetch_related('interface_set__ipv4__ip_type__extension')\ - .prefetch_related('interface_set__type')\ - .prefetch_related('interface_set__domain__related_domain__extension') + machines = ( + Machine.objects.filter(user=users) + .select_related("user") + .prefetch_related("interface_set__domain__extension") + .prefetch_related("interface_set__ipv4__ip_type__extension") + .prefetch_related("interface_set__machine_type") + .prefetch_related("interface_set__domain__related_domain__extension") + ) machines = SortTable.sort( machines, - request.GET.get('col'), - request.GET.get('order'), - SortTable.MACHINES_INDEX - ) - pagination_large_number = GeneralOption.get_cached_value( - 'pagination_large_number' + request.GET.get("col"), + request.GET.get("order"), + SortTable.MACHINES_INDEX, ) + + optionnal_apps = [import_module(app) for app in OPTIONNAL_APPS_RE2O] + optionnal_templates_list = [ + app.views.profil(request, users) + for app in optionnal_apps + if hasattr(app.views, "profil") + ] + + pagination_large_number = GeneralOption.get_cached_value("pagination_large_number") nb_machines = machines.count() machines = re2o_paginator(request, machines, pagination_large_number) factures = Facture.objects.filter(user=users) factures = SortTable.sort( factures, - request.GET.get('col'), - request.GET.get('order'), - SortTable.COTISATIONS_INDEX + request.GET.get("col"), + request.GET.get("order"), + SortTable.COTISATIONS_INDEX, ) bans = Ban.objects.filter(user=users) bans = SortTable.sort( bans, - request.GET.get('col'), - request.GET.get('order'), - SortTable.USERS_INDEX_BAN + request.GET.get("col"), + request.GET.get("order"), + SortTable.USERS_INDEX_BAN, ) whitelists = Whitelist.objects.filter(user=users) whitelists = SortTable.sort( whitelists, - request.GET.get('col'), - request.GET.get('order'), - SortTable.USERS_INDEX_WHITE + request.GET.get("col"), + request.GET.get("order"), + SortTable.USERS_INDEX_WHITE, ) try: balance = find_payment_method(Paiement.objects.get(is_balance=True)) except Paiement.DoesNotExist: user_solde = False else: - user_solde = ( - balance is not None - and balance.can_credit_balance(request.user) - ) + user_solde = balance is not None and balance.can_credit_balance(request.user) return render( request, - 'users/profil.html', + "users/profil.html", { - 'users': users, - 'machines_list': machines, - 'nb_machines': nb_machines, - 'facture_list': factures, - 'ban_list': bans, - 'white_list': whitelists, - 'user_solde': user_solde, - 'solde_activated': Paiement.objects.filter(is_balance=True).exists(), - 'asso_name': AssoOption.objects.first().name, - 'emailaddress_list': users.email_address, - 'local_email_accounts_enabled': ( + "users": users, + "machines_list": machines, + "nb_machines": nb_machines, + "optionnal_templates_list": optionnal_templates_list, + "facture_list": factures, + "ban_list": bans, + "white_list": whitelists, + "user_solde": user_solde, + "solde_activated": Paiement.objects.filter(is_balance=True).exists(), + "asso_name": AssoOption.objects.first().name, + "emailaddress_list": users.email_address, + "local_email_accounts_enabled": ( OptionalUser.objects.first().local_email_accounts_enabled - ) - } + ), + }, ) @@ -1035,24 +954,22 @@ def reset_password(request): if userform.is_valid(): try: user = User.objects.get( - pseudo=userform.cleaned_data['pseudo'], - email=userform.cleaned_data['email'], + pseudo=userform.cleaned_data["pseudo"], + email=userform.cleaned_data["email"], state__in=[User.STATE_ACTIVE, User.STATE_NOT_YET_ACTIVE], ) except User.DoesNotExist: messages.error(request, _("The user doesn't exist.")) return form( - {'userform': userform, 'action_name': _("Reset")}, - 'users/user.html', - request + {"userform": userform, "action_name": _("Reset")}, + "users/user.html", + request, ) user.reset_passwd_mail(request) messages.success(request, _("An email to reset the password was sent.")) - redirect(reverse('index')) + redirect(reverse("index")) return form( - {'userform': userform, 'action_name': _("Reset")}, - 'users/user.html', - request + {"userform": userform, "action_name": _("Reset")}, "users/user.html", request ) @@ -1065,7 +982,7 @@ def process(request, token): return process_passwd(request, req) else: messages.error(request, _("Error: please contact an admin.")) - redirect(reverse('index')) + redirect(reverse("index")) def process_passwd(request, req): @@ -1076,43 +993,52 @@ def process_passwd(request, req): if u_form.is_valid(): with transaction.atomic(), reversion.create_revision(): u_form.save() - reversion.set_comment(_("Password reset")) + reversion.set_comment("Password reset") req.delete() messages.success(request, _("The password was changed.")) - return redirect(reverse('index')) + return redirect(reverse("index")) return form( - {'userform': u_form, 'action_name': _("Change the password")}, - 'users/user.html', - request + {"userform": u_form, "action_name": _("Change the password")}, + "users/user.html", + request, ) + @login_required def initial_register(request): - switch_ip = request.GET.get('switch_ip', None) - switch_port = request.GET.get('switch_port', None) - client_mac = request.GET.get('client_mac', None) - u_form = InitialRegisterForm(request.POST or None, user=request.user, switch_ip=switch_ip, switch_port=switch_port, client_mac=client_mac) + switch_ip = request.GET.get("switch_ip", None) + switch_port = request.GET.get("switch_port", None) + client_mac = request.GET.get("client_mac", None) + u_form = InitialRegisterForm( + request.POST or None, + user=request.user, + switch_ip=switch_ip, + switch_port=switch_port, + client_mac=client_mac, + ) if not u_form.fields: messages.error(request, _("Incorrect URL, or already registered device.")) - return redirect(reverse( - 'users:profil', - kwargs={'userid': str(request.user.id)} - )) - if switch_ip and switch_port: - port = Port.objects.filter(switch__interface__ipv4__ipv4=switch_ip, port=switch_port).first() - if u_form.is_valid(): - messages.success(request, _("Successful registration! Please" - " disconnect and reconnect your Ethernet" - " cable to get Internet access.")) - return form( - {}, - 'users/plugin_out.html', - request + return redirect( + reverse("users:profil", kwargs={"userid": str(request.user.id)}) ) + if switch_ip and switch_port: + port = Port.objects.filter( + switch__interface__ipv4__ipv4=switch_ip, port=switch_port + ).first() + if u_form.is_valid(): + messages.success( + request, + _( + "Successful registration! Please" + " disconnect and reconnect your Ethernet" + " cable to get Internet access." + ), + ) + return form({}, "users/plugin_out.html", request) return form( - {'userform': u_form, 'port': port, 'mac': client_mac}, - 'users/user_autocapture.html', - request + {"userform": u_form, "port": port, "mac": client_mac}, + "users/user_autocapture.html", + request, ) @@ -1121,75 +1047,72 @@ class JSONResponse(HttpResponse): def __init__(self, data, **kwargs): content = JSONRenderer().render(data) - kwargs['content_type'] = 'application/json' + kwargs["content_type"] = "application/json" super(JSONResponse, self).__init__(content, **kwargs) @csrf_exempt @login_required -@permission_required('machines.serveur') +@permission_required("machines.serveur") def ml_std_list(_request): """ API view sending all the available standard mailings""" - return JSONResponse([ - {'name': 'adherents'} - ]) + return JSONResponse([{"name": "adherents"}]) @csrf_exempt @login_required -@permission_required('machines.serveur') +@permission_required("machines.serveur") def ml_std_members(request, ml_name): """ API view sending all the members for a standard mailing""" # All with active connextion - if ml_name == 'adherents': - members = all_has_access().values('email').distinct() + if ml_name == "adherents": + members = all_has_access().values("email").distinct() # Unknown mailing else: messages.error(request, _("The mailing list doesn't exist.")) - return redirect(reverse('index')) + return redirect(reverse("index")) seria = MailingMemberSerializer(members, many=True) return JSONResponse(seria.data) @csrf_exempt @login_required -@permission_required('machines.serveur') +@permission_required("machines.serveur") def ml_club_list(_request): """ API view sending all the available club mailings""" - clubs = Club.objects.filter(mailing=True).values('pseudo') + clubs = Club.objects.filter(mailing=True).values("pseudo") seria = MailingSerializer(clubs, many=True) return JSONResponse(seria.data) @csrf_exempt @login_required -@permission_required('machines.serveur') +@permission_required("machines.serveur") def ml_club_admins(request, ml_name): """ API view sending all the administrators for a specific club mailing""" try: club = Club.objects.get(mailing=True, pseudo=ml_name) except Club.DoesNotExist: messages.error(request, _("The mailing list doesn't exist.")) - return redirect(reverse('index')) - members = club.administrators.all().values('email').distinct() + return redirect(reverse("index")) + members = club.administrators.all().values("email").distinct() seria = MailingMemberSerializer(members, many=True) return JSONResponse(seria.data) @csrf_exempt @login_required -@permission_required('machines.serveur') +@permission_required("machines.serveur") def ml_club_members(request, ml_name): """ API view sending all the members for a specific club mailing""" try: club = Club.objects.get(mailing=True, pseudo=ml_name) except Club.DoesNotExist: messages.error(request, _("The mailing list doesn't exist.")) - return redirect(reverse('index')) + return redirect(reverse("index")) members = ( - club.administrators.all().values('email').distinct() | - club.members.all().values('email').distinct() + club.administrators.all().values("email").distinct() + | club.members.all().values("email").distinct() ) seria = MailingMemberSerializer(members, many=True) return JSONResponse(seria.data) - diff --git a/users/widgets.py b/users/widgets.py index b423d2e0..81cced82 100644 --- a/users/widgets.py +++ b/users/widgets.py @@ -1,19 +1,20 @@ -from django.forms.widgets import Input -from django.forms.utils import flatatt -from django.utils.safestring import mark_safe -from django.template import Context, Template -from django.template.loader import get_template -from django.conf import settings -from django.utils.translation import ugettext_lazy as _, get_language_bidi -from django.utils.dates import ( - WEEKDAYS, - WEEKDAYS_ABBR, - MONTHS, - MONTHS_3, - MONTHS_AP, - MONTHS_ALT +from django.forms.widgets import Input +from django.forms.utils import flatatt +from django.utils.safestring import mark_safe +from django.template import Template +from django.template.loader import get_template +from django.conf import settings +from django.utils.translation import ugettext_lazy as _, get_language_bidi +from django.utils.dates import ( + WEEKDAYS, + WEEKDAYS_ABBR, + MONTHS, + MONTHS_3, + MONTHS_AP, + MONTHS_ALT, ) + def list2str(str_iterable): """ Utility function to return a string representing a list of string @@ -22,29 +23,42 @@ def list2str(str_iterable): :returns: A representation of the iterable as a list (e.g '["a", "b"]') """ return '["' + '", "'.join(str_iterable) + '"]' - + + class DateTimePicker(Input): is_localized = False - def render(self, name, value, attrs=None): - super().render(name, value, attrs) - flat_attrs = flatatt(attrs) - context = Context({ - 'name': name, - 'attrs': flat_attrs, - 'id': attrs['id'], - 'closeText': _("Close"), - 'currentText': _("Today"), - 'dayNames': mark_safe(list2str((str(item[1]) for item in WEEKDAYS.items()))), - 'dayNamesMin': mark_safe(list2str((str(item[1]) for item in WEEKDAYS_ABBR.items()))), - 'dayNamesShort': mark_safe(list2str((str(item[1]) for item in WEEKDAYS_ABBR.items()))), - 'firstDay': mark_safe('"' + str(WEEKDAYS[settings.FIRST_DAY_OF_WEEK]) + '"'), - 'isRTL': str(get_language_bidi()).lower(), - 'monthNames': mark_safe(list2str((str(item[1]) for item in MONTHS.items()))), - 'monthNamesShort': mark_safe(list2str((str(item[1]) for item in MONTHS_3.items()))), - 'nextText': mark_safe('"' + str(_('Next')) + '"'), - 'prevText': mark_safe('"' + str(_('Previous')) + '"'), - 'weekHeader': mark_safe('"' + str(_('Wk')) + '"' ), - }) - template = get_template('users/datetimepicker.html') - return template.render(context) + def render(self, name, value, attrs=None): + super().render(name, value, attrs) + flat_attrs = flatatt(attrs) + context = { + "name": name, + "attrs": flat_attrs, + "id": attrs["id"], + "closeText": _("Close"), + "currentText": _("Today"), + "dayNames": mark_safe( + list2str((str(item[1]) for item in WEEKDAYS.items())) + ), + "dayNamesMin": mark_safe( + list2str((str(item[1]) for item in WEEKDAYS_ABBR.items())) + ), + "dayNamesShort": mark_safe( + list2str((str(item[1]) for item in WEEKDAYS_ABBR.items())) + ), + "firstDay": mark_safe( + '"' + str(WEEKDAYS[settings.FIRST_DAY_OF_WEEK]) + '"' + ), + "isRTL": str(get_language_bidi()).lower(), + "monthNames": mark_safe( + list2str((str(item[1]) for item in MONTHS.items())) + ), + "monthNamesShort": mark_safe( + list2str((str(item[1]) for item in MONTHS_3.items())) + ), + "nextText": mark_safe('"' + str(_("Next")) + '"'), + "prevText": mark_safe('"' + str(_("Previous")) + '"'), + "weekHeader": mark_safe('"' + str(_("Wk")) + '"'), + } + template = get_template("users/datetimepicker.html") + return template.render(context)