From 9e7edd9902d8f04d4eea2598161b3e6f3b639c06 Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Sun, 10 Jun 2018 21:32:41 +0200 Subject: [PATCH 01/48] debut de refonte de la page profil --- static/css/base.css | 37 ++++++++++++++ users/templates/users/profil.html | 85 ++++++++++++++++++++++++------- 2 files changed, 105 insertions(+), 17 deletions(-) diff --git a/static/css/base.css b/static/css/base.css index b6a7ae26..814a5f72 100644 --- a/static/css/base.css +++ b/static/css/base.css @@ -91,3 +91,40 @@ footer a { } .row.content {height:auto;} } + +/* style for the user page */ + +.dashboard_container{ +margin-top: 30px; +margin-bottom: 20px; +} + + +.panel-heading.dashboard{ + text-align: center; +} + +.panel-body.dashboard{ + text-align: center; +} +#grad_red { + background: red; /* For browsers that do not support gradients */ + background: linear-gradient(#ff6363, #fefefe); /* Standard syntax (must be last) */ +} + +#grad_green { + background: green; /* For browsers that do not support gradients */ + background: linear-gradient(#C8DD58,#4FB64A); /* Standard syntax (must be last) */ +} + +#grad_grey { + background: gray; /* For browsers that do not support gradients */ + background: linear-gradient(#d4d4ff, #fefefe); /* Standard syntax (must be last) */ +} + +#grad_machines{ + background: green; + background: linear-gradient(#c266e0,#fefefe) +} + + diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index abba61a2..f1c9331b 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -27,21 +27,72 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load acl %} {% block title %}Profil{% endblock %} {% block content %} -

{{ users.surname }} {{users.name}}

-

Vous êtes {% if users.end_adhesion != None %} -un {{ users.class_name | lower}}{% else %} -non adhérent{% endif %} et votre connexion est {% if users.has_access %} -active{% else %}désactivée{% endif %}.

-{% if user_solde %} -

Votre solde est de {{ user.solde }}€. -{% if allow_online_payment %} - - - Recharger - -{% endif %} -

-{% endif %} +
+

Bienvenue {{users.name}} {{ users.surname }}

+
+
+
+
+
+ {% if users.is_adherent %} +
+
Adhérent
+
Fin d'adhésion: {{user.end_adhesion}}
+ {% else %} +
+
Non adhérent
+ + {% endif %} +
+
+
+
+
+
+
+
{{user.solde}}
+ +
+
+
+
+
+
+ {% if nb_machines %} +
+
+ {{nb_machines}} + Machines + +
+ {% else %} +
+
Aucune machine
+
+ {% endif %} + +
+
+
+
+
+ +
@@ -50,7 +101,7 @@ non adhérent{% endif %} et votre connexion est {% if users.has_access %} Informations détaillées
-
+
{% if machines_list %} From 83aa267940982b16f1a3685e8fc49f94e6b766a9 Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Sun, 10 Jun 2018 21:36:36 +0200 Subject: [PATCH 02/48] rectification si aucune machine --- users/templates/users/profil.html | 1 - 1 file changed, 1 deletion(-) diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index f1c9331b..9b751842 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -78,7 +78,6 @@ with this program; if not, write to the Free Software Foundation, Inc., {% else %}
Aucune machine
-
{% endif %}
+ {% if solde_activated %} + {% endif %} + {% if solde_activated %}
+ {% else %} +
+ {% endif %}
{% if nb_machines %}
-
- {{nb_machines}} - Machines - +
+ {{nb_machines}} + Machines + +
+
{% else %}
-
Aucune machine
- {% endif %} - +
Aucune machine
+
+ {% endif %}
From a435f0d1ca261ceddcebc771617317bd714b9751 Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Fri, 15 Jun 2018 18:08:30 +0200 Subject: [PATCH 04/48] =?UTF-8?q?Frontend=20page=20preference=20si=20solde?= =?UTF-8?q?=20non=20activ=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../preferences/display_preferences.html | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 99e3e14f..1904c7e0 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -40,16 +40,14 @@ with this program; if not, write to the Free Software Foundation, Inc., Téléphone obligatoirement requis {{ useroptions.is_tel_mandatory }} - Activation du solde pour les utilisateurs - {{ useroptions.user_solde }} + Auto inscription + {{ useroptions.self_adhesion }} Champ gpg fingerprint {{ useroptions.gpg_fingerprint }} - {% if useroptions.user_solde %} - Solde négatif - {{ useroptions.solde_negatif }} - {% endif %} + Shell par défaut des utilisateurs + {{ useroptions.shell_default }} Creations d'adhérents par tous @@ -57,20 +55,23 @@ with this program; if not, write to the Free Software Foundation, Inc., Creations de clubs par tous {{ useroptions.all_can_create_club }} - {% if useroptions.user_solde %} + - Solde maximum - {{ useroptions.max_solde }} - Montant minimal de rechargement en ligne - {{ useroptions.min_online_payment }} + Activation du solde pour les utilisateurs + {{ useroptions.user_solde }} + {% if useroptions.user_solde %} + Solde négatif + {{ useroptions.solde_negatif }} + + + Solde maximum + {{ useroptions.max_solde }} + Montant minimal de rechargement en ligne + {{ useroptions.min_online_payment }} - {% endif %} - - Auto inscription - {{ useroptions.self_adhesion }} - Shell par défaut des utilisateurs - {{ useroptions.shell_default }} + {% else %} + {% endif%}

Préférences machines

From e18c538495178a7c2af4674f44b2d37775fddf7a Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Fri, 15 Jun 2018 18:12:43 +0200 Subject: [PATCH 05/48] Envoi le la variable d'activation du solde --- users/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/users/views.py b/users/views.py index 34a08313..1612aa93 100644 --- a/users/views.py +++ b/users/views.py @@ -111,8 +111,8 @@ def new_user(request): GTU_sum_up = GeneralOption.get_cached_value('GTU_sum_up') GTU = GeneralOption.get_cached_value('GTU') if user.is_valid(): - user = user.save(commit=False) - user.save() + #user = user.save(commit=False) + user = user.save() user.reset_passwd_mail(request) messages.success(request, "L'utilisateur %s a été crée, un mail\ pour l'initialisation du mot de passe a été envoyé" % user.pseudo) @@ -860,6 +860,7 @@ def profil(request, users, **_kwargs): 'white_list': whitelists, 'user_solde': user_solde, 'allow_online_payment': allow_online_payment, + 'solde_activated': OptionalUser.objects.first().user_solde } ) From 75324a13d39765a74c587cc21f85420289bcba83 Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Fri, 15 Jun 2018 18:16:07 +0200 Subject: [PATCH 06/48] Envoi de la variable d'activation du solde --- users/templates/users/profil.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index a4fa73b6..5f325ce0 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -40,7 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% if users.is_adherent %}
-
Connécté
+
Connécté
Fin de connexion: {{user.end_adhesion|date:"d M Y"}}
{% else %} From 48b85f865832f06c2148ecc1426cef9eb6d3b168 Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Sat, 16 Jun 2018 09:57:21 +0200 Subject: [PATCH 07/48] =?UTF-8?q?Prise=20en=20compte=20du=20bannissement?= =?UTF-8?q?=20et=20d=C3=A9ployement=20des=20des=20infos=20si=20clique=20da?= =?UTF-8?q?ns=20les=20titres?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/css/base.css | 2 ++ users/templates/users/profil.html | 41 ++++++++++++++++++++----------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/static/css/base.css b/static/css/base.css index 814a5f72..e4c8075c 100644 --- a/static/css/base.css +++ b/static/css/base.css @@ -106,6 +106,8 @@ margin-bottom: 20px; .panel-body.dashboard{ text-align: center; + height: 60px; + vertical-align:middle; } #grad_red { background: red; /* For browsers that do not support gradients */ diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index 5f325ce0..2eac1e17 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -38,20 +38,28 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endif %}
- {% if users.is_adherent %} -
-
Connécté
-
Fin de connexion: {{user.end_adhesion|date:"d M Y"}}
-
- {% else %} + {% if users.is_ban%}
-
Non connécté
-
+
Votre compte est banni
+
+ Fin du ban : {{user.end_ban|date:"d M Y"}} +
+
+ {% elif not users.is_adherent%} +
+
Non Connécté
+ + {% else %} +
+
Connécté
+
+ Fin de connexion: {{user.end_adhesion|date:"d M Y"}} +
{% endif %}
@@ -61,7 +69,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
-
{{user.solde}}
+
+ + {{user.solde}} +
@@ -81,7 +92,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% if nb_machines %}
-
+
{{nb_machines}} Machines From 7ab0d656c937d4ccd88df00ca694479ebc24fb64 Mon Sep 17 00:00:00 2001 From: Laouen Fernet Date: Sun, 27 May 2018 00:36:25 +0200 Subject: [PATCH 08/48] add model PortProfile --- re2o/templatetags/acl.py | 1 + re2o/views.py | 1 + topologie/admin.py | 7 +- topologie/forms.py | 58 ++++++++++++++++ topologie/migrations/0061_portprofile.py | 36 ++++++++++ .../migrations/0062_auto_20180609_1151.py | 66 +++++++++++++++++++ .../migrations/0063_auto_20180609_1158.py | 20 ++++++ .../migrations/0064_auto_20180609_1220.py | 20 ++++++ topologie/models.py | 55 ++++++++++++++++ .../templates/topologie/aff_port_profile.html | 46 +++++++++++++ topologie/templates/topologie/index.html | 10 +++ topologie/urls.py | 9 +++ topologie/views.py | 65 +++++++++++++++++- 13 files changed, 391 insertions(+), 3 deletions(-) create mode 100644 topologie/migrations/0061_portprofile.py create mode 100644 topologie/migrations/0062_auto_20180609_1151.py create mode 100644 topologie/migrations/0063_auto_20180609_1158.py create mode 100644 topologie/migrations/0064_auto_20180609_1220.py create mode 100644 topologie/templates/topologie/aff_port_profile.html diff --git a/re2o/templatetags/acl.py b/re2o/templatetags/acl.py index fccbea1d..11399633 100644 --- a/re2o/templatetags/acl.py +++ b/re2o/templatetags/acl.py @@ -129,6 +129,7 @@ MODEL_NAME = { 'Room': topologie.models.Room, 'Building': topologie.models.Building, 'SwitchBay': topologie.models.SwitchBay, + 'PortProfile': topologie.models.PortProfile, # users 'User': users.models.User, 'Adherent': users.models.Adherent, diff --git a/re2o/views.py b/re2o/views.py index 3c5cde09..d803505e 100644 --- a/re2o/views.py +++ b/re2o/views.py @@ -111,6 +111,7 @@ HISTORY_BIND = { 'accesspoint': topologie.models.AccessPoint, 'switchbay': topologie.models.SwitchBay, 'building': topologie.models.Building, + 'portprofile': topologie.models.PortProfile, }, 'machines': { 'machine': machines.models.Machine, diff --git a/topologie/admin.py b/topologie/admin.py index 62ffd6c4..d2a25461 100644 --- a/topologie/admin.py +++ b/topologie/admin.py @@ -38,7 +38,8 @@ from .models import ( ConstructorSwitch, AccessPoint, SwitchBay, - Building + Building, + PortProfile, ) @@ -86,6 +87,9 @@ class BuildingAdmin(VersionAdmin): """Administration d'un batiment""" pass +class PortProfileAdmin(VersionAdmin): + """Administration of a port profile""" + pass admin.site.register(Port, PortAdmin) admin.site.register(AccessPoint, AccessPointAdmin) @@ -96,3 +100,4 @@ admin.site.register(ModelSwitch, ModelSwitchAdmin) admin.site.register(ConstructorSwitch, ConstructorSwitchAdmin) admin.site.register(Building, BuildingAdmin) admin.site.register(SwitchBay, SwitchBayAdmin) +admin.site.register(PortProfile, PortProfileAdmin) diff --git a/topologie/forms.py b/topologie/forms.py index 18831217..377a39b3 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -35,6 +35,7 @@ from __future__ import unicode_literals from django import forms from django.forms import ModelForm from django.db.models import Prefetch +from django.utils.translation import ugettext_lazy as _ from machines.models import Interface from machines.forms import ( @@ -53,6 +54,7 @@ from .models import ( AccessPoint, SwitchBay, Building, + PortProfile, ) @@ -262,3 +264,59 @@ class EditBuildingForm(FormRevMixin, ModelForm): def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(EditBuildingForm, self).__init__(*args, prefix=prefix, **kwargs) + + +class NewPortProfileForm(FormRevMixin, ModelForm): + """Form to create a port profile""" + class Meta: + model = PortProfile + fields = '__all__' + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(NewPortProfileForm, self).__init__(*args, + prefix=prefix, + **kwargs) + + def clean(self): + cleaned_data = super(NewPortProfileForm, self).clean() + radius_type = cleaned_data.get('radius_type') + radius_mode = cleaned_data.get('radius_mode') + + if radius_type == 'NO' and radius_mode: + raise forms.ValidationError(_("You can't specify a RADIUS mode" + " with RADIUS type NO")) + elif radius_type != 'NO' and not radius_mode: + raise forms.ValidationError(_("You have to specify a RADIUS" + " mode")) + + return cleaned_data + + +class EditPortProfileForm(FormRevMixin, ModelForm): + """Form to edit a port profile""" + class Meta: + model = PortProfile + fields = '__all__' + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(EditPortProfileForm, self).__init__(*args, + prefix=prefix, + **kwargs) + + def clean(self): + cleaned_data = super(EditPortProfileForm, self).clean() + radius_type = cleaned_data.get('radius_type') + radius_mode = cleaned_data.get('radius_mode') + + if radius_type == 'NO' and radius_mode: + raise forms.ValidationError(_("You can't specify a RADIUS mode" + " with RADIUS type NO")) + elif radius_type != 'NO' and not radius_mode: + raise forms.ValidationError(_("You have to specify a RADIUS" + " mode")) + + return cleaned_data + + diff --git a/topologie/migrations/0061_portprofile.py b/topologie/migrations/0061_portprofile.py new file mode 100644 index 00000000..7b6af2ed --- /dev/null +++ b/topologie/migrations/0061_portprofile.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-05-26 22:26 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import re2o.mixins + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0081_auto_20180521_1413'), + ('topologie', '0060_server'), + ] + + operations = [ + migrations.CreateModel( + name='PortProfile', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('room_default', models.BooleanField()), + ('hotspot_default', models.BooleanField()), + ('uplink_default', models.BooleanField()), + ('orga_machine_default', models.BooleanField()), + ('radius_type', models.CharField(choices=[('NO', 'NO'), ('802.1X', '802.1X'), ('MAC-radius', 'MAC-radius')], max_length=32)), + ('radius_mode', models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], max_length=32)), + ('vlan_tagged', models.ManyToManyField(related_name='vlan_tagged', to='machines.Vlan')), + ('vlan_untagged', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vlan_untagged', to='machines.Vlan')), + ], + options={ + 'permissions': (('view_port_profile', 'Can view a port profile object'),), + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + ] diff --git a/topologie/migrations/0062_auto_20180609_1151.py b/topologie/migrations/0062_auto_20180609_1151.py new file mode 100644 index 00000000..3f074f7c --- /dev/null +++ b/topologie/migrations/0062_auto_20180609_1151.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-09 16:51 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0061_portprofile'), + ] + + operations = [ + migrations.AlterModelOptions( + name='portprofile', + options={'permissions': (('view_port_profile', 'Can view a port profile object'),), 'verbose_name': 'Port profile', 'verbose_name_plural': 'Port profiles'}, + ), + migrations.AddField( + model_name='portprofile', + name='name', + field=models.CharField(default='Sans nom', max_length=255, verbose_name='Name'), + preserve_default=False, + ), + migrations.AlterField( + model_name='portprofile', + name='hotspot_default', + field=models.BooleanField(verbose_name='Hotspot default'), + ), + migrations.AlterField( + model_name='portprofile', + name='orga_machine_default', + field=models.BooleanField(verbose_name='Organisation machine default'), + ), + migrations.AlterField( + model_name='portprofile', + name='radius_mode', + field=models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], 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')], max_length=32, verbose_name='RADIUS type'), + ), + migrations.AlterField( + model_name='portprofile', + name='room_default', + field=models.BooleanField(verbose_name='Room default'), + ), + migrations.AlterField( + model_name='portprofile', + name='uplink_default', + field=models.BooleanField(verbose_name='Uplink default'), + ), + migrations.AlterField( + model_name='portprofile', + name='vlan_tagged', + field=models.ManyToManyField(related_name='vlan_tagged', to='machines.Vlan', verbose_name='VLAN(s) tagged'), + ), + migrations.AlterField( + model_name='portprofile', + name='vlan_untagged', + field=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'), + ), + ] diff --git a/topologie/migrations/0063_auto_20180609_1158.py b/topologie/migrations/0063_auto_20180609_1158.py new file mode 100644 index 00000000..59ea8732 --- /dev/null +++ b/topologie/migrations/0063_auto_20180609_1158.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-09 16:58 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0062_auto_20180609_1151'), + ] + + operations = [ + migrations.AlterField( + model_name='portprofile', + name='vlan_tagged', + field=models.ManyToManyField(blank=True, related_name='vlan_tagged', to='machines.Vlan', verbose_name='VLAN(s) tagged'), + ), + ] diff --git a/topologie/migrations/0064_auto_20180609_1220.py b/topologie/migrations/0064_auto_20180609_1220.py new file mode 100644 index 00000000..f657a612 --- /dev/null +++ b/topologie/migrations/0064_auto_20180609_1220.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-09 17:20 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0063_auto_20180609_1158'), + ] + + operations = [ + migrations.AlterField( + model_name='portprofile', + name='radius_mode', + field=models.CharField(blank=True, choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], max_length=32, null=True, verbose_name='RADIUS mode'), + ), + ] diff --git a/topologie/models.py b/topologie/models.py index e1c945c7..cdf30787 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -46,6 +46,7 @@ from django.dispatch import receiver from django.core.exceptions import ValidationError from django.db import IntegrityError from django.db import transaction +from django.utils.translation import ugettext_lazy as _ from reversion import revisions as reversion from machines.models import Machine, regen @@ -488,6 +489,60 @@ class Room(AclMixin, RevMixin, models.Model): return self.name +class PortProfile(AclMixin, 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'), + ) + name = models.CharField(max_length=255, verbose_name=_("Name")) + room_default = models.BooleanField(verbose_name=_("Room default")) + hotspot_default = models.BooleanField(_("Hotspot default")) + uplink_default = models.BooleanField(_("Uplink default")) + orga_machine_default = models.BooleanField(_("Organisation machine" + " default")) + 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', + blank=True, + verbose_name=_("VLAN(s) tagged")) + radius_type = models.CharField( + max_length=32, + choices=TYPES, + verbose_name=_("RADIUS type") + ) + radius_mode = models.CharField( + max_length=32, + choices=MODES, + blank=True, + null=True, + verbose_name=_("RADIUS mode") + ) + + class Meta: + permissions = ( + ("view_port_profile", _("Can view a port profile object")), + ) + verbose_name = _("Port profile") + verbose_name_plural = _("Port profiles") + + def __str__(self): + return self.name + + @receiver(post_save, sender=AccessPoint) def ap_post_save(**_kwargs): """Regeneration des noms des bornes vers le controleur""" diff --git a/topologie/templates/topologie/aff_port_profile.html b/topologie/templates/topologie/aff_port_profile.html new file mode 100644 index 00000000..b4b936c5 --- /dev/null +++ b/topologie/templates/topologie/aff_port_profile.html @@ -0,0 +1,46 @@ +{% load acl %} +{% load i18n %} + +
+ +{% if port_profile_list.paginator %} +{% include "pagination.html" with list=port_profile_list %} +{% endif %} + + + + + + + + + + + + {% for port_profile in port_profile_list %} + + + + + + + + + {% endfor %} +
{% trans "Name" %}{% trans "VLAN untagged" %}{% trans "VLAN(s) tagged" %}{% trans "RADIUS type" %}{% trans "RADIUS mode" %}
{{port_profile.name}}{{port_profile.vlan_untagged}} + {{port_profile.vlan_tagged.all|join:", "}} + {{port_profile.radius_type}}{{port_profile.radius_mode}} + {% include 'buttons/history.html' with href='topologie:history' name='portprofile' id=port_profile.pk %} + {% can_edit port_profile %} + {% include 'buttons/edit.html' with href='topologie:edit-port-profile' id=port_profile.pk %} + {% acl_end %} + {% can_delete port_profile %} + {% include 'buttons/suppr.html' with href='topologie:del-port-profile' id=port_profile.pk %} + {% acl_end %} +
+ +{% if port_profile_list.paginator %} +{% include "pagination.html" with list=port_profile_list %} +{% endif %} + +
diff --git a/topologie/templates/topologie/index.html b/topologie/templates/topologie/index.html index a7c4bb51..7902f4a3 100644 --- a/topologie/templates/topologie/index.html +++ b/topologie/templates/topologie/index.html @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load acl %} +{% load i18n %} {% block title %}Switchs{% endblock %} @@ -72,5 +73,14 @@ Topologie des Switchs

+

{% trans "Port profiles" %}

+{% can_create PortProfile %} +
{% trans " Add a port profile" %} +
+{% acl_end %} + {% include "topologie/aff_port_profile.html" with port_profile_list=port_profile_list %} +
+
+
{% endblock %} diff --git a/topologie/urls.py b/topologie/urls.py index af3327b7..7cfc5714 100644 --- a/topologie/urls.py +++ b/topologie/urls.py @@ -113,4 +113,13 @@ urlpatterns = [ url(r'^del_building/(?P[0-9]+)$', views.del_building, name='del-building'), + 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]+)$', + views.del_port_profile, + name='del-port-profile'), ] diff --git a/topologie/views.py b/topologie/views.py index 652ef475..f1f6a09f 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -47,6 +47,7 @@ from django.template.loader import get_template from django.template import Context, Template, loader from django.db.models.signals import post_save from django.dispatch import receiver +from django.utils.translation import ugettext as _ import tempfile @@ -80,6 +81,7 @@ from .models import ( SwitchBay, Building, Server, + PortProfile, ) from .forms import ( EditPortForm, @@ -94,7 +96,9 @@ from .forms import ( AddAccessPointForm, EditAccessPointForm, EditSwitchBayForm, - EditBuildingForm + EditBuildingForm, + NewPortProfileForm, + EditPortProfileForm, ) from subprocess import ( @@ -124,8 +128,12 @@ def index(request): request.GET.get('order'), SortTable.TOPOLOGIE_INDEX ) + + port_profile_list = PortProfile.objects.all() + pagination_number = GeneralOption.get_cached_value('pagination_number') switch_list = re2o_paginator(request, switch_list, pagination_number) + port_profile_list = re2o_paginator(request, port_profile_list, pagination_number) if any(service_link.need_regen() for service_link in Service_link.objects.filter(service__service_type='graph_topo')): make_machine_graph() @@ -137,7 +145,7 @@ def index(request): return render( request, 'topologie/index.html', - {'switch_list': switch_list} + {'switch_list': switch_list, 'port_profile_list': port_profile_list} ) @@ -955,6 +963,59 @@ def del_constructor_switch(request, constructor_switch, **_kwargs): }, 'topologie/delete.html', request) +@login_required +@can_create(PortProfile) +def new_port_profile(request): + """Create a new port profile""" + port_profile = NewPortProfileForm(request.POST or None) + if port_profile.is_valid(): + port_profile.save() + messages.success(request, _("Port profile created")) + return redirect(reverse('topologie:index')) + return form( + {'topoform': port_profile, 'action_name': _("Create")}, + 'topologie/topo.html', + request + ) + + +@login_required +@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) + if port_profile.is_valid(): + if port_profile.changed_data: + port_profile.save() + messages.success(request, _("Port profile modified")) + return redirect(reverse('topologie:index')) + return form( + {'topoform': port_profile, 'action_name': _("Edit")}, + 'topologie/topo.html', + request + ) + + + +@login_required +@can_delete(PortProfile) +def del_port_profile(request, port_profile, **_kwargs): + """Delete a port profile""" + if request.method == 'POST': + try: + port_profile.delete() + messages.success(request, _("The port profile was successfully" + " deleted")) + except ProtectedError: + messages.success(request, _("Impossible to delete the port" + " profile")) + return redirect(reverse('topologie:index')) + return form( + {'objet': port_profile, 'objet_name': _("Port profile")}, + 'topologie/delete.html', + request + ) + def make_machine_graph(): """ Create the graph of switchs, machines and access points. From d8bcbba229036b130c91f127798a8c2204af4ac5 Mon Sep 17 00:00:00 2001 From: Grizzly Date: Tue, 26 Jun 2018 15:22:16 +0000 Subject: [PATCH 09/48] orthographe et mise en page --- users/templates/users/profil.html | 49 +++++++++++++++---------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index 2eac1e17..bc0a5ac4 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -40,23 +40,24 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% if users.is_ban%}
-
Votre compte est banni
+
Votre compte est banni
Fin du ban : {{user.end_ban|date:"d M Y"}}
- {% elif not users.is_adherent%} + {% elif not users.is_connected%}
-
Non Connécté
- +
Pas d'accès à internet
+ +
{% else %}
-
Connécté
+
Connecté
Fin de connexion: {{user.end_adhesion|date:"d M Y"}}
@@ -67,23 +68,21 @@ with this program; if not, write to the Free Software Foundation, Inc., {% if solde_activated %}
-
-
-
- - {{user.solde}} -
- -
-
-
+
+
+ {{user.solde}} +
+ +
+
{% endif %} + {% if solde_activated %}
{% else %} From c762f49468bccc557dc76505cc5f49784d00ecee Mon Sep 17 00:00:00 2001 From: Charlie Jacomme Date: Tue, 26 Jun 2018 18:44:07 +0200 Subject: [PATCH 10/48] Optimizing facture request --- users/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/views.py b/users/views.py index fcb44f65..c18fa643 100644 --- a/users/views.py +++ b/users/views.py @@ -860,7 +860,7 @@ def profil(request, users, **_kwargs): ) nb_machines = machines.count() machines = re2o_paginator(request, machines, pagination_large_number) - factures = Facture.objects.filter(user=users) + factures = Facture.objects.filter(user=users).select_related('paiement').select_related('user') factures = SortTable.sort( factures, request.GET.get('col'), From 983b5620aade099f321aab7e4dd4223f5f64f55d Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 26 Jun 2018 16:49:19 +0000 Subject: [PATCH 11/48] Refactor port_profil --- topologie/migrations/0061_portprofile.py | 30 +++-- .../migrations/0062_auto_20180609_1151.py | 66 ----------- .../migrations/0063_auto_20180609_1158.py | 20 ---- .../migrations/0064_auto_20180609_1220.py | 20 ---- topologie/models.py | 111 ++++++++++++++---- .../templates/topologie/aff_port_profile.html | 32 ++++- 6 files changed, 137 insertions(+), 142 deletions(-) delete mode 100644 topologie/migrations/0062_auto_20180609_1151.py delete mode 100644 topologie/migrations/0063_auto_20180609_1158.py delete mode 100644 topologie/migrations/0064_auto_20180609_1220.py diff --git a/topologie/migrations/0061_portprofile.py b/topologie/migrations/0061_portprofile.py index 7b6af2ed..2b326f28 100644 --- a/topologie/migrations/0061_portprofile.py +++ b/topologie/migrations/0061_portprofile.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-05-26 22:26 +# Generated by Django 1.10.7 on 2018-06-26 16:37 from __future__ import unicode_literals from django.db import migrations, models @@ -10,7 +10,7 @@ import re2o.mixins class Migration(migrations.Migration): dependencies = [ - ('machines', '0081_auto_20180521_1413'), + ('machines', '0082_auto_20180525_2209'), ('topologie', '0060_server'), ] @@ -19,18 +19,26 @@ class Migration(migrations.Migration): name='PortProfile', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('room_default', models.BooleanField()), - ('hotspot_default', models.BooleanField()), - ('uplink_default', models.BooleanField()), - ('orga_machine_default', models.BooleanField()), - ('radius_type', models.CharField(choices=[('NO', 'NO'), ('802.1X', '802.1X'), ('MAC-radius', 'MAC-radius')], max_length=32)), - ('radius_mode', models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], max_length=32)), - ('vlan_tagged', models.ManyToManyField(related_name='vlan_tagged', to='machines.Vlan')), - ('vlan_untagged', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vlan_untagged', to='machines.Vlan')), + ('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')], 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')], max_length=32, verbose_name='RADIUS type')), + ('radius_mode', models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], default='COMMON', 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='Mode de transmission et vitesse du port', max_length=32, verbose_name='Speed')), + ('mac_limit', models.IntegerField(blank=True, help_text='Limit du nombre de mac sur le port', null=True, verbose_name='Mac limit')), + ('flow_control', models.BooleanField(default=False, help_text='Gestion des débits', verbose_name='Flow control')), + ('dhcp_snooping', models.BooleanField(default=False, help_text='Protection dhcp pirate', verbose_name='Dhcp snooping')), + ('dhcpv6_snooping', models.BooleanField(default=False, help_text='Protection dhcpv6 pirate', verbose_name='Dhcpv6 snooping')), + ('arp_protect', models.BooleanField(default=False, help_text="Verification assignation de l'IP par dhcp", verbose_name='Arp protect')), + ('ra_guard', models.BooleanField(default=False, help_text='Protection contre ra pirate', verbose_name='Ra guard')), + ('loop_protect', models.BooleanField(default=False, help_text='Protection contre les boucles', verbose_name='Loop Protect')), + ('vlan_tagged', models.ManyToManyField(blank=True, null=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', }, - bases=(re2o.mixins.AclMixin, models.Model), + bases=(re2o.mixins.AclMixin, re2o.mixins.RevMixin, models.Model), ), ] diff --git a/topologie/migrations/0062_auto_20180609_1151.py b/topologie/migrations/0062_auto_20180609_1151.py deleted file mode 100644 index 3f074f7c..00000000 --- a/topologie/migrations/0062_auto_20180609_1151.py +++ /dev/null @@ -1,66 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-06-09 16:51 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('topologie', '0061_portprofile'), - ] - - operations = [ - migrations.AlterModelOptions( - name='portprofile', - options={'permissions': (('view_port_profile', 'Can view a port profile object'),), 'verbose_name': 'Port profile', 'verbose_name_plural': 'Port profiles'}, - ), - migrations.AddField( - model_name='portprofile', - name='name', - field=models.CharField(default='Sans nom', max_length=255, verbose_name='Name'), - preserve_default=False, - ), - migrations.AlterField( - model_name='portprofile', - name='hotspot_default', - field=models.BooleanField(verbose_name='Hotspot default'), - ), - migrations.AlterField( - model_name='portprofile', - name='orga_machine_default', - field=models.BooleanField(verbose_name='Organisation machine default'), - ), - migrations.AlterField( - model_name='portprofile', - name='radius_mode', - field=models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], 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')], max_length=32, verbose_name='RADIUS type'), - ), - migrations.AlterField( - model_name='portprofile', - name='room_default', - field=models.BooleanField(verbose_name='Room default'), - ), - migrations.AlterField( - model_name='portprofile', - name='uplink_default', - field=models.BooleanField(verbose_name='Uplink default'), - ), - migrations.AlterField( - model_name='portprofile', - name='vlan_tagged', - field=models.ManyToManyField(related_name='vlan_tagged', to='machines.Vlan', verbose_name='VLAN(s) tagged'), - ), - migrations.AlterField( - model_name='portprofile', - name='vlan_untagged', - field=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'), - ), - ] diff --git a/topologie/migrations/0063_auto_20180609_1158.py b/topologie/migrations/0063_auto_20180609_1158.py deleted file mode 100644 index 59ea8732..00000000 --- a/topologie/migrations/0063_auto_20180609_1158.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-06-09 16:58 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('topologie', '0062_auto_20180609_1151'), - ] - - operations = [ - migrations.AlterField( - model_name='portprofile', - name='vlan_tagged', - field=models.ManyToManyField(blank=True, related_name='vlan_tagged', to='machines.Vlan', verbose_name='VLAN(s) tagged'), - ), - ] diff --git a/topologie/migrations/0064_auto_20180609_1220.py b/topologie/migrations/0064_auto_20180609_1220.py deleted file mode 100644 index f657a612..00000000 --- a/topologie/migrations/0064_auto_20180609_1220.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-06-09 17:20 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('topologie', '0063_auto_20180609_1158'), - ] - - operations = [ - migrations.AlterField( - model_name='portprofile', - name='radius_mode', - field=models.CharField(blank=True, choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], max_length=32, null=True, verbose_name='RADIUS mode'), - ), - ] diff --git a/topologie/models.py b/topologie/models.py index cdf30787..c7f0e28f 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -489,7 +489,7 @@ class Room(AclMixin, RevMixin, models.Model): return self.name -class PortProfile(AclMixin, models.Model): +class PortProfile(AclMixin, RevMixin, models.Model): """Contains the information of the ports' configuration for a switch""" TYPES = ( ('NO', 'NO'), @@ -500,37 +500,100 @@ class PortProfile(AclMixin, models.Model): ('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'), + ) + PROFIL_DEFAULT= ( + ('room', 'room'), + ('accespoint', 'accesspoint'), + ('uplink', 'uplink'), + ('asso_machine', 'asso_machine'), + ) name = models.CharField(max_length=255, verbose_name=_("Name")) - room_default = models.BooleanField(verbose_name=_("Room default")) - hotspot_default = models.BooleanField(_("Hotspot default")) - uplink_default = models.BooleanField(_("Uplink default")) - orga_machine_default = models.BooleanField(_("Organisation machine" - " default")) + profil_default = models.CharField( + max_length=32, + choices=PROFIL_DEFAULT, + blank=True, + null=True, + unique=True, + verbose_name=_("profil default") + ) vlan_untagged = models.ForeignKey( - 'machines.Vlan', - related_name='vlan_untagged', - on_delete=models.SET_NULL, - blank=True, - null=True, - verbose_name=_("VLAN untagged") + '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', - blank=True, - verbose_name=_("VLAN(s) tagged")) + 'machines.Vlan', + related_name='vlan_tagged', + blank=True, + null=True, + verbose_name=_("VLAN(s) tagged") + ) radius_type = models.CharField( - max_length=32, - choices=TYPES, - verbose_name=_("RADIUS type") + max_length=32, + choices=TYPES, + verbose_name=_("RADIUS type") ) radius_mode = models.CharField( - max_length=32, - choices=MODES, - blank=True, - null=True, - verbose_name=_("RADIUS mode") + max_length=32, + choices=MODES, + default='COMMON', + verbose_name=_("RADIUS mode") ) + speed = models.CharField( + max_length=32, + choices=SPEED, + default='auto', + help_text='Mode de transmission et vitesse du port', + verbose_name=_("Speed") + ) + mac_limit = models.IntegerField( + null=True, + blank=True, + help_text='Limit du nombre de mac sur le port', + verbose_name=_("Mac limit") + ) + flow_control = models.BooleanField( + default=False, + help_text='Gestion des débits', + verbose_name=_("Flow control") + ) + dhcp_snooping = models.BooleanField( + default=False, + help_text='Protection dhcp pirate', + verbose_name=_("Dhcp snooping") + ) + dhcpv6_snooping = models.BooleanField( + default=False, + help_text='Protection dhcpv6 pirate', + verbose_name=_("Dhcpv6 snooping") + ) + arp_protect = models.BooleanField( + default=False, + help_text='Verification assignation de l\'IP par dhcp', + verbose_name=_("Arp protect") + ) + ra_guard = models.BooleanField( + default=False, + help_text='Protection contre ra pirate', + verbose_name=_("Ra guard") + ) + loop_protect = models.BooleanField( + default=False, + help_text='Protection contre les boucles', + verbose_name=_("Loop Protect") + ) class Meta: permissions = ( diff --git a/topologie/templates/topologie/aff_port_profile.html b/topologie/templates/topologie/aff_port_profile.html index b4b936c5..0c1ca622 100644 --- a/topologie/templates/topologie/aff_port_profile.html +++ b/topologie/templates/topologie/aff_port_profile.html @@ -8,13 +8,43 @@ {% endif %} - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% for port_profile in port_profile_list %} From dbf24147bcd0c658e311067e0661f9911d70c6cb Mon Sep 17 00:00:00 2001 From: Charlie Jacomme Date: Tue, 26 Jun 2018 18:59:15 +0200 Subject: [PATCH 12/48] fix mailalias model --- users/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/models.py b/users/models.py index 102ed827..f290bfa8 100644 --- a/users/models.py +++ b/users/models.py @@ -1634,7 +1634,7 @@ class MailAlias(RevMixin, AclMixin, models.Model): help_text="username de l'adresse mail" ) extension = models.ForeignKey( - 'Extension', + 'machines.Extension', on_delete=models.CASCADE, help_text="Extension mail interne" ) From e7b49bd5fa6985423f88549d7724ef273bdda8a1 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 26 Jun 2018 17:12:52 +0000 Subject: [PATCH 13/48] Pas de null sur manytomany --- topologie/migrations/0061_portprofile.py | 2 +- topologie/models.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/topologie/migrations/0061_portprofile.py b/topologie/migrations/0061_portprofile.py index 2b326f28..d0e84021 100644 --- a/topologie/migrations/0061_portprofile.py +++ b/topologie/migrations/0061_portprofile.py @@ -31,7 +31,7 @@ class Migration(migrations.Migration): ('arp_protect', models.BooleanField(default=False, help_text="Verification assignation de l'IP par dhcp", verbose_name='Arp protect')), ('ra_guard', models.BooleanField(default=False, help_text='Protection contre ra pirate', verbose_name='Ra guard')), ('loop_protect', models.BooleanField(default=False, help_text='Protection contre les boucles', verbose_name='Loop Protect')), - ('vlan_tagged', models.ManyToManyField(blank=True, null=True, related_name='vlan_tagged', to='machines.Vlan', verbose_name='VLAN(s) tagged')), + ('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={ diff --git a/topologie/models.py b/topologie/models.py index c7f0e28f..86beed71 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -537,7 +537,6 @@ class PortProfile(AclMixin, RevMixin, models.Model): 'machines.Vlan', related_name='vlan_tagged', blank=True, - null=True, verbose_name=_("VLAN(s) tagged") ) radius_type = models.CharField( From 8105a613243345c6776831fa8b60935aa213011b Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Tue, 26 Jun 2018 22:27:33 +0200 Subject: [PATCH 14/48] Ajout de l'extension mail dans preference et debut de frontend --- machines/forms.py | 1 - machines/models.py | 4 -- .../0035_optionaluser_mail_extension.py | 20 ++++++++ preferences/models.py | 15 +++++- .../preferences/display_preferences.html | 4 ++ users/admin.py | 4 +- users/forms.py | 6 +-- users/migrations/0073_mail_mailalias.py | 38 +++++++++++++++ users/migrations/0074_transfermail.py | 36 +++++++++++++++ users/models.py | 46 +++++++------------ users/views.py | 2 + 11 files changed, 132 insertions(+), 44 deletions(-) create mode 100644 preferences/migrations/0035_optionaluser_mail_extension.py create mode 100644 users/migrations/0073_mail_mailalias.py create mode 100644 users/migrations/0074_transfermail.py diff --git a/machines/forms.py b/machines/forms.py index a4f20a91..eda62c7c 100644 --- a/machines/forms.py +++ b/machines/forms.py @@ -266,7 +266,6 @@ class ExtensionForm(FormRevMixin, ModelForm): self.fields['origin'].label = 'Enregistrement A origin' self.fields['origin_v6'].label = 'Enregistrement AAAA origin' self.fields['soa'].label = 'En-tête SOA à utiliser' - self.fielss['mail_extension'].label = 'Utilisable comme extension mail' class DelExtensionForm(FormRevMixin, Form): diff --git a/machines/models.py b/machines/models.py index b7e829d4..a0a84a8e 100644 --- a/machines/models.py +++ b/machines/models.py @@ -641,10 +641,6 @@ class Extension(RevMixin, AclMixin, models.Model): 'SOA', on_delete=models.CASCADE ) - mail_extension = models.BooleanField( - default=False, - help_text="Determine si l'extension peut être utilisée comme extension mail interne" - ) class Meta: permissions = ( diff --git a/preferences/migrations/0035_optionaluser_mail_extension.py b/preferences/migrations/0035_optionaluser_mail_extension.py new file mode 100644 index 00000000..3d1b42b4 --- /dev/null +++ b/preferences/migrations/0035_optionaluser_mail_extension.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-26 19:31 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0034_auto_20180416_1120'), + ] + + operations = [ + migrations.AddField( + model_name='optionaluser', + name='mail_extension', + field=models.CharField(default='@example.org', help_text='Extension principale pour les mails internes', max_length=32), + ), + ] diff --git a/preferences/models.py b/preferences/models.py index 560fa30f..59b44111 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -31,6 +31,7 @@ from django.db.models.signals import post_save from django.dispatch import receiver from django.core.cache import cache +from django.forms import ValidationError import cotisations.models import machines.models from re2o.mixins import AclMixin @@ -102,6 +103,11 @@ class OptionalUser(AclMixin, PreferencesModel): blank=True, null=True ) + mail_extension = models.CharField( + max_length = 32, + default = "@example.org", + help_text="Extension principale pour les mails internes", + ) class Meta: permissions = ( @@ -109,13 +115,18 @@ class OptionalUser(AclMixin, PreferencesModel): ) def clean(self): - """Creation du mode de paiement par solde""" + """Clean du model: + Creation du mode de paiement par solde + Vérifie que l'extension mail commence bien par @ + """ if self.user_solde: p = cotisations.models.Paiement.objects.filter(moyen="Solde") if not len(p): c = cotisations.models.Paiement(moyen="Solde") c.save() - + if self.mail_extension[0] != "@": + raise ValidationError("L'extension mail doit commencer par un @") + @receiver(post_save, sender=OptionalUser) def optionaluser_post_save(**kwargs): diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 99e3e14f..a5d7c866 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -71,6 +71,10 @@ with this program; if not, write to the Free Software Foundation, Inc., + + + +
{% trans "Name" %} {% trans "VLAN untagged" %} {% trans "VLAN(s) tagged" %}
{% trans "RADIUS type" %} {% trans "RADIUS mode" %}{% trans "RADIUS type" %}
{% trans "speed" %}{% trans "Mac limit" %}{% trans "Flow control" %}
{% trans "dhcp snooping" %}{% trans "dhcpv6 snooping" %}{% trans "arp protect" %}
{% trans "ra guard" %}{% trans "loop protect" %}
Shell par défaut des utilisateurs {{ useroptions.shell_default }}
Extension mail interne{{ useroptions.mail_extension }}

Préférences machines

diff --git a/users/admin.py b/users/admin.py index 5f4c197b..92528455 100644 --- a/users/admin.py +++ b/users/admin.py @@ -127,7 +127,6 @@ class UserAdmin(VersionAdmin, BaseUserAdmin): list_display = ( 'pseudo', 'surname', - 'email', 'school', 'is_admin', 'shell' @@ -141,7 +140,7 @@ class UserAdmin(VersionAdmin, BaseUserAdmin): 'Personal info', { 'fields': - ('surname', 'email', 'school', 'shell', 'uid_number') + ('surname', 'school', 'shell', 'uid_number') } ), ('Permissions', {'fields': ('is_admin', )}), @@ -156,7 +155,6 @@ class UserAdmin(VersionAdmin, BaseUserAdmin): 'fields': ( 'pseudo', 'surname', - 'email', 'school', 'is_admin', 'password1', diff --git a/users/forms.py b/users/forms.py index a14118cb..044c170b 100644 --- a/users/forms.py +++ b/users/forms.py @@ -140,7 +140,7 @@ class UserCreationForm(FormRevMixin, forms.ModelForm): class Meta: model = Adherent - fields = ('pseudo', 'surname', 'email') + fields = ('pseudo', 'surname') def clean_password2(self): """Verifie que password1 et 2 sont identiques""" @@ -220,7 +220,7 @@ class UserChangeForm(FormRevMixin, forms.ModelForm): class Meta: model = Adherent - fields = ('pseudo', 'password', 'surname', 'email') + fields = ('pseudo', 'password', 'surname') def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) @@ -313,7 +313,6 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): 'name', 'surname', 'pseudo', - 'email', 'school', 'comment', 'room', @@ -365,7 +364,6 @@ class ClubForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): fields = [ 'surname', 'pseudo', - 'email', 'school', 'comment', 'room', diff --git a/users/migrations/0073_mail_mailalias.py b/users/migrations/0073_mail_mailalias.py new file mode 100644 index 00000000..ce676464 --- /dev/null +++ b/users/migrations/0073_mail_mailalias.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-26 19:07 +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): + + dependencies = [ + ('users', '0072_auto_20180426_2021'), + ] + + operations = [ + migrations.CreateModel( + name='Mail', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('external_mail', models.EmailField(help_text='Mail externe', max_length=254)), + ('redirection', models.BooleanField(default=False)), + ('internal_address', models.BooleanField(default=False)), + ('user', models.OneToOneField(help_text="Object mail d'un User", on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), + ), + migrations.CreateModel( + name='MailAlias', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('valeur', models.CharField(help_text="username de l'adresse mail", max_length=64, unique=True)), + ('mail', models.ForeignKey(help_text='Objects Mail associé', on_delete=django.db.models.deletion.CASCADE, to='users.Mail')), + ], + bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), + ), + ] diff --git a/users/migrations/0074_transfermail.py b/users/migrations/0074_transfermail.py new file mode 100644 index 00000000..dbf6adca --- /dev/null +++ b/users/migrations/0074_transfermail.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-26 18:34 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0073_mail_mailalias'), + ] + + def create_mail(apps, schema_editor): + db_alias = schema_editor.connection.alias + user = apps.get_model('users','User') + mail = apps.get_model('users','Mail') + mailalias = apps.get_model('users','MailAlias') + users = user.objects.using(db_alias).all() + for us in users: + ma = mail() + ma.user=us + ma.external_mail = us.email + ma.save() + al = mailalias() + al.mail = ma + al.valeur = us.pseudo + al.save() + + def uncreatemail(apps,schema_editor): + pass + + operations = [ + migrations.RunPython(create_mail, uncreatemail) + ] diff --git a/users/models.py b/users/models.py index 89413676..b9a41b81 100644 --- a/users/models.py +++ b/users/models.py @@ -79,7 +79,7 @@ from re2o.field_permissions import FieldPermissionModelMixin from re2o.mixins import AclMixin, RevMixin from cotisations.models import Cotisation, Facture, Paiement, Vente -from machines.models import Domain, Interface, Machine, regen, Extension +from machines.models import Domain, Interface, Machine, regen from preferences.models import GeneralOption, AssoOption, OptionalUser from preferences.models import OptionalMachine, MailMessageOption @@ -134,7 +134,6 @@ class UserManager(BaseUserManager): self, pseudo, surname, - email, password=None, su=False ): @@ -148,7 +147,6 @@ class UserManager(BaseUserManager): pseudo=pseudo, surname=surname, name=surname, - email=self.normalize_email(email), ) user.set_password(password) @@ -157,19 +155,19 @@ class UserManager(BaseUserManager): user.save(using=self._db) return user - def create_user(self, pseudo, surname, email, password=None): + def create_user(self, pseudo, surname, password=None): """ Creates and saves a User with the given pseudo, name, surname, email, and password. """ - return self._create_user(pseudo, surname, email, password, False) + return self._create_user(pseudo, surname, password, False) - def create_superuser(self, pseudo, surname, email, password): + def create_superuser(self, pseudo, surname, password): """ Creates and saves a superuser with the given pseudo, name, surname, email, and password. """ - return self._create_user(pseudo, surname, email, password, True) + return self._create_user(pseudo, surname, password, True) class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, @@ -188,19 +186,13 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, ) surname = models.CharField(max_length=255) + email = models.EmailField() pseudo = models.CharField( max_length=32, unique=True, help_text="Doit contenir uniquement des lettres, chiffres, ou tirets", validators=[linux_user_validator] ) - email = models.EmailField() - """ - email= models.OneToOneField( - Mail, - on_delete=models.PROTECT - ) - """ school = models.ForeignKey( 'School', on_delete=models.PROTECT, @@ -233,7 +225,7 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, ) USERNAME_FIELD = 'pseudo' - REQUIRED_FIELDS = ['surname', 'email'] + REQUIRED_FIELDS = ['surname'] objects = UserManager() @@ -680,10 +672,10 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, """ Return the mail address choosen by the user """ - if not self.mail.internal_activated: - return(self.mail.external) + if not self.mail.internal_address: + return self.mail.external_mail else: - return(self.mail.mailalias_set.first()) + return self.mail.mailalias_set.get(valeur=pseudo) def get_next_domain_name(self): """Look for an available name for a new interface for @@ -1601,7 +1593,7 @@ class Mail(RevMixin, AclMixin, models.Model): Compte mail d'un utilisateur """ external_mail = models.EmailField(help_text="Mail externe") - user = models.ForeignKey( + user = models.OneToOneField( 'User', on_delete=models.CASCADE, help_text="Object mail d'un User" @@ -1614,7 +1606,7 @@ class Mail(RevMixin, AclMixin, models.Model): ) def __str__(self): - return self.mail + return self.user.get_mail() class MailAlias(RevMixin, AclMixin, models.Model): @@ -1629,20 +1621,14 @@ class MailAlias(RevMixin, AclMixin, models.Model): help_text="Objects Mail associé" ) valeur = models.CharField( + unique=True, max_length=64, help_text="username de l'adresse mail" ) - extension = models.ForeignKey( - 'machines.Extension', - on_delete=models.CASCADE, - help_text="Extension mail interne" - ) - class Meta: - unique_together = ('valeur', 'extension',) def __str__(self): - return self.valeur + "@" + self.extension + return self.valeur + OptionalUser.get_cached_value('mail_extension') def can_view(self, user_request, *_args, **_kwargs): """ @@ -1663,7 +1649,7 @@ class MailAlias(RevMixin, AclMixin, models.Model): return True, None else: if user_request == self.mail.user: - if self.id != 0: + if self.valeur == self.mail.user.pseudo: return True, None else: return False, "Vous ne pouvez pas supprimer l'alias lié à votre pseudo" @@ -1679,7 +1665,7 @@ class MailAlias(RevMixin, AclMixin, models.Model): return True, None else: if user_request == self.mail.user: - if self.id != 0: + if self.valeur == self.mail.user.pseudo: return True, None else: return False, "Vous ne pouvez pas modifier l'alias lié à votre pseudo" diff --git a/users/views.py b/users/views.py index 369ec947..6525f8fb 100644 --- a/users/views.py +++ b/users/views.py @@ -80,6 +80,8 @@ from .models import ( Adherent, Club, ListShell, + MailAlias, + Mail, ) from .forms import ( BanForm, From 92f30fbe19cea3d14f5830b68595209949b12f41 Mon Sep 17 00:00:00 2001 From: chirac Date: Tue, 26 Jun 2018 23:29:40 +0000 Subject: [PATCH 15/48] =?UTF-8?q?Ajout=20reglages=20s=C3=A9curit=C3=A9=20+?= =?UTF-8?q?=20frontend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- topologie/forms.py | 43 -------------- .../migrations/0062_auto_20180627_0123.py | 25 ++++++++ topologie/models.py | 8 +++ .../templates/topologie/aff_port_profile.html | 59 +++++++------------ topologie/views.py | 5 +- 5 files changed, 55 insertions(+), 85 deletions(-) create mode 100644 topologie/migrations/0062_auto_20180627_0123.py diff --git a/topologie/forms.py b/topologie/forms.py index 377a39b3..3ed9e8b3 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -265,34 +265,6 @@ class EditBuildingForm(FormRevMixin, ModelForm): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(EditBuildingForm, self).__init__(*args, prefix=prefix, **kwargs) - -class NewPortProfileForm(FormRevMixin, ModelForm): - """Form to create a port profile""" - class Meta: - model = PortProfile - fields = '__all__' - - def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(NewPortProfileForm, self).__init__(*args, - prefix=prefix, - **kwargs) - - def clean(self): - cleaned_data = super(NewPortProfileForm, self).clean() - radius_type = cleaned_data.get('radius_type') - radius_mode = cleaned_data.get('radius_mode') - - if radius_type == 'NO' and radius_mode: - raise forms.ValidationError(_("You can't specify a RADIUS mode" - " with RADIUS type NO")) - elif radius_type != 'NO' and not radius_mode: - raise forms.ValidationError(_("You have to specify a RADIUS" - " mode")) - - return cleaned_data - - class EditPortProfileForm(FormRevMixin, ModelForm): """Form to edit a port profile""" class Meta: @@ -305,18 +277,3 @@ class EditPortProfileForm(FormRevMixin, ModelForm): prefix=prefix, **kwargs) - def clean(self): - cleaned_data = super(EditPortProfileForm, self).clean() - radius_type = cleaned_data.get('radius_type') - radius_mode = cleaned_data.get('radius_mode') - - if radius_type == 'NO' and radius_mode: - raise forms.ValidationError(_("You can't specify a RADIUS mode" - " with RADIUS type NO")) - elif radius_type != 'NO' and not radius_mode: - raise forms.ValidationError(_("You have to specify a RADIUS" - " mode")) - - return cleaned_data - - diff --git a/topologie/migrations/0062_auto_20180627_0123.py b/topologie/migrations/0062_auto_20180627_0123.py new file mode 100644 index 00000000..b8135de8 --- /dev/null +++ b/topologie/migrations/0062_auto_20180627_0123.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-26 23:23 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0061_portprofile'), + ] + + operations = [ + migrations.AlterField( + model_name='portprofile', + name='radius_mode', + field=models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], default='COMMON', help_text="En cas d'auth par mac, auth common ou strcit sur le 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="Choix du type d'authentification radius : non actif, mac ou 802.1X", max_length=32, verbose_name='RADIUS type'), + ), + ] diff --git a/topologie/models.py b/topologie/models.py index 86beed71..d3400e41 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -542,12 +542,14 @@ class PortProfile(AclMixin, RevMixin, models.Model): radius_type = models.CharField( max_length=32, choices=TYPES, + help_text="Choix du type d'authentification radius : non actif, mac ou 802.1X", verbose_name=_("RADIUS type") ) radius_mode = models.CharField( max_length=32, choices=MODES, default='COMMON', + help_text="En cas d'auth par mac, auth common ou strcit sur le port", verbose_name=_("RADIUS mode") ) speed = models.CharField( @@ -601,6 +603,12 @@ class PortProfile(AclMixin, RevMixin, models.Model): verbose_name = _("Port profile") verbose_name_plural = _("Port profiles") + security_parameters_fields = ['loop_protect', 'ra_guard', 'arp_protect', 'dhcpv6_snooping', 'dhcp_snooping', 'flow_control'] + + @cached_property + def security_parameters_enabled(self): + return [parameter for parameter in self.security_parameters_fields if getattr(self, parameter)] + def __str__(self): return self.name diff --git a/topologie/templates/topologie/aff_port_profile.html b/topologie/templates/topologie/aff_port_profile.html index 0c1ca622..7e044c0a 100644 --- a/topologie/templates/topologie/aff_port_profile.html +++ b/topologie/templates/topologie/aff_port_profile.html @@ -9,53 +9,34 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + {% for port_profile in port_profile_list %} - + - - + + {% endif %} + + + - - - + + +
{% trans "Name" %} - {% trans "VLAN untagged" %}{% trans "VLAN(s) tagged" %}
{% trans "RADIUS type" %}{% trans "RADIUS mode" %}{% trans "RADIUS type" %}
{% trans "speed" %}{% trans "Mac limit" %}{% trans "Flow control" %}
{% trans "dhcp snooping" %}{% trans "dhcpv6 snooping" %}{% trans "arp protect" %}
{% trans "ra guard" %}{% trans "loop protect" %}{% trans "Nom" %}{% trans "Default pour" %}{% trans "VLANs" %}{% trans "Réglages RADIUS" %}{% trans "Vitesse" %}{% trans "Mac address limit" %}{% trans "Sécurité" %}
{{port_profile.name}}{{port_profile.vlan_untagged}}{{port_profile.profil_default}} - {{port_profile.vlan_tagged.all|join:", "}} + Untagged : {{port_profile.vlan_untagged}} +
+ Tagged : {{port_profile.vlan_tagged.all|join:", "}}
{{port_profile.radius_type}}{{port_profile.radius_mode}} + Type : {{port_profile.radius_type}} + {% if port_profile.radius_type == "MAC-radius" %} +
+ Mode : {{port_profile.radius_mode}}
{{port_profile.speed}}{{port_profile.mac_limit}}{{port_profile.security_parameters_enabled|join:"
"}}
{% include 'buttons/history.html' with href='topologie:history' name='portprofile' id=port_profile.pk %} {% can_edit port_profile %} diff --git a/topologie/views.py b/topologie/views.py index eb86ce9c..4b21c651 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -97,7 +97,6 @@ from .forms import ( EditAccessPointForm, EditSwitchBayForm, EditBuildingForm, - NewPortProfileForm, EditPortProfileForm, ) @@ -135,7 +134,7 @@ def index(request): switch_list = re2o_paginator(request, switch_list, pagination_number) port_profile_list = re2o_paginator(request, port_profile_list, pagination_number) - if any(service_link.need_regen() for service_link in Service_link.objects.filter(service__service_type='graph_topo')): + if any(service_link.need_regen for service_link in Service_link.objects.filter(service__service_type='graph_topo')): make_machine_graph() for service_link in Service_link.objects.filter(service__service_type='graph_topo'): service_link.done_regen() @@ -967,7 +966,7 @@ def del_constructor_switch(request, constructor_switch, **_kwargs): @can_create(PortProfile) def new_port_profile(request): """Create a new port profile""" - port_profile = NewPortProfileForm(request.POST or None) + port_profile = EditPortProfileForm(request.POST or None) if port_profile.is_valid(): port_profile.save() messages.success(request, _("Port profile created")) From d123ce920d7231f1e3e18a769e9783d2314ec258 Mon Sep 17 00:00:00 2001 From: chirac Date: Wed, 27 Jun 2018 20:28:54 +0000 Subject: [PATCH 16/48] =?UTF-8?q?Menu=20s=C3=A9par=C3=A9=20pour=20les=20pr?= =?UTF-8?q?ofils=20de=20ports?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- topologie/templates/topologie/index.html | 10 ----- .../topologie/index_portprofile.html | 44 +++++++++++++++++++ topologie/templates/topologie/sidebar.html | 6 ++- topologie/urls.py | 3 ++ topologie/views.py | 17 +++++-- 5 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 topologie/templates/topologie/index_portprofile.html diff --git a/topologie/templates/topologie/index.html b/topologie/templates/topologie/index.html index 7902f4a3..7949f412 100644 --- a/topologie/templates/topologie/index.html +++ b/topologie/templates/topologie/index.html @@ -73,14 +73,4 @@ Topologie des Switchs

-

{% trans "Port profiles" %}

-{% can_create PortProfile %} -{% trans " Add a port profile" %} -
-{% acl_end %} - {% include "topologie/aff_port_profile.html" with port_profile_list=port_profile_list %} -
-
-
- {% endblock %} diff --git a/topologie/templates/topologie/index_portprofile.html b/topologie/templates/topologie/index_portprofile.html new file mode 100644 index 00000000..a4287da6 --- /dev/null +++ b/topologie/templates/topologie/index_portprofile.html @@ -0,0 +1,44 @@ +{% extends "topologie/sidebar.html" %} +{% comment %} +Re2o est un logiciel d'administration développé initiallement au rezometz. Il +se veut agnostique au réseau considéré, de manière à être installable en +quelques clics. + +Copyright © 2017 Gabriel Détraz +Copyright © 2017 Goulven Kermarec +Copyright © 2017 Augustin Lemesle + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +{% endcomment %} + +{% load bootstrap3 %} +{% load acl %} +{% load i18n %} + +{% block title %}Switchs{% endblock %} + +{% block content %} + +

{% trans "Port profiles" %}

+{% can_create PortProfile %} +{% trans " Add a port profile" %} +
+{% acl_end %} + {% include "topologie/aff_port_profile.html" with port_profile_list=port_profile_list %} +
+
+
+ +{% endblock %} diff --git a/topologie/templates/topologie/sidebar.html b/topologie/templates/topologie/sidebar.html index ce7b4114..c7ea6337 100644 --- a/topologie/templates/topologie/sidebar.html +++ b/topologie/templates/topologie/sidebar.html @@ -33,7 +33,11 @@ with this program; if not, write to the Free Software Foundation, Inc., Switchs - + + + Profil des ports switchs + + Bornes WiFi diff --git a/topologie/urls.py b/topologie/urls.py index 7cfc5714..a827acf2 100644 --- a/topologie/urls.py +++ b/topologie/urls.py @@ -113,6 +113,9 @@ urlpatterns = [ 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'), diff --git a/topologie/views.py b/topologie/views.py index 4b21c651..b7761e0c 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -128,11 +128,9 @@ def index(request): SortTable.TOPOLOGIE_INDEX ) - port_profile_list = PortProfile.objects.all() pagination_number = GeneralOption.get_cached_value('pagination_number') switch_list = re2o_paginator(request, switch_list, pagination_number) - port_profile_list = re2o_paginator(request, port_profile_list, pagination_number) if any(service_link.need_regen for service_link in Service_link.objects.filter(service__service_type='graph_topo')): make_machine_graph() @@ -144,7 +142,20 @@ def index(request): return render( request, 'topologie/index.html', - {'switch_list': switch_list, 'port_profile_list': port_profile_list} + {'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) + return render( + request, + 'topologie/index_portprofile.html', + {'port_profile_list': port_profile_list} ) From 1f3f557944ebbceb3abb53b80eebdcfe4f1b7e39 Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Wed, 27 Jun 2018 23:13:43 +0200 Subject: [PATCH 17/48] Affichage et modification du compte mail et des alias mail --- users/forms.py | 15 ++++++++++- users/models.py | 12 ++++++++- users/templates/users/aff_alias.html | 5 ++-- users/templates/users/profil.html | 40 ++++++++++++++++++++++++++++ users/urls.py | 1 + users/views.py | 39 +++++++++++++++++++++------ 6 files changed, 99 insertions(+), 13 deletions(-) diff --git a/users/forms.py b/users/forms.py index 044c170b..edcaf569 100644 --- a/users/forms.py +++ b/users/forms.py @@ -53,6 +53,7 @@ from .models import ( School, ListRight, Whitelist, + Mail, MailAlias, ListShell, Ban, @@ -596,8 +597,20 @@ class MailAliasForm(FormRevMixin, ModelForm): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(MailAliasForm, self).__init__(*args, prefix=prefix, **kwargs) self.fields['valeur'].label = 'nom de l\'adresse mail' - self.fields['extension'].label = 'extension de l\'adresse mail' class Meta: model = MailAlias exclude = ['mail'] + +class MailForm(FormRevMixin, ModelForm): + """Creation, edition d'un objet mail""" + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(MailForm, self).__init__(*args, prefix=prefix, **kwargs) + self.fields['external_mail'].label = 'Adresse mail externe' + self.fields['redirection'].label = 'Activation de la redirection vers l\'adress externe' + self.fields['internal_address'].label = 'Adresse mail interne' + + class Meta: + model = Mail + exclude = ['user'] diff --git a/users/models.py b/users/models.py index b9a41b81..233db6a4 100644 --- a/users/models.py +++ b/users/models.py @@ -675,7 +675,7 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, if not self.mail.internal_address: return self.mail.external_mail else: - return self.mail.mailalias_set.get(valeur=pseudo) + return self.mail.mailalias_set.get(valeur=self.pseudo) def get_next_domain_name(self): """Look for an available name for a new interface for @@ -1608,6 +1608,16 @@ class Mail(RevMixin, AclMixin, models.Model): def __str__(self): return self.user.get_mail() + def can_edit(self, user_request, *_args, **_kwargs): + """ + Check if the user can edit the mail + """ + + if user_request.has_perm('users.change_mail') or user_request == self.user: + return True, None + else: + return False, "Vous n'avez pas les droits suffisants et n'êtes pas propriétairs de cet alias" + class MailAlias(RevMixin, AclMixin, models.Model): """ diff --git a/users/templates/users/aff_alias.html b/users/templates/users/aff_alias.html index 59a9b6f1..ec5b7313 100644 --- a/users/templates/users/aff_alias.html +++ b/users/templates/users/aff_alias.html @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., {% if alias_list.paginator %} {% include "pagination.html" with list=alias_list %} {% endif %} - @@ -37,10 +36,10 @@ with this program; if not, write to the Free Software Foundation, Inc., diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index bc0a5ac4..d402e108 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -427,6 +427,46 @@ with this program; if not, write to the Free Software Foundation, Inc., +
+
+

+ + Gestion des mails +

+
+
+
+ {% can_edit user.mail %} + + + Modifier les options mail + + {% acl_end %} +
+
{{ alias }} {% can_delete alias %} - {% include 'buttons/suppr.html' with href='users:del-alias' id=alias.id %} + {% include 'buttons/suppr.html' with href='users:del-mailalias' id=alias.id %} {% acl_end %} {% can_edit alias %} - {% include 'buttons/edit.html' with href='users:edit-alias' id=alias.id %} + {% include 'buttons/edit.html' with href='users:edit-mailalias' id=alias.id %} {% acl_end %} {% include 'buttons/history.html' with href='users:history' name='alias' id=alias.id %}
+ + + + + + + + + +
Adresse mail externeCompte mail {{ asso_name }}Adresse mail de contact
{{ user.mail.external_mail }}{{ user.mail.internal_address|yesno:"Activé,Désactivé" }}{{ user.mail.external_mail }}
+ + {% can_create MailAlias %} + + + Ajouter un alias mail + + {% acl_end %} + {% if alias_list %} + {% include "users/aff_alias.html" with alias_list=alias_list %} + {% endif %} + + +

diff --git a/users/urls.py b/users/urls.py index f27a15c3..d77d015b 100644 --- a/users/urls.py +++ b/users/urls.py @@ -68,6 +68,7 @@ urlpatterns = [ url(r'^add_mailalias/(?P[0-9]+)$', views.add_mailalias, name='add-mailalias'), url(r'^edit_mailalias/(?P[0-9]+)$', views.edit_mailalias, name='edit-mailalias'), url(r'^del-mailalias/(?P[0-9]+)$', views.del_mailalias, name='del-mailalias'), + url(r'^edit_mail/(?P[0-9]+)$', views.edit_mail, name='edit-mail'), url(r'^add_school/$', views.add_school, name='add-school'), url(r'^edit_school/(?P[0-9]+)$', views.edit_school, diff --git a/users/views.py b/users/views.py index 1cf4a4de..1f708bca 100644 --- a/users/views.py +++ b/users/views.py @@ -86,6 +86,8 @@ from .models import ( from .forms import ( BanForm, WhitelistForm, + MailAliasForm, + MailForm, DelSchoolForm, DelListRightForm, NewListRightForm, @@ -503,12 +505,12 @@ def del_whitelist(request, whitelist, **_kwargs): def add_mailalias(request, user, userid): """ Créer un alias """ mailalias_instance = MailAlias(mail=user.mail) - whitelist = WhitelistForm( + mailalias = MailAliasForm( request.POST or None, - instance=whitelist_instance + instance=mailalias_instance ) - if whitelist.is_valid(): - whitelist.save() + if mailalias.is_valid(): + mailalias.save() messages.success(request, "Alias créé") return redirect(reverse( 'users:profil', @@ -529,9 +531,9 @@ def edit_mailalias(request, mailalias_instance, **_kwargs): request.POST or None, instance=mailalias_instance ) - if whitelist.is_valid(): - if whitelist.changed_data: - whitelist.save() + if mailalias.is_valid(): + if mailalias.changed_data: + mailalias.save() messages.success(request, "Alias modifiée") return redirect(reverse('users:index')) return form( @@ -557,6 +559,25 @@ def del_mailalias(request, mailalias, **_kwargs): request ) +@login_required +@can_edit(Mail) +def edit_mail(request, mail_instance, **_kwargs): + """ Editer un compte mail""" + mail = MailForm( + request.POST or None, + instance=mail_instance + ) + if mail.is_valid(): + if mail.changed_data: + mail.save() + messages.success(request, "Compte mail modifiée") + return redirect(reverse('users:index')) + return form( + {'userform': mail, 'action_name': 'Editer un compte mail'}, + 'users/user.html', + request + ) + @login_required @can_create(School) def add_school(request): @@ -957,7 +978,9 @@ def profil(request, users, **_kwargs): 'white_list': whitelists, 'user_solde': user_solde, 'allow_online_payment': allow_online_payment, - 'solde_activated': OptionalUser.objects.first().user_solde + 'solde_activated': OptionalUser.objects.first().user_solde, + 'asso_name': AssoOption.objects.first().name, + 'alias_list': users.mail.mailalias_set.all() } ) From a04a2845571f83d5bba9dd27da1cf21ef24c25a6 Mon Sep 17 00:00:00 2001 From: Maxime Bombar Date: Thu, 28 Jun 2018 19:54:47 +0200 Subject: [PATCH 18/48] [printer] Initial commit. --- printer/__init__.py | 0 printer/admin.py | 3 +++ printer/apps.py | 5 +++++ printer/migrations/__init__.py | 0 printer/models.py | 3 +++ printer/tests.py | 3 +++ printer/urls.py | 3 +++ printer/views.py | 3 +++ 8 files changed, 20 insertions(+) create mode 100644 printer/__init__.py create mode 100644 printer/admin.py create mode 100644 printer/apps.py create mode 100644 printer/migrations/__init__.py create mode 100644 printer/models.py create mode 100644 printer/tests.py create mode 100644 printer/urls.py create mode 100644 printer/views.py diff --git a/printer/__init__.py b/printer/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/printer/admin.py b/printer/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/printer/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/printer/apps.py b/printer/apps.py new file mode 100644 index 00000000..62de6453 --- /dev/null +++ b/printer/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class PrinterConfig(AppConfig): + name = 'printer' diff --git a/printer/migrations/__init__.py b/printer/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/printer/models.py b/printer/models.py new file mode 100644 index 00000000..71a83623 --- /dev/null +++ b/printer/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/printer/tests.py b/printer/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/printer/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/printer/urls.py b/printer/urls.py new file mode 100644 index 00000000..5dcec9cd --- /dev/null +++ b/printer/urls.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +urlpatterns = [] diff --git a/printer/views.py b/printer/views.py new file mode 100644 index 00000000..91ea44a2 --- /dev/null +++ b/printer/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. From 9aa875113e128b6a52329ce2431517e15d263c5a Mon Sep 17 00:00:00 2001 From: Maxime Bombar Date: Thu, 28 Jun 2018 19:59:00 +0200 Subject: [PATCH 19/48] printer/settings.py : Temporary settings for printer app --- printer/settings.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 printer/settings.py diff --git a/printer/settings.py b/printer/settings.py new file mode 100644 index 00000000..97895a3b --- /dev/null +++ b/printer/settings.py @@ -0,0 +1,13 @@ + + + + + +"""printer.settings +Define variables, to be changed into a configuration table. +""" + + + +MAX_PRINTFILE_SIZE = 10 * 1024 * 1024 # 25 MB +ALLOWED_TYPES = ['application/pdf'] From 767ba17fc3f524d706cc2b4eaa3a3fc94cdb2fe7 Mon Sep 17 00:00:00 2001 From: Maxime Bombar Date: Thu, 28 Jun 2018 20:20:08 +0200 Subject: [PATCH 20/48] Printer App a beginning. --- printer/forms.py | 37 ++++++++ printer/models.py | 117 ++++++++++++++++++++++++- printer/templates/printer/echec.html | 12 +++ printer/templates/printer/newjob.html | 87 ++++++++++++++++++ printer/templates/printer/success.html | 12 +++ printer/urls.py | 16 +++- printer/validators.py | 72 +++++++++++++++ printer/views.py | 56 +++++++++++- re2o/settings.py | 1 + re2o/urls.py | 1 + templates/base.html | 6 ++ 11 files changed, 412 insertions(+), 5 deletions(-) create mode 100644 printer/forms.py create mode 100644 printer/templates/printer/echec.html create mode 100644 printer/templates/printer/newjob.html create mode 100644 printer/templates/printer/success.html create mode 100644 printer/validators.py diff --git a/printer/forms.py b/printer/forms.py new file mode 100644 index 00000000..d013a91b --- /dev/null +++ b/printer/forms.py @@ -0,0 +1,37 @@ +# -*- mode: python; coding: utf-8 -*- + +"""printer.forms +Form to add, edit, cancel printer jobs. +Author : Maxime Bombar . +Date : 29/06/2018 +""" + +from django import forms +from django.forms import ( + Form, + ModelForm, +) + +import itertools + +from re2o.mixins import FormRevMixin + +from .models import ( + JobWithOptions, +) + + +class JobForm(FormRevMixin, ModelForm): + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(TrueJobForm, self).__init__(*args, prefix=prefix, **kwargs) + + class Meta: + model = JobWithOptions + fields = [ + 'file', + 'color', + 'disposition', + 'count', + ] + diff --git a/printer/models.py b/printer/models.py index 71a83623..f16e620d 100644 --- a/printer/models.py +++ b/printer/models.py @@ -1,3 +1,116 @@ -from django.db import models +# -*- mode: python; coding: utf-8 -*- -# Create your models here. +"""printer.models +Models of the printer application +Author : Maxime Bombar . +Date : 29/06/2018 +""" + +from __future__ import unicode_literals + +from django.db import models +from django.forms import ValidationError +from django.utils.translation import ugettext_lazy as _ +from django.template.defaultfilters import filesizeformat + +from re2o.mixins import RevMixin + +import users.models + +from .validators import ( + FileValidator, +) + +from .settings import ( + MAX_PRINTFILE_SIZE, + ALLOWED_TYPES, +) + + +""" +- ```user_printing_path``` is a function that returns the path of the uploaded file, used with the FileField. +- ```Job``` is the main model of a printer job. His parent is the ```user``` model. +""" + + +def user_printing_path(instance, filename): + # File will be uploaded to MEDIA_ROOT/printings/user_/ + return 'printings/user_{0}/{1}'.format(instance.user.id, filename) + + +class JobWithOptions(RevMixin, models.Model): + """ + This is the main model of printer application : + + - ```user``` is a ForeignKey to the User Application + - ```file``` is the file to print + - ```starttime``` is the time when the job was launched + - ```endtime``` is the time when the job was stopped. + A job is stopped when it is either finished or cancelled. + - ```status``` can be running, finished or cancelled. + - ```club``` is blank in general. If the job was launched as a club then + it is the id of the club. + - ```price``` is the total price of this printing. + + Printing Options : + + - ```format``` is the paper format. Example: A4. + - ```color``` is the colorization option. Either Color or Greyscale. + - ```disposition``` is the paper disposition. + - ```count``` is the number of copies to be printed. + - ```stapling``` is the stapling options. + - ```perforations``` is the perforation options. + + + Parent class : User + """ + STATUS_AVAILABLE = ( + ('Printable', 'Printable'), + ('Running', 'Running'), + ('Cancelled', 'Cancelled'), + ('Finished', 'Finished') + ) + user = models.ForeignKey('users.User', on_delete=models.PROTECT) + file = models.FileField(upload_to=user_printing_path, validators=[FileValidator(allowed_types=ALLOWED_TYPES, max_size=MAX_PRINTFILE_SIZE)]) + starttime = models.DateTimeField(auto_now_add=True) + endtime = models.DateTimeField(null=True) + status = models.CharField(max_length=255, choices=STATUS_AVAILABLE) + printAs = models.ForeignKey('users.User', on_delete=models.PROTECT, related_name='print_as_user', null=True) + price = models.IntegerField(default=0) + + FORMAT_AVAILABLE = ( + ('A4', 'A4'), + ('A3', 'A4'), + ) + COLOR_CHOICES = ( + ('Greyscale', 'Greyscale'), + ('Color', 'Color') + ) + DISPOSITIONS_AVAILABLE = ( + ('TwoSided', 'Two sided'), + ('OneSided', 'One sided'), + ('Booklet', 'Booklet') + ) + STAPLING_OPTIONS = ( + ('None', 'None'), + ('TopLeft', 'One top left'), + ('TopRight', 'One top right'), + ('LeftSided', 'Two left sided'), + ('RightSided', 'Two right sided') + ) + PERFORATION_OPTIONS = ( + ('None', 'None'), + ('TwoLeftSidedHoles', 'Two left sided holes'), + ('TwoRightSidedHoles', 'Two right sided holes'), + ('TwoTopHoles', 'Two top holes'), + ('TwoBottomHoles', 'Two bottom holes'), + ('FourLeftSidedHoles', 'Four left sided holes'), + ('FourRightSidedHoles', 'Four right sided holes') + ) + + format = models.CharField(max_length=255, choices=FORMAT_AVAILABLE, default='A4') + color = models.CharField(max_length=255, choices=COLOR_CHOICES, default='Greyscale') + disposition = models.CharField(max_length=255, choices=DISPOSITIONS_AVAILABLE, default='TwoSided') + count = models.PositiveIntegerField(default=1) + stapling = models.CharField(max_length=255, choices=STAPLING_OPTIONS, default='None') + perforation = models.CharField(max_length=255, choices=PERFORATION_OPTIONS, default='None') diff --git a/printer/templates/printer/echec.html b/printer/templates/printer/echec.html new file mode 100644 index 00000000..d15907c3 --- /dev/null +++ b/printer/templates/printer/echec.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} +{% load staticfiles %} +{% load i18n %} + +{% load bootstrap3 %} +{% load massive_bootstrap_form %} +{% load static %} +{% block title %}Printing interface{% endblock %} + +{% block content %} +

{% trans "Failure" %}

+{% endblock %} diff --git a/printer/templates/printer/newjob.html b/printer/templates/printer/newjob.html new file mode 100644 index 00000000..1167e18e --- /dev/null +++ b/printer/templates/printer/newjob.html @@ -0,0 +1,87 @@ +{% extends "base.html" %} +{% load staticfiles %} +{% load i18n %} + +{% load bootstrap3 %} +{% load massive_bootstrap_form %} +{% load static %} +{% block title %}Printing interface{% endblock %} + +{% block content %} +
+ {% csrf_token %} +

{% trans "Printing Menu" %}

+ {{ jobform.management_form }} + {% bootstrap_formset_errors jobform %} +
+ {% for job in jobform.forms %} +
+ {% bootstrap_form job label_class='sr-only' %} + +
+ {% endfor %} +
+ + {% bootstrap_button action_name button_type="submit" icon="star" %} +
+ +{% endblock %} + diff --git a/printer/templates/printer/success.html b/printer/templates/printer/success.html new file mode 100644 index 00000000..c7649e0a --- /dev/null +++ b/printer/templates/printer/success.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} +{% load staticfiles %} +{% load i18n %} + +{% load bootstrap3 %} +{% load massive_bootstrap_form %} +{% load static %} +{% block title %}Printing interface{% endblock %} + +{% block content %} +

{% trans "Success" %}

+{% endblock %} diff --git a/printer/urls.py b/printer/urls.py index 5dcec9cd..3629c741 100644 --- a/printer/urls.py +++ b/printer/urls.py @@ -1,3 +1,17 @@ # -*- coding: utf-8 -*- +"""printer.urls +The defined URLs for the printer app +Author : Maxime Bombar . +Date : 29/06/2018 +""" +from __future__ import unicode_literals -urlpatterns = [] +from django.conf.urls import url + +import re2o +from . import views + +urlpatterns = [ + url(r'^new_job/$', views.new_job, name="new-job"), + url(r'^success/$', views.success, name="success"), +] diff --git a/printer/validators.py b/printer/validators.py new file mode 100644 index 00000000..069383cb --- /dev/null +++ b/printer/validators.py @@ -0,0 +1,72 @@ +# -*- mode: python; coding: utf-8 -*- + + +"""printer.validators +Custom validators useful for printer application. +Author : Maxime Bombar . +Date : 29/06/2018 +""" + + + +from django.utils.translation import ugettext_lazy as _ +from django.core.exceptions import ValidationError +from django.template.defaultfilters import filesizeformat +from django.utils.deconstruct import deconstructible + +import mimetypes + +@deconstructible +class FileValidator(object): + """ + Custom validator for files. It checks the size and mimetype. + + Parameters: + * ```allowed_types``` is an iterable of allowed mimetypes. Example: ['application/pdf'] for a pdf file. + * ```max_size``` is the maximum size allowed in bytes. Example: 25*1024*1024 for 25 MB. + + Usage example: + + class UploadModel(models.Model): + file = fileField(..., validators=FileValidator(allowed_types = ['application/pdf'], max_size=25*1024*1024)) + """ + + + def __init__(self, *args, **kwargs): + """ + Initialize the custom validator. + By default, all types and size are allowed. + """ + self.allowed_types = kwargs.pop('allowed_types', None) + self.max_size = kwargs.pop('max_size', None) + + def __call__(self, value): + """ + Check the type and size. + """ + + + type_message = _("MIME type '%(type)s' is not valid. Please, use one of these types: %(allowed_types)s.") + type_code = 'invalidType' + + oversized_message = _('The current file size is %(size)s. The maximum file size is %(max_size)s.') + oversized_code = 'oversized' + + + mimetype = mimetypes.guess_type(value.name)[0] + if self.allowed_types and not (mimetype in self.allowed_types): + type_params = { + 'type': mimetype, + 'allowed_types': ', '.join(self.allowed_types), + } + + raise ValidationError(type_message, code=type_code, params=type_params) + + filesize = len(value) + if self.max_size and filesize > self.max_size: + oversized_params = { + 'size': '{}'.format(filesizeformat(filesize)), + 'max_size': '{}'.format(filesizeformat(self.max_size)), + } + + raise ValidationError(oversized_message, code=oversized_code, params=oversized_params) diff --git a/printer/views.py b/printer/views.py index 91ea44a2..4dced049 100644 --- a/printer/views.py +++ b/printer/views.py @@ -1,3 +1,55 @@ -from django.shortcuts import render +# -*- mode: python; coding: utf-8 -*- -# Create your views here. +"""printer.views +The views for the printer app +Author : Maxime Bombar . +Date : 29/06/2018 +""" + +from __future__ import unicode_literals + +from django.urls import reverse +from django.shortcuts import render, redirect +from django.forms import modelformset_factory, formset_factory + +from re2o.views import form +from users.models import User + +from . import settings + +from .forms import ( + JobForm, + ) + + +def new_job(request): + """ + View to create a new printing job + """ + job_formset = formset_factory(JobForm)( + request.POST or None, request.FILES, + ) + if job_formset.is_valid(): + for job in job_formset: + job = job.save(commit=False) + job.user=request.user + job.status='Printable' + job.save() + return redirect(reverse( + 'printer:success', + )) + return form( + { + 'jobform': job_formset, + 'action_name': "Print", + }, + 'printer/newjob.html', + request + ) + +def success(request): + return form( + {}, + 'printer/success.html', + request + ) diff --git a/re2o/settings.py b/re2o/settings.py index 1117dd77..3025d5cd 100644 --- a/re2o/settings.py +++ b/re2o/settings.py @@ -75,6 +75,7 @@ LOCAL_APPS = ( 're2o', 'preferences', 'logs', + 'printer', ) INSTALLED_APPS = ( DJANGO_CONTRIB_APPS + diff --git a/re2o/urls.py b/re2o/urls.py index 3322e82b..b1a1037c 100644 --- a/re2o/urls.py +++ b/re2o/urls.py @@ -71,6 +71,7 @@ urlpatterns = [ r'^preferences/', include('preferences.urls', namespace='preferences') ), + url(r'^printer/', include('printer.urls', namespace='printer')), ] # Add debug_toolbar URLs if activated if 'debug_toolbar' in settings.INSTALLED_APPS: diff --git a/templates/base.html b/templates/base.html index d6b03798..3755e1c2 100644 --- a/templates/base.html +++ b/templates/base.html @@ -112,6 +112,12 @@ with this program; if not, write to the Free Software Foundation, Inc., {% acl_end %} + {% can_view_app logs %}
  • {% trans "Statistics" %}
  • {% acl_end %} From 14c1b92c3316fd340cef023efc9ab38677f26059 Mon Sep 17 00:00:00 2001 From: Maxime Bombar Date: Thu, 28 Jun 2018 20:35:34 +0200 Subject: [PATCH 21/48] printer/migrations --- printer/migrations/0001_initial.py | 26 ++++++++++ printer/migrations/0002_auto_20180628_2032.py | 48 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 printer/migrations/0001_initial.py create mode 100644 printer/migrations/0002_auto_20180628_2032.py diff --git a/printer/migrations/0001_initial.py b/printer/migrations/0001_initial.py new file mode 100644 index 00000000..55f63a7f --- /dev/null +++ b/printer/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-28 18:30 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Dummy', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/printer/migrations/0002_auto_20180628_2032.py b/printer/migrations/0002_auto_20180628_2032.py new file mode 100644 index 00000000..a9439262 --- /dev/null +++ b/printer/migrations/0002_auto_20180628_2032.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-28 18:32 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import printer.models +import printer.validators +import re2o.mixins + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('printer', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='JobWithOptions', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('file', models.FileField(upload_to=printer.models.user_printing_path, validators=[printer.validators.FileValidator(allowed_types=['application/pdf'], max_size=10485760)])), + ('starttime', models.DateTimeField(auto_now_add=True)), + ('endtime', models.DateTimeField(null=True)), + ('status', models.CharField(choices=[('Printable', 'Printable'), ('Running', 'Running'), ('Cancelled', 'Cancelled'), ('Finished', 'Finished')], max_length=255)), + ('price', models.IntegerField(default=0)), + ('format', models.CharField(choices=[('A4', 'A4'), ('A3', 'A4')], default='A4', max_length=255)), + ('color', models.CharField(choices=[('Greyscale', 'Greyscale'), ('Color', 'Color')], default='Greyscale', max_length=255)), + ('disposition', models.CharField(choices=[('TwoSided', 'Two sided'), ('OneSided', 'One sided'), ('Booklet', 'Booklet')], default='TwoSided', max_length=255)), + ('count', models.PositiveIntegerField(default=1)), + ('stapling', models.CharField(choices=[('None', 'None'), ('TopLeft', 'One top left'), ('TopRight', 'One top right'), ('LeftSided', 'Two left sided'), ('RightSided', 'Two right sided')], default='None', max_length=255)), + ('perforation', models.CharField(choices=[('None', 'None'), ('TwoLeftSidedHoles', 'Two left sided holes'), ('TwoRightSidedHoles', 'Two right sided holes'), ('TwoTopHoles', 'Two top holes'), ('TwoBottomHoles', 'Two bottom holes'), ('FourLeftSidedHoles', 'Four left sided holes'), ('FourRightSidedHoles', 'Four right sided holes')], default='None', max_length=255)), + ('printAs', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='print_as_user', to=settings.AUTH_USER_MODEL)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), + ], + bases=(re2o.mixins.RevMixin, models.Model), + ), + migrations.RemoveField( + model_name='dummy', + name='user', + ), + migrations.DeleteModel( + name='Dummy', + ), + ] From 4894273214035e0ca9bf5d9bfe6b546b258e5bce Mon Sep 17 00:00:00 2001 From: Maxime Bombar Date: Fri, 29 Jun 2018 05:05:15 +0200 Subject: [PATCH 22/48] [Printer] Minor Fixes --- printer/forms.py | 4 ++-- printer/views.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/printer/forms.py b/printer/forms.py index d013a91b..3d60fd38 100644 --- a/printer/forms.py +++ b/printer/forms.py @@ -21,10 +21,10 @@ from .models import ( ) -class JobForm(FormRevMixin, ModelForm): +class JobWithOptionsForm(FormRevMixin, ModelForm): def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(TrueJobForm, self).__init__(*args, prefix=prefix, **kwargs) + super(JobWithOptionsForm, self).__init__(*args, prefix=prefix, **kwargs) class Meta: model = JobWithOptions diff --git a/printer/views.py b/printer/views.py index 4dced049..21d122fc 100644 --- a/printer/views.py +++ b/printer/views.py @@ -18,7 +18,7 @@ from users.models import User from . import settings from .forms import ( - JobForm, + JobWithOptionsForm, ) @@ -26,8 +26,8 @@ def new_job(request): """ View to create a new printing job """ - job_formset = formset_factory(JobForm)( - request.POST or None, request.FILES, + job_formset = formset_factory(JobWithOptionsForm)( + request.POST or None, request.FILES or None, ) if job_formset.is_valid(): for job in job_formset: From 23b0753f8985c2fcf882a9e83ba6059a2d4d452a Mon Sep 17 00:00:00 2001 From: Maxime Bombar Date: Fri, 29 Jun 2018 05:16:31 +0200 Subject: [PATCH 23/48] [printer] Update settings.py (temporary) --- printer/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/printer/settings.py b/printer/settings.py index 97895a3b..c8879bd4 100644 --- a/printer/settings.py +++ b/printer/settings.py @@ -9,5 +9,5 @@ Define variables, to be changed into a configuration table. -MAX_PRINTFILE_SIZE = 10 * 1024 * 1024 # 25 MB +MAX_PRINTFILE_SIZE = 25 * 1024 * 1024 # 25 MB ALLOWED_TYPES = ['application/pdf'] From acc110609982bc3d588c956c0027f4fc03661bf1 Mon Sep 17 00:00:00 2001 From: Maxime Bombar Date: Fri, 29 Jun 2018 05:54:54 +0200 Subject: [PATCH 24/48] Printer is an optional app --- re2o/settings.py | 1 - 1 file changed, 1 deletion(-) diff --git a/re2o/settings.py b/re2o/settings.py index 3025d5cd..1117dd77 100644 --- a/re2o/settings.py +++ b/re2o/settings.py @@ -75,7 +75,6 @@ LOCAL_APPS = ( 're2o', 'preferences', 'logs', - 'printer', ) INSTALLED_APPS = ( DJANGO_CONTRIB_APPS + From 43e2ef6b4d3f863f2d66f048b88b62a643b8a482 Mon Sep 17 00:00:00 2001 From: Maxime Bombar Date: Fri, 29 Jun 2018 06:06:52 +0200 Subject: [PATCH 25/48] Login is required to use the printer --- printer/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/printer/views.py b/printer/views.py index 21d122fc..6767ab9c 100644 --- a/printer/views.py +++ b/printer/views.py @@ -1,5 +1,4 @@ # -*- mode: python; coding: utf-8 -*- - """printer.views The views for the printer app Author : Maxime Bombar . @@ -11,6 +10,7 @@ from __future__ import unicode_literals from django.urls import reverse from django.shortcuts import render, redirect from django.forms import modelformset_factory, formset_factory +from django.contrib.auth.decorators import login_required from re2o.views import form from users.models import User @@ -21,7 +21,7 @@ from .forms import ( JobWithOptionsForm, ) - +@login_required def new_job(request): """ View to create a new printing job From 8e5d897615de8457b229d4038732dc3a822c6fcb Mon Sep 17 00:00:00 2001 From: chirac Date: Fri, 29 Jun 2018 16:36:04 +0200 Subject: [PATCH 26/48] Les champs mails sont dans users --- re2o/templatetags/acl.py | 1 - users/admin.py | 5 +- users/forms.py | 10 ++- users/migrations/0073_auto_20180629_1614.py | 46 +++++++++++++ users/migrations/0073_mail_mailalias.py | 38 ----------- users/migrations/0074_transfermail.py | 36 ----------- users/models.py | 72 +++++++-------------- users/templates/users/profil.html | 10 +-- users/views.py | 11 ++-- 9 files changed, 88 insertions(+), 141 deletions(-) create mode 100644 users/migrations/0073_auto_20180629_1614.py delete mode 100644 users/migrations/0073_mail_mailalias.py delete mode 100644 users/migrations/0074_transfermail.py diff --git a/re2o/templatetags/acl.py b/re2o/templatetags/acl.py index f2eb1062..17a51baf 100644 --- a/re2o/templatetags/acl.py +++ b/re2o/templatetags/acl.py @@ -135,7 +135,6 @@ MODEL_NAME = { 'SwitchBay': topologie.models.SwitchBay, # users 'User': users.models.User, - 'Mail': users.models.Mail, 'MailAlias': users.models.MailAlias, 'Adherent': users.models.Adherent, 'Club': users.models.Club, diff --git a/users/admin.py b/users/admin.py index 92528455..8389c029 100644 --- a/users/admin.py +++ b/users/admin.py @@ -34,7 +34,6 @@ from reversion.admin import VersionAdmin from .models import ( User, - Mail, MailAlias, ServiceUser, School, @@ -127,6 +126,7 @@ class UserAdmin(VersionAdmin, BaseUserAdmin): list_display = ( 'pseudo', 'surname', + 'external_mail', 'school', 'is_admin', 'shell' @@ -140,7 +140,7 @@ class UserAdmin(VersionAdmin, BaseUserAdmin): 'Personal info', { 'fields': - ('surname', 'school', 'shell', 'uid_number') + ('surname', 'external_mail', 'school', 'shell', 'uid_number') } ), ('Permissions', {'fields': ('is_admin', )}), @@ -155,6 +155,7 @@ class UserAdmin(VersionAdmin, BaseUserAdmin): 'fields': ( 'pseudo', 'surname', + 'external_mail', 'school', 'is_admin', 'password1', diff --git a/users/forms.py b/users/forms.py index edcaf569..8631d28b 100644 --- a/users/forms.py +++ b/users/forms.py @@ -53,7 +53,6 @@ from .models import ( School, ListRight, Whitelist, - Mail, MailAlias, ListShell, Ban, @@ -307,7 +306,6 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): self.fields['room'].label = 'Chambre' self.fields['room'].empty_label = "Pas de chambre" self.fields['school'].empty_label = "Séléctionner un établissement" - class Meta: model = Adherent fields = [ @@ -600,10 +598,10 @@ class MailAliasForm(FormRevMixin, ModelForm): class Meta: model = MailAlias - exclude = ['mail'] + exclude = ['user'] class MailForm(FormRevMixin, ModelForm): - """Creation, edition d'un objet mail""" + """Creation, edition des paramètres mail""" def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(MailForm, self).__init__(*args, prefix=prefix, **kwargs) @@ -612,5 +610,5 @@ class MailForm(FormRevMixin, ModelForm): self.fields['internal_address'].label = 'Adresse mail interne' class Meta: - model = Mail - exclude = ['user'] + model = User + fields = ['external_mail', 'redirection', 'internal_address'] diff --git a/users/migrations/0073_auto_20180629_1614.py b/users/migrations/0073_auto_20180629_1614.py new file mode 100644 index 00000000..681cb722 --- /dev/null +++ b/users/migrations/0073_auto_20180629_1614.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-29 14:14 +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): + + dependencies = [ + ('users', '0072_auto_20180426_2021'), + ] + + operations = [ + migrations.CreateModel( + name='MailAlias', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('valeur', models.CharField(help_text="username de l'adresse mail", max_length=64, unique=True)), + ], + bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), + ), + migrations.RenameField( + model_name='user', + old_name='email', + new_name='external_mail', + ), + migrations.AddField( + model_name='user', + name='internal_address', + field=models.BooleanField(default=False, help_text="Activer ou non l'utilisation de l'adresse mail interne"), + ), + migrations.AddField( + model_name='user', + name='redirection', + field=models.BooleanField(default=False, help_text='Activer ou non la redirection du mail interne vers le mail externe'), + ), + migrations.AddField( + model_name='mailalias', + name='user', + field=models.ForeignKey(blank=True, help_text='Utilisateur associé', null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/users/migrations/0073_mail_mailalias.py b/users/migrations/0073_mail_mailalias.py deleted file mode 100644 index ce676464..00000000 --- a/users/migrations/0073_mail_mailalias.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-06-26 19:07 -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): - - dependencies = [ - ('users', '0072_auto_20180426_2021'), - ] - - operations = [ - migrations.CreateModel( - name='Mail', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('external_mail', models.EmailField(help_text='Mail externe', max_length=254)), - ('redirection', models.BooleanField(default=False)), - ('internal_address', models.BooleanField(default=False)), - ('user', models.OneToOneField(help_text="Object mail d'un User", on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), - ), - migrations.CreateModel( - name='MailAlias', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('valeur', models.CharField(help_text="username de l'adresse mail", max_length=64, unique=True)), - ('mail', models.ForeignKey(help_text='Objects Mail associé', on_delete=django.db.models.deletion.CASCADE, to='users.Mail')), - ], - bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), - ), - ] diff --git a/users/migrations/0074_transfermail.py b/users/migrations/0074_transfermail.py deleted file mode 100644 index dbf6adca..00000000 --- a/users/migrations/0074_transfermail.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-06-26 18:34 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0073_mail_mailalias'), - ] - - def create_mail(apps, schema_editor): - db_alias = schema_editor.connection.alias - user = apps.get_model('users','User') - mail = apps.get_model('users','Mail') - mailalias = apps.get_model('users','MailAlias') - users = user.objects.using(db_alias).all() - for us in users: - ma = mail() - ma.user=us - ma.external_mail = us.email - ma.save() - al = mailalias() - al.mail = ma - al.valeur = us.pseudo - al.save() - - def uncreatemail(apps,schema_editor): - pass - - operations = [ - migrations.RunPython(create_mail, uncreatemail) - ] diff --git a/users/models.py b/users/models.py index 233db6a4..7da3e195 100644 --- a/users/models.py +++ b/users/models.py @@ -134,6 +134,7 @@ class UserManager(BaseUserManager): self, pseudo, surname, + external_mail, password=None, su=False ): @@ -147,6 +148,7 @@ class UserManager(BaseUserManager): pseudo=pseudo, surname=surname, name=surname, + external_mail=external_mail, ) user.set_password(password) @@ -155,19 +157,19 @@ class UserManager(BaseUserManager): user.save(using=self._db) return user - def create_user(self, pseudo, surname, password=None): + def create_user(self, pseudo, surname, external_mail, password=None): """ Creates and saves a User with the given pseudo, name, surname, email, and password. """ - return self._create_user(pseudo, surname, password, False) + return self._create_user(pseudo, surname, external_mail, password, False) - def create_superuser(self, pseudo, surname, password): + def create_superuser(self, pseudo, surname, external_mail, password): """ Creates and saves a superuser with the given pseudo, name, surname, email, and password. """ - return self._create_user(pseudo, surname, password, True) + return self._create_user(pseudo, surname, external_mail, password, True) class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, @@ -186,13 +188,21 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, ) surname = models.CharField(max_length=255) - email = models.EmailField() pseudo = models.CharField( max_length=32, unique=True, help_text="Doit contenir uniquement des lettres, chiffres, ou tirets", validators=[linux_user_validator] ) + external_mail = models.EmailField() + redirection = models.BooleanField( + default=False, + help_text='Activer ou non la redirection du mail interne vers le mail externe' + ) + internal_address = models.BooleanField( + default=False, + help_text='Activer ou non l\'utilisation de l\'adresse mail interne' + ) school = models.ForeignKey( 'School', on_delete=models.PROTECT, @@ -225,7 +235,7 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, ) USERNAME_FIELD = 'pseudo' - REQUIRED_FIELDS = ['surname'] + REQUIRED_FIELDS = ['surname', 'external_mail'] objects = UserManager() @@ -519,7 +529,7 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, user_ldap.sn = self.pseudo user_ldap.dialupAccess = str(self.has_access()) user_ldap.home_directory = '/home/' + self.pseudo - user_ldap.mail = self.email + user_ldap.mail = self.get_mail() user_ldap.given_name = self.surname.lower() + '_'\ + self.name.lower()[:3] user_ldap.gid = LDAP['user_gid'] @@ -672,10 +682,10 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, """ Return the mail address choosen by the user """ - if not self.mail.internal_address: - return self.mail.external_mail + if not self.internal_address: + return self.external_mail else: - return self.mail.mailalias_set.get(valeur=self.pseudo) + return self.mailalias_set.get(valeur=self.pseudo) def get_next_domain_name(self): """Look for an available name for a new interface for @@ -1586,49 +1596,18 @@ class LdapServiceUserGroup(ldapdb.models.Model): return self.name -class Mail(RevMixin, AclMixin, models.Model): - """ - Mail account of a user - - Compte mail d'un utilisateur - """ - external_mail = models.EmailField(help_text="Mail externe") - user = models.OneToOneField( - 'User', - on_delete=models.CASCADE, - help_text="Object mail d'un User" - ) - redirection = models.BooleanField( - default=False - ) - internal_address = models.BooleanField( - default=False - ) - - def __str__(self): - return self.user.get_mail() - - def can_edit(self, user_request, *_args, **_kwargs): - """ - Check if the user can edit the mail - """ - - if user_request.has_perm('users.change_mail') or user_request == self.user: - return True, None - else: - return False, "Vous n'avez pas les droits suffisants et n'êtes pas propriétairs de cet alias" - - class MailAlias(RevMixin, AclMixin, models.Model): """ Define a alias for a user Mail Définit un aliase pour un Mail d'utilisateur """ - mail = models.ForeignKey( - 'Mail', + user = models.ForeignKey( + User, on_delete=models.CASCADE, - help_text="Objects Mail associé" + help_text="Utilisateur associé", + null=True, + blank=True ) valeur = models.CharField( unique=True, @@ -1636,7 +1615,6 @@ class MailAlias(RevMixin, AclMixin, models.Model): help_text="username de l'adresse mail" ) - def __str__(self): return self.valeur + OptionalUser.get_cached_value('mail_extension') diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index d402e108..770ee8fa 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -436,8 +436,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    - {% can_edit user.mail %} - + {% can_edit user %} + Modifier les options mail @@ -450,9 +450,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    Adresse mail de contact
    {{ user.mail.external_mail }}{{ user.mail.internal_address|yesno:"Activé,Désactivé" }}{{ user.mail.external_mail }}{{ user.external_mail }}{{ user.internal_address|yesno:"Activé,Désactivé" }}{{ user.get_mail }}
    {% can_create MailAlias %} diff --git a/users/views.py b/users/views.py index 1f708bca..198070c5 100644 --- a/users/views.py +++ b/users/views.py @@ -81,7 +81,6 @@ from .models import ( Club, ListShell, MailAlias, - Mail, ) from .forms import ( BanForm, @@ -504,7 +503,7 @@ def del_whitelist(request, whitelist, **_kwargs): @can_edit(User) def add_mailalias(request, user, userid): """ Créer un alias """ - mailalias_instance = MailAlias(mail=user.mail) + mailalias_instance = MailAlias(user=user) mailalias = MailAliasForm( request.POST or None, instance=mailalias_instance @@ -560,12 +559,12 @@ def del_mailalias(request, mailalias, **_kwargs): ) @login_required -@can_edit(Mail) -def edit_mail(request, mail_instance, **_kwargs): +@can_edit(User) +def edit_mail(request, user_instance, **_kwargs): """ Editer un compte mail""" mail = MailForm( request.POST or None, - instance=mail_instance + instance=user_instance ) if mail.is_valid(): if mail.changed_data: @@ -980,7 +979,7 @@ def profil(request, users, **_kwargs): 'allow_online_payment': allow_online_payment, 'solde_activated': OptionalUser.objects.first().user_solde, 'asso_name': AssoOption.objects.first().name, - 'alias_list': users.mail.mailalias_set.all() + 'alias_list': users.mailalias_set.all() } ) From 58a495db54d7681a36925e3b0374c68ac2872b55 Mon Sep 17 00:00:00 2001 From: chirac Date: Fri, 29 Jun 2018 17:45:21 +0200 Subject: [PATCH 27/48] =?UTF-8?q?Fix=20erreurs,=20cr=C3=A9ation=20alias=20?= =?UTF-8?q?mails?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- users/forms.py | 2 +- users/migrations/0074_auto_20180629_1717.py | 27 +++++++++++++++++++++ users/models.py | 23 +++++++++--------- users/urls.py | 2 +- users/views.py | 8 +++--- 5 files changed, 46 insertions(+), 16 deletions(-) create mode 100644 users/migrations/0074_auto_20180629_1717.py diff --git a/users/forms.py b/users/forms.py index 8631d28b..4d6334ff 100644 --- a/users/forms.py +++ b/users/forms.py @@ -594,7 +594,7 @@ class MailAliasForm(FormRevMixin, ModelForm): def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(MailAliasForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['valeur'].label = 'nom de l\'adresse mail' + self.fields['valeur'].label = "Prefixe de l'alias mail. Ne peut contenir de @" class Meta: model = MailAlias diff --git a/users/migrations/0074_auto_20180629_1717.py b/users/migrations/0074_auto_20180629_1717.py new file mode 100644 index 00000000..446a2cdd --- /dev/null +++ b/users/migrations/0074_auto_20180629_1717.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-29 15:17 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0073_auto_20180629_1614'), + ] + + operations = [ + migrations.AlterField( + model_name='mailalias', + name='user', + field=models.ForeignKey(help_text='Utilisateur associé', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='mailalias', + name='valeur', + field=models.CharField(help_text="Valeur de l'alias mail", max_length=64, unique=True), + ), + ] diff --git a/users/models.py b/users/models.py index 7da3e195..28ca0ce8 100644 --- a/users/models.py +++ b/users/models.py @@ -51,7 +51,7 @@ import datetime from django.db import models from django.db.models import Q -from django import forms +from django.forms import ValidationError from django.db.models.signals import post_save, post_delete from django.dispatch import receiver from django.utils.functional import cached_property @@ -97,7 +97,7 @@ def linux_user_validator(login): """ Retourne une erreur de validation si le login ne respecte pas les contraintes unix (maj, min, chiffres ou tiret)""" if not linux_user_check(login): - raise forms.ValidationError( + raise ValidationError( ", ce pseudo ('%(label)s') contient des carractères interdits", params={'label': login}, ) @@ -1606,13 +1606,11 @@ class MailAlias(RevMixin, AclMixin, models.Model): User, on_delete=models.CASCADE, help_text="Utilisateur associé", - null=True, - blank=True ) valeur = models.CharField( unique=True, max_length=64, - help_text="username de l'adresse mail" + help_text="Valeur de l'alias mail" ) def __str__(self): @@ -1623,7 +1621,7 @@ class MailAlias(RevMixin, AclMixin, models.Model): Check if the user can view the aliases """ - if user_request.has_perm('users.view_mailalias') or user.request == self.mail.user: + if user_request.has_perm('users.view_mailalias') or user.request == self.user: return True, None else: return False, "Vous n'avais pas les droits suffisants et n'êtes pas propriétaire de ces alias" @@ -1636,8 +1634,8 @@ class MailAlias(RevMixin, AclMixin, models.Model): if user_request.has_perm('users.delete_mailalias'): return True, None else: - if user_request == self.mail.user: - if self.valeur == self.mail.user.pseudo: + if user_request == self.user: + if self.valeur != self.user.pseudo: return True, None else: return False, "Vous ne pouvez pas supprimer l'alias lié à votre pseudo" @@ -1652,13 +1650,16 @@ class MailAlias(RevMixin, AclMixin, models.Model): if user_request.has_perm('users.change_mailalias'): return True, None else: - if user_request == self.mail.user: - if self.valeur == self.mail.user.pseudo: + if user_request == self.user: + if self.valeur != self.user.pseudo: return True, None else: return False, "Vous ne pouvez pas modifier l'alias lié à votre pseudo" else: return False, "Vous n'avez pas les droits suffisants et n'êtes pas propriétairs de cet alias" - + def clean(self, *args, **kwargs): + if "@" in self.valeur: + raise ValidationError("Cet alias ne peut contenir un @") + super(MailAlias, self).clean(*args, **kwargs) diff --git a/users/urls.py b/users/urls.py index d77d015b..007eb940 100644 --- a/users/urls.py +++ b/users/urls.py @@ -68,7 +68,7 @@ urlpatterns = [ url(r'^add_mailalias/(?P[0-9]+)$', views.add_mailalias, name='add-mailalias'), url(r'^edit_mailalias/(?P[0-9]+)$', views.edit_mailalias, name='edit-mailalias'), url(r'^del-mailalias/(?P[0-9]+)$', views.del_mailalias, name='del-mailalias'), - url(r'^edit_mail/(?P[0-9]+)$', views.edit_mail, name='edit-mail'), + url(r'^edit_mail/(?P[0-9]+)$', views.edit_mail, name='edit-mail'), url(r'^add_school/$', views.add_school, name='add-school'), url(r'^edit_school/(?P[0-9]+)$', views.edit_school, diff --git a/users/views.py b/users/views.py index 198070c5..88f42353 100644 --- a/users/views.py +++ b/users/views.py @@ -515,7 +515,6 @@ def add_mailalias(request, user, userid): 'users:profil', kwargs={'userid': str(userid)} )) - return form( {'userform': mailalias, 'action_name': 'Ajouter un alias mail'}, 'users/user.html', @@ -534,7 +533,10 @@ def edit_mailalias(request, mailalias_instance, **_kwargs): if mailalias.changed_data: mailalias.save() messages.success(request, "Alias modifiée") - return redirect(reverse('users:index')) + return redirect(reverse( + 'users:profil', + kwargs={'userid': str(mailalias_instance.user.id)} + )) return form( {'userform': mailalias, 'action_name': 'Editer un alias mail'}, 'users/user.html', @@ -550,7 +552,7 @@ def del_mailalias(request, mailalias, **_kwargs): messages.success(request, "L'alias a été supprimé") return redirect(reverse( 'users:profil', - kwargs={'userid': str(mailalias.mail.user.id)} + kwargs={'userid': str(mailalias.user.id)} )) return form( {'objet': mailalias, 'objet_name': 'mailalias'}, From 754475760605f054a8348c2c25eb367659991b70 Mon Sep 17 00:00:00 2001 From: chirac Date: Fri, 29 Jun 2018 18:20:25 +0200 Subject: [PATCH 28/48] Boolean compte mail actif/inactif --- .../0036_optionaluser_mail_accounts.py | 20 +++++++++++++++++++ preferences/models.py | 6 +++++- .../preferences/display_preferences.html | 16 ++++++++++----- 3 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 preferences/migrations/0036_optionaluser_mail_accounts.py diff --git a/preferences/migrations/0036_optionaluser_mail_accounts.py b/preferences/migrations/0036_optionaluser_mail_accounts.py new file mode 100644 index 00000000..c3bf4fa8 --- /dev/null +++ b/preferences/migrations/0036_optionaluser_mail_accounts.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-29 16:01 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0035_optionaluser_mail_extension'), + ] + + operations = [ + migrations.AddField( + model_name='optionaluser', + name='mail_accounts', + field=models.BooleanField(default=False, help_text='Activation des comptes mails pour les utilisateurs'), + ), + ] diff --git a/preferences/models.py b/preferences/models.py index 59b44111..0f4d21ba 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -103,11 +103,15 @@ class OptionalUser(AclMixin, PreferencesModel): blank=True, null=True ) + mail_accounts = models.BooleanField( + default=False, + help_text="Activation des comptes mails pour les utilisateurs" + ) mail_extension = models.CharField( max_length = 32, default = "@example.org", help_text="Extension principale pour les mails internes", - ) + ) class Meta: permissions = ( diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 2e289858..1489c930 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    +
    Généralités
    @@ -55,11 +56,13 @@ with this program; if not, write to the Free Software Foundation, Inc., - +
    Téléphone obligatoirement requisCreations de clubs par tous {{ useroptions.all_can_create_club }}
    +
    Gestion du solde {% if useroptions.user_solde %}Activé{% else %} Désactivé{% endif%}
    + - {% if useroptions.user_solde %} + @@ -69,14 +72,17 @@ with this program; if not, write to the Free Software Foundation, Inc., - {% else %} - - {% endif%} +
    Activation du solde pour les utilisateurs {{ useroptions.user_solde }}Solde négatif {{ useroptions.solde_negatif }}
    Montant minimal de rechargement en ligne {{ useroptions.min_online_payment }}
    +
    Comptes mails {% if useroptions.mail_accounts %}Activé{% else %} Désactivé{% endif%}
    + + +
    Gestion des comptes mails{{ useroptions.mail_accounts }} Extension mail interne {{ useroptions.mail_extension }}
    +

    Préférences machines

    From cd03f37a834fed7e724000526972c876c1397538 Mon Sep 17 00:00:00 2001 From: chirac Date: Fri, 29 Jun 2018 18:48:27 +0200 Subject: [PATCH 29/48] Affichage des champs supp de mail que si compte mails actifs --- users/forms.py | 8 +++++--- users/models.py | 30 +++++++++++++++++++++++++++++- users/views.py | 8 ++++++-- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/users/forms.py b/users/forms.py index 4d6334ff..40e2d800 100644 --- a/users/forms.py +++ b/users/forms.py @@ -600,14 +600,16 @@ class MailAliasForm(FormRevMixin, ModelForm): model = MailAlias exclude = ['user'] -class MailForm(FormRevMixin, ModelForm): +class MailForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): """Creation, edition des paramètres mail""" def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(MailForm, self).__init__(*args, prefix=prefix, **kwargs) self.fields['external_mail'].label = 'Adresse mail externe' - self.fields['redirection'].label = 'Activation de la redirection vers l\'adress externe' - self.fields['internal_address'].label = 'Adresse mail interne' + if 'redirection' in self.fields: + self.fields['redirection'].label = 'Activation de la redirection vers l\'adress externe' + if 'internal_address' in self.fields: + self.fields['internal_address'].label = 'Adresse mail interne' class Meta: model = User diff --git a/users/models.py b/users/models.py index 28ca0ce8..3eb3bbaf 100644 --- a/users/models.py +++ b/users/models.py @@ -682,7 +682,7 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, """ Return the mail address choosen by the user """ - if not self.internal_address: + if not OptionalUser.get_cached_value('mail_accounts') or not self.internal_address: return self.external_mail else: return self.mailalias_set.get(valeur=self.pseudo) @@ -805,6 +805,32 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, "Droit requis pour changer le shell" ) + @staticmethod + def can_change_redirection(user_request, *_args, **_kwargs): + """ Check if a user can change redirection. + + :param user_request: The user who request + :returns: a message and a boolean which is True if the user has + the right to change a redirection + """ + return ( + OptionalUser.get_cached_value('mail_accounts'), + "La gestion des comptes mails doit être activée" + ) + + @staticmethod + def can_change_internal_address(user_request, *_args, **_kwargs): + """ Check if a user can change internal address . + + :param user_request: The user who request + :returns: a message and a boolean which is True if the user has + the right to change internal address + """ + return ( + OptionalUser.get_cached_value('mail_accounts'), + "La gestion des comptes mails doit être activée" + ) + @staticmethod def can_change_force(user_request, *_args, **_kwargs): """ Check if a user can change a force @@ -899,6 +925,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, 'shell': self.can_change_shell, 'force': self.can_change_force, 'selfpasswd': self.check_selfpasswd, + 'redirection': self.can_change_redirection, + 'internal_address' : self.can_change_internal_address, } def __str__(self): diff --git a/users/views.py b/users/views.py index 88f42353..6d75f7b6 100644 --- a/users/views.py +++ b/users/views.py @@ -566,13 +566,17 @@ def edit_mail(request, user_instance, **_kwargs): """ Editer un compte mail""" mail = MailForm( request.POST or None, - instance=user_instance + instance=user_instance, + user=request.user ) if mail.is_valid(): if mail.changed_data: mail.save() messages.success(request, "Compte mail modifiée") - return redirect(reverse('users:index')) + return redirect(reverse( + 'users:profil', + kwargs={'userid': str(user_instance.id)} + )) return form( {'userform': mail, 'action_name': 'Editer un compte mail'}, 'users/user.html', From ecfae508fceb4ea9b3697042ef2c13d5015de339 Mon Sep 17 00:00:00 2001 From: chirac Date: Fri, 29 Jun 2018 18:53:39 +0200 Subject: [PATCH 30/48] Fix compte mail , get_mail si redirection --- users/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/users/models.py b/users/models.py index 3eb3bbaf..f9cf68ca 100644 --- a/users/models.py +++ b/users/models.py @@ -682,10 +682,10 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, """ Return the mail address choosen by the user """ - if not OptionalUser.get_cached_value('mail_accounts') or not self.internal_address: - return self.external_mail + if not OptionalUser.get_cached_value('mail_accounts') or not self.internal_address or self.redirection: + return str(self.external_mail) else: - return self.mailalias_set.get(valeur=self.pseudo) + return str(self.mailalias_set.get(valeur=self.pseudo)) def get_next_domain_name(self): """Look for an available name for a new interface for From 081ae1e8ddd85b603b3275ef7266deb6d8dd5c7c Mon Sep 17 00:00:00 2001 From: chirac Date: Fri, 29 Jun 2018 19:28:55 +0200 Subject: [PATCH 31/48] Initialise les alias pour les bases existantes --- users/migrations/0074_auto_20180629_1717.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/users/migrations/0074_auto_20180629_1717.py b/users/migrations/0074_auto_20180629_1717.py index 446a2cdd..92c53a90 100644 --- a/users/migrations/0074_auto_20180629_1717.py +++ b/users/migrations/0074_auto_20180629_1717.py @@ -13,6 +13,19 @@ class Migration(migrations.Migration): ('users', '0073_auto_20180629_1614'), ] + def transfer_pseudo(apps, schema_editor): + db_alias = schema_editor.connection.alias + users = apps.get_model("users", "User") + mailalias = apps.get_model("users", "MailAlias") + users_list = users.objects.using(db_alias).all() + for user in users_list: + mailalias.objects.using(db_alias).create(valeur=user.pseudo, user=user) + + def untransfer_pseudo(apps, schema_editor): + db_alias = schema_editor.connection.alias + mailalias = apps.get_model("users", "MailAlias") + mailalias.objects.using(db_alias).delete() + operations = [ migrations.AlterField( model_name='mailalias', @@ -24,4 +37,5 @@ class Migration(migrations.Migration): name='valeur', field=models.CharField(help_text="Valeur de l'alias mail", max_length=64, unique=True), ), + migrations.RunPython(transfer_pseudo, untransfer_pseudo), ] From c942d87256f286fbed7ee85dd32377a725fc47c2 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 30 Jun 2018 02:53:19 +0200 Subject: [PATCH 32/48] =?UTF-8?q?Sync=20du=20pseudo=20et=20mailalias=20?= =?UTF-8?q?=C3=A0=20la=20creation=20de=20l'user?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- users/models.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/users/models.py b/users/models.py index f9cf68ca..d51aba95 100644 --- a/users/models.py +++ b/users/models.py @@ -929,6 +929,12 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, 'internal_address' : self.can_change_internal_address, } + 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 MailAlias.objects.filter(valeur=self.pseudo).exclude(user=self): + raise ValidationError("Ce pseudo est déjà utilisé") + def __str__(self): return self.pseudo @@ -1051,9 +1057,11 @@ class Club(User): @receiver(post_save, sender=User) 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'] + mail_alias, created = MailAlias.objects.get_or_create(valeur=user.pseudo, user=user) if is_created: user.notif_inscription() user.ldap_sync( From 857e20045a82ecdf13f1f6534a5333e7ed6336f2 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 30 Jun 2018 03:22:46 +0200 Subject: [PATCH 33/48] Augmente la valeur de l'alias --- users/migrations/0074_auto_20180629_1717.py | 2 +- users/models.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/users/migrations/0074_auto_20180629_1717.py b/users/migrations/0074_auto_20180629_1717.py index 92c53a90..7574ea09 100644 --- a/users/migrations/0074_auto_20180629_1717.py +++ b/users/migrations/0074_auto_20180629_1717.py @@ -35,7 +35,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='mailalias', name='valeur', - field=models.CharField(help_text="Valeur de l'alias mail", max_length=64, unique=True), + field=models.CharField(help_text="Valeur de l'alias mail", max_length=128, unique=True), ), migrations.RunPython(transfer_pseudo, untransfer_pseudo), ] diff --git a/users/models.py b/users/models.py index d51aba95..6cf51c09 100644 --- a/users/models.py +++ b/users/models.py @@ -938,7 +938,6 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, def __str__(self): return self.pseudo - class Adherent(User): """ A class representing a member (it's a user with special informations) """ @@ -1645,7 +1644,7 @@ class MailAlias(RevMixin, AclMixin, models.Model): ) valeur = models.CharField( unique=True, - max_length=64, + max_length=128, help_text="Valeur de l'alias mail" ) From 5177ab2b38ca1867c997bdd377319e2adfa2073a Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 30 Jun 2018 03:25:46 +0200 Subject: [PATCH 34/48] Legacy shaebang pour python2 --- api/acl.py | 1 + api/authentication.py | 2 ++ api/pagination.py | 1 + api/permissions.py | 1 + api/routers.py | 1 + api/serializers.py | 1 + api/settings.py | 1 + api/tests.py | 1 + api/urls.py | 1 + api/views.py | 1 + 10 files changed, 11 insertions(+) diff --git a/api/acl.py b/api/acl.py index 8c39aed0..4d634beb 100644 --- a/api/acl.py +++ b/api/acl.py @@ -1,3 +1,4 @@ +# -*- 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. diff --git a/api/authentication.py b/api/authentication.py index 469c51f1..0198c4ce 100644 --- a/api/authentication.py +++ b/api/authentication.py @@ -1,3 +1,4 @@ +# -*- 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. @@ -43,6 +44,7 @@ class ExpiringTokenAuthentication(TokenAuthentication): ) utc_now = datetime.datetime.now(datetime.timezone.utc) if token.created < utc_now - token_duration: + raise ValueError('boom') raise exceptions.AuthenticationFailed(_('Token has expired')) return (token.user, token) diff --git a/api/pagination.py b/api/pagination.py index 20dcad6e..d34c17ab 100644 --- a/api/pagination.py +++ b/api/pagination.py @@ -1,3 +1,4 @@ +# -*- 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. diff --git a/api/permissions.py b/api/permissions.py index 53f06620..0b666ebd 100644 --- a/api/permissions.py +++ b/api/permissions.py @@ -1,3 +1,4 @@ +# -*- 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. diff --git a/api/routers.py b/api/routers.py index fcfb5077..2d245382 100644 --- a/api/routers.py +++ b/api/routers.py @@ -1,3 +1,4 @@ +# -*- 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. diff --git a/api/serializers.py b/api/serializers.py index d507efab..bff1bd9c 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -1,3 +1,4 @@ +# -*- 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. diff --git a/api/settings.py b/api/settings.py index f8171638..925d503a 100644 --- a/api/settings.py +++ b/api/settings.py @@ -1,3 +1,4 @@ +# -*- 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. diff --git a/api/tests.py b/api/tests.py index ef05cec2..0931ab8e 100644 --- a/api/tests.py +++ b/api/tests.py @@ -1,3 +1,4 @@ +# -*- 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. diff --git a/api/urls.py b/api/urls.py index bde01dc1..2947850e 100644 --- a/api/urls.py +++ b/api/urls.py @@ -1,3 +1,4 @@ +# -*- 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. diff --git a/api/views.py b/api/views.py index e239ff71..45e083cc 100644 --- a/api/views.py +++ b/api/views.py @@ -1,3 +1,4 @@ +# -*- 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. From fcbb91667681df842deec128195183bd10272879 Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Sat, 30 Jun 2018 14:14:53 +0200 Subject: [PATCH 35/48] frontend sur l'affichage des options mail --- .../preferences/display_preferences.html | 4 +- users/templates/users/profil.html | 53 +++++++++++++------ users/views.py | 7 +-- 3 files changed, 42 insertions(+), 22 deletions(-) diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 1489c930..7e402cef 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -57,7 +57,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {{ useroptions.all_can_create_club }} -
    Gestion du solde {% if useroptions.user_solde %}Activé{% else %} Désactivé{% endif%}
    +
    {% if useroptions.user_solde %}Gestion du solde{% else %}Gesion du solde{% endif%}
    @@ -73,7 +73,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    Activation du solde pour les utilisateurs{{ useroptions.min_online_payment }}
    -
    Comptes mails {% if useroptions.mail_accounts %}Activé{% else %} Désactivé{% endif%}
    +
    {% if useroptions.mail_accounts %}Comptes mails{% else %}Comptes mails{% endif%}
    diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index 770ee8fa..3940e490 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -429,10 +429,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    -

    +

    - Gestion des mails -

    + Paramètres mail +
    @@ -442,19 +442,25 @@ with this program; if not, write to the Free Software Foundation, Inc., Modifier les options mail {% acl_end %} -
    -
    Gestion des comptes mails
    - - - - - - - - - -
    Adresse mail externeCompte mail {{ asso_name }}Adresse mail de contact
    {{ user.external_mail }}{{ user.internal_address|yesno:"Activé,Désactivé" }}{{ user.get_mail }}
    -
    + {% if mail_accounts %} +
    + + + + + + + + + + +
    Adresse mail externeCompte mail {{ asso_name }}Adresse mail de contact
    {{ user.external_mail }}{{ user.internal_address|yesno:"Activé,Désactivé" }}{{ user.get_mail }}
    + +
    + {% if user.internal_address %} {% can_create MailAlias %}
    @@ -464,7 +470,20 @@ with this program; if not, write to the Free Software Foundation, Inc., {% if alias_list %} {% include "users/aff_alias.html" with alias_list=alias_list %} {% endif %} -
    + {% endif %} + {% else %} +
    + + + + + + + +
    Adresse mail
    {{ user.external_mail }}
    +
    + {% endif %} +
    diff --git a/users/views.py b/users/views.py index 6d75f7b6..731a56df 100644 --- a/users/views.py +++ b/users/views.py @@ -572,13 +572,13 @@ def edit_mail(request, user_instance, **_kwargs): if mail.is_valid(): if mail.changed_data: mail.save() - messages.success(request, "Compte mail modifiée") + messages.success(request, "Option mail modifiée") return redirect(reverse( 'users:profil', kwargs={'userid': str(user_instance.id)} )) return form( - {'userform': mail, 'action_name': 'Editer un compte mail'}, + {'userform': mail, 'action_name': 'Editer les options mail'}, 'users/user.html', request ) @@ -985,7 +985,8 @@ def profil(request, users, **_kwargs): 'allow_online_payment': allow_online_payment, 'solde_activated': OptionalUser.objects.first().user_solde, 'asso_name': AssoOption.objects.first().name, - 'alias_list': users.mailalias_set.all() + 'alias_list': users.mailalias_set.all(), + 'mail_accounts': OptionalUser.objects.first().mail_accounts } ) From 0348eefedaf1a005b934fc0a5d84cdb47d2c5699 Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Sat, 30 Jun 2018 14:46:35 +0200 Subject: [PATCH 36/48] ajout du nombre d'alias max --- .../0037_optionaluser_max_mail_alias.py | 20 +++++++++++++++++++ preferences/models.py | 6 +++++- .../preferences/display_preferences.html | 4 ++++ users/models.py | 17 ++++++++++++++++ users/templates/users/profil.html | 14 ++++++------- 5 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 preferences/migrations/0037_optionaluser_max_mail_alias.py diff --git a/preferences/migrations/0037_optionaluser_max_mail_alias.py b/preferences/migrations/0037_optionaluser_max_mail_alias.py new file mode 100644 index 00000000..8d6ca609 --- /dev/null +++ b/preferences/migrations/0037_optionaluser_max_mail_alias.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-30 12:32 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0036_optionaluser_mail_accounts'), + ] + + operations = [ + migrations.AddField( + model_name='optionaluser', + name='max_mail_alias', + field=models.IntegerField(default=15, help_text="Nombre maximal d'alias pour un utilisateur lambda"), + ), + ] diff --git a/preferences/models.py b/preferences/models.py index 0f4d21ba..be392b72 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -111,7 +111,11 @@ class OptionalUser(AclMixin, PreferencesModel): max_length = 32, default = "@example.org", help_text="Extension principale pour les mails internes", - ) + ) + max_mail_alias = models.IntegerField( + default = 15, + help_text = "Nombre maximal d'alias pour un utilisateur lambda" + ) class Meta: permissions = ( diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 7e402cef..71bf60f8 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -81,6 +81,10 @@ with this program; if not, write to the Free Software Foundation, Inc., Extension mail interne {{ useroptions.mail_extension }} + + Nombre d'alias maximum + {{ useroption.max_mail_alias }} +

    Préférences machines

    diff --git a/users/models.py b/users/models.py index 6cf51c09..d31f0e59 100644 --- a/users/models.py +++ b/users/models.py @@ -1651,6 +1651,23 @@ class MailAlias(RevMixin, AclMixin, models.Model): def __str__(self): return self.valeur + OptionalUser.get_cached_value('mail_extension') + @staticmethod + def can_create(user_request, userid, *_args, **_kwargs): + """Check if an user can create an mailalias object. + + :param user_request: The user who wants to create a mailalias object. + :return: a message and a boolean which is True if the user can create + an user or if the `options.all_can_create` is set. + """ + if not user_request.has_perm('users.add_mailalias'): + if int(user_request.id) != int(userid): + return False, 'Vous n\'avez pas le droit d\'ajouter un alias à une autre personne' + elif user_request.mailalias_set.all().count() >= OptionalUser.get_cached_value('max_mail_alias'): + return False, "Vous avez atteint la limite de {} alias".format(OptionalUser.get_cached_value('max_mail_alias')) + else: + return True, None + return True, None + def can_view(self, user_request, *_args, **_kwargs): """ Check if the user can view the aliases diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index 3940e490..62bbf1e5 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -436,7 +436,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    From 299453b5bba5edd7605d33895fbbb3549a8d23bd Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 30 Jun 2018 15:18:08 +0200 Subject: [PATCH 37/48] Serialisation des alias mail --- api/serializers.py | 17 ++++++++++++++++- api/urls.py | 1 + api/views.py | 7 +++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/api/serializers.py b/api/serializers.py index bff1bd9c..8f7c8035 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -485,10 +485,12 @@ class UserSerializer(NamespacedHMSerializer): """ access = serializers.BooleanField(source='has_access') uid = serializers.IntegerField(source='uid_number') + email = serializers.CharField(source='get_mail') class Meta: model = users.User fields = ('name', 'pseudo', 'email', 'school', 'shell', 'comment', + 'external_mail', 'redirection', 'internal_address', 'state', 'registered', 'telephone', 'solde', 'access', 'end_access', 'uid', 'class_name', 'api_url') extra_kwargs = { @@ -502,10 +504,12 @@ class ClubSerializer(NamespacedHMSerializer): name = serializers.CharField(source='surname') access = serializers.BooleanField(source='has_access') uid = serializers.IntegerField(source='uid_number') + email = serializers.CharField(source='get_mail') class Meta: model = users.Club fields = ('name', 'pseudo', 'email', 'school', 'shell', 'comment', + 'external_mail', 'redirection', 'internal_address', 'state', 'registered', 'telephone', 'solde', 'room', 'access', 'end_access', 'administrators', 'members', 'mailing', 'uid', 'api_url') @@ -519,10 +523,12 @@ class AdherentSerializer(NamespacedHMSerializer): """ access = serializers.BooleanField(source='has_access') uid = serializers.IntegerField(source='uid_number') + email = serializers.CharField(source='get_mail') class Meta: model = users.Adherent - fields = ('name', 'surname', 'pseudo', 'email', 'school', 'shell', + fields = ('name', 'surname', 'pseudo', 'email', 'redirection', 'internal_address', + 'external_mail', 'school', 'shell', 'comment', 'state', 'registered', 'telephone', 'room', 'solde', 'access', 'end_access', 'uid', 'api_url') extra_kwargs = { @@ -586,6 +592,15 @@ class WhitelistSerializer(NamespacedHMSerializer): fields = ('user', 'raison', 'date_start', 'date_end', 'active', 'api_url') +class MailAliasSerializer(NamespacedHMSerializer): + """Serialize `users.models.MailAlias` objects. + """ + + class Meta: + model = users.MailAlias + fields = ('user', 'valeur', 'complete_mail') + + # SERVICE REGEN diff --git a/api/urls.py b/api/urls.py index 2947850e..6003284b 100644 --- a/api/urls.py +++ b/api/urls.py @@ -92,6 +92,7 @@ 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/mailalias', views.MailAliasViewSet) # SERVICE REGEN router.register_viewset(r'services/regen', views.ServiceRegenViewSet, base_name='serviceregen') # DHCP diff --git a/api/views.py b/api/views.py index 45e083cc..60be3b46 100644 --- a/api/views.py +++ b/api/views.py @@ -462,6 +462,13 @@ class WhitelistViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = serializers.WhitelistSerializer +class MailAliasViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `users.models.MailAlias` objects. + """ + queryset = users.MailAlias.objects.all() + serializer_class = serializers.MailAliasSerializer + + # SERVICE REGEN From 447919a2afc0e8aa43621e0ce26914e3705f7444 Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 30 Jun 2018 15:29:00 +0000 Subject: [PATCH 38/48] =?UTF-8?q?Ajout=20et=20transfert=20des=20anciennes?= =?UTF-8?q?=20donn=C3=A9es=20vers=20le=20nouveau=20syst=C3=A8me=20de=20pro?= =?UTF-8?q?fil=20de=20ports?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- topologie/forms.py | 7 +-- topologie/migrations/0061_portprofile.py | 2 +- .../migrations/0063_port_custom_profil.py | 21 +++++++ topologie/migrations/0064_createprofil.py | 57 +++++++++++++++++++ .../migrations/0065_auto_20180630_1703.py | 23 ++++++++ topologie/models.py | 32 +++++++---- topologie/templates/topologie/aff_port.html | 6 +- 7 files changed, 129 insertions(+), 19 deletions(-) create mode 100644 topologie/migrations/0063_port_custom_profil.py create mode 100644 topologie/migrations/0064_createprofil.py create mode 100644 topologie/migrations/0065_auto_20180630_1703.py diff --git a/topologie/forms.py b/topologie/forms.py index 3ed9e8b3..6e4d5a0d 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -80,8 +80,8 @@ class EditPortForm(FormRevMixin, ModelForm): optimiser le temps de chargement avec select_related (vraiment lent sans)""" class Meta(PortForm.Meta): - fields = ['room', 'related', 'machine_interface', 'radius', - 'vlan_force', 'details'] + fields = ['room', 'related', 'machine_interface', 'custom_profil', + 'details'] def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) @@ -109,8 +109,7 @@ class AddPortForm(FormRevMixin, ModelForm): 'room', 'machine_interface', 'related', - 'radius', - 'vlan_force', + 'custom_profil', 'details' ] diff --git a/topologie/migrations/0061_portprofile.py b/topologie/migrations/0061_portprofile.py index d0e84021..7e130163 100644 --- a/topologie/migrations/0061_portprofile.py +++ b/topologie/migrations/0061_portprofile.py @@ -20,7 +20,7 @@ class Migration(migrations.Migration): 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')], max_length=32, null=True, unique=True, verbose_name='profil default')), + ('profil_default', models.CharField(blank=True, choices=[('room', 'room'), ('nothing', 'nothing'), ('accespoint', 'accesspoint'), ('uplink', 'uplink'), ('asso_machine', 'asso_machine')], 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')], max_length=32, verbose_name='RADIUS type')), ('radius_mode', models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], default='COMMON', 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='Mode de transmission et vitesse du port', max_length=32, verbose_name='Speed')), diff --git a/topologie/migrations/0063_port_custom_profil.py b/topologie/migrations/0063_port_custom_profil.py new file mode 100644 index 00000000..15feebce --- /dev/null +++ b/topologie/migrations/0063_port_custom_profil.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-28 07:49 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0062_auto_20180627_0123'), + ] + + operations = [ + migrations.AddField( + model_name='port', + name='custom_profil', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='topologie.PortProfile'), + ), + ] diff --git a/topologie/migrations/0064_createprofil.py b/topologie/migrations/0064_createprofil.py new file mode 100644 index 00000000..189d1812 --- /dev/null +++ b/topologie/migrations/0064_createprofil.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-12-31 19:53 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0063_port_custom_profil'), + ] + + def transfer_profil(apps, schema_editor): + db_alias = schema_editor.connection.alias + port = apps.get_model("topologie", "Port") + 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') + for vlan_instance in vlan.objects.using(db_alias).all(): + if port.objects.using(db_alias).filter(vlan_force=vlan_instance): + custom_profil = 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_profil=custom_profil) + 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_profil=common_profil) + port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=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_profil=strict_profil) + port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=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_profil=strict_profil) + port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=common_profil) + profil_room.save() + + + + def untransfer_profil(apps, schema_editor): + return + + operations = [ + migrations.RunPython(transfer_profil, untransfer_profil), + ] diff --git a/topologie/migrations/0065_auto_20180630_1703.py b/topologie/migrations/0065_auto_20180630_1703.py new file mode 100644 index 00000000..9fed2d83 --- /dev/null +++ b/topologie/migrations/0065_auto_20180630_1703.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-30 15:03 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0064_createprofil'), + ] + + operations = [ + migrations.RemoveField( + model_name='port', + name='radius', + ), + migrations.RemoveField( + model_name='port', + name='vlan_force', + ), + ] diff --git a/topologie/models.py b/topologie/models.py index d3400e41..82c6833f 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -366,12 +366,6 @@ class Port(AclMixin, RevMixin, models.Model): de forcer un port sur un vlan particulier. S'additionne à la politique RADIUS""" PRETTY_NAME = "Port de switch" - STATES = ( - ('NO', 'NO'), - ('STRICT', 'STRICT'), - ('BLOQ', 'BLOQ'), - ('COMMON', 'COMMON'), - ) switch = models.ForeignKey( 'Switch', @@ -397,13 +391,13 @@ class Port(AclMixin, RevMixin, models.Model): blank=True, related_name='related_port' ) - radius = models.CharField(max_length=32, choices=STATES, default='NO') - vlan_force = models.ForeignKey( - 'machines.Vlan', - on_delete=models.SET_NULL, + custom_profil = models.ForeignKey( + 'PortProfile', + on_delete=models.PROTECT, blank=True, null=True ) + details = models.CharField(max_length=255, blank=True) class Meta: @@ -412,6 +406,23 @@ class Port(AclMixin, RevMixin, models.Model): ("view_port", "Peut voir un objet port"), ) + @cached_property + def get_port_profil(self): + """Return the config profil for this port""" + if self.custom_profil: + return custom_profil + elif self.related: + return PortProfil.objects.get(profil_default='uplink') + elif self.machine_interface: + if isinstance(self.machine_interface.machine, AccessPoint): + return PortProfil.objects.get(profil_default='access_point') + else: + return PortProfil.objects.get(profil_default='asso_machine') + elif self.room: + return PortProfil.objects.get(profil_default='room') + else: + return PortProfil.objects.get(profil_default='nothing') + @classmethod def get_instance(cls, portid, *_args, **kwargs): return (cls.objects @@ -515,6 +526,7 @@ class PortProfile(AclMixin, RevMixin, models.Model): ('accespoint', 'accesspoint'), ('uplink', 'uplink'), ('asso_machine', 'asso_machine'), + ('nothing', 'nothing'), ) name = models.CharField(max_length=255, verbose_name=_("Name")) profil_default = models.CharField( diff --git a/topologie/templates/topologie/aff_port.html b/topologie/templates/topologie/aff_port.html index deeb0655..f22ce4c9 100644 --- a/topologie/templates/topologie/aff_port.html +++ b/topologie/templates/topologie/aff_port.html @@ -32,8 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% include "buttons/sort.html" with prefix='port' col='room' text='Room' %} {% include "buttons/sort.html" with prefix='port' col='interface' text='Interface machine' %} {% include "buttons/sort.html" with prefix='port' col='related' text='Related' %} - {% include "buttons/sort.html" with prefix='port' col='radius' text='Radius' %} - {% include "buttons/sort.html" with prefix='port' col='vlan' text='Vlan forcé' %} + Profil du port Détails @@ -66,8 +65,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% acl_end %} {% endif %} - {{ port.radius }} - {% if not port.vlan_force %}Aucun{% else %}{{ port.vlan_force }}{% endif %} + {% if not port.custom_profil %}Par défaut{% else %}{{port.custom_profil}}{% endif %} {{ port.details }} From e6b8c5c899277d80972452e9c48d457a5729d716 Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 30 Jun 2018 16:19:02 +0000 Subject: [PATCH 39/48] =?UTF-8?q?Finition,=20gestion=20du=20renvoie=20du?= =?UTF-8?q?=20profil=20par=20d=C3=A9faut=20du=20port?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- search/views.py | 4 +-- topologie/models.py | 30 ++++++++++++++------- topologie/templates/topologie/aff_port.html | 2 +- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/search/views.py b/search/views.py index 871515fa..0ae8470b 100644 --- a/search/views.py +++ b/search/views.py @@ -262,9 +262,9 @@ def search_single_word(word, filters, user, ) | Q( related__switch__interface__domain__name__icontains=word ) | Q( - radius__icontains=word + custom_profil__name__icontains=word ) | Q( - vlan_force__name__icontains=word + custom_profil__profil_default__icontains=word ) | Q( details__icontains=word ) diff --git a/topologie/models.py b/topologie/models.py index 82c6833f..3d30af76 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -409,19 +409,29 @@ class Port(AclMixin, RevMixin, models.Model): @cached_property def get_port_profil(self): """Return the config profil for this port""" - if self.custom_profil: - return custom_profil - elif self.related: - return PortProfil.objects.get(profil_default='uplink') - elif self.machine_interface: - if isinstance(self.machine_interface.machine, AccessPoint): - return PortProfil.objects.get(profil_default='access_point') + def profil_or_nothing(profil): + port_profil = PortProfile.objects.filter(profil_default=profil).first() + if port_profil: + return port_profil else: - return PortProfil.objects.get(profil_default='asso_machine') + nothing = PortProfile.objects.filter(profil_default='nothing').first() + if not nothing: + nothing = PortProfile.objects.create(profil_default='nothing', name='nothing', radius_type='NO') + return nothing + + if self.custom_profil: + return self.custom_profil + elif self.related: + return profil_or_nothing('uplink') + elif self.machine_interface: + if hasattr(self.machine_interface.machine, 'accesspoint'): + return profil_or_nothing('access_point') + else: + return profil_or_nothing('asso_machine') elif self.room: - return PortProfil.objects.get(profil_default='room') + return profil_or_nothing('room') else: - return PortProfil.objects.get(profil_default='nothing') + return profil_or_nothing('nothing') @classmethod def get_instance(cls, portid, *_args, **kwargs): diff --git a/topologie/templates/topologie/aff_port.html b/topologie/templates/topologie/aff_port.html index f22ce4c9..f878365a 100644 --- a/topologie/templates/topologie/aff_port.html +++ b/topologie/templates/topologie/aff_port.html @@ -65,7 +65,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% acl_end %} {% endif %} - {% if not port.custom_profil %}Par défaut{% else %}{{port.custom_profil}}{% endif %} + {% if not port.custom_profil %}Par défaut : {% endif %}{{port.get_port_profil}} {{ port.details }} From b2d45d002118458bc60fc42f16072d735ffa13f3 Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 30 Jun 2018 16:36:14 +0000 Subject: [PATCH 40/48] =?UTF-8?q?Adapte=20freeradius=20pour=20le=20nouveau?= =?UTF-8?q?=20syst=C3=A8me=20de=20profil=20de=20ports?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- freeradius_utils/auth.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index fcf55cfa..4e9ad24e 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -359,23 +359,26 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, if not port: return (sw_name, "Chambre inconnue", u'Port inconnu', VLAN_OK) + # On récupère le profil du port + port_profil = port.get_port_profil + # Si un vlan a été précisé, on l'utilise pour VLAN_OK - if port.vlan_force: - DECISION_VLAN = int(port.vlan_force.vlan_id) + if port_profil.vlan_untagged: + DECISION_VLAN = int(port_profil.vlan_untagged.vlan_id) extra_log = u"Force sur vlan " + str(DECISION_VLAN) else: DECISION_VLAN = VLAN_OK - if port.radius == 'NO': + if port_profil.radius_type == 'NO': return (sw_name, "", u"Pas d'authentification sur ce port" + extra_log, DECISION_VLAN) - if port.radius == 'BLOQ': + if port_profil.radius_type == 'BLOQ': return (sw_name, port.room, u'Port desactive', VLAN_NOK) - if port.radius == 'STRICT': + if port_profil.radius_type == 'STRICT': room = port.room if not room: return (sw_name, "Inconnue", u'Chambre inconnue', VLAN_NOK) @@ -390,7 +393,7 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, return (sw_name, room, u'Chambre resident desactive', VLAN_NOK) # else: user OK, on passe à la verif MAC - if port.radius == 'COMMON' or port.radius == 'STRICT': + if port_profil.radius_type == 'COMMON' or port_profil.radius_type == 'STRICT': # Authentification par mac interface = (Interface.objects .filter(mac_address=mac_address) From 51793bdee62ba6fbbf7f5feab6d73386886e5110 Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 30 Jun 2018 17:04:15 +0000 Subject: [PATCH 41/48] =?UTF-8?q?Boolean=20direct=20pour=20d=C3=A9sactiver?= =?UTF-8?q?=20un=20port=20+=20logo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- freeradius_utils/auth.py | 6 ++--- topologie/forms.py | 3 ++- .../migrations/0066_auto_20180630_1855.py | 25 +++++++++++++++++++ topologie/models.py | 6 ++++- topologie/templates/topologie/aff_port.html | 2 ++ topologie/templates/topologie/sidebar.html | 4 +-- 6 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 topologie/migrations/0066_auto_20180630_1855.py diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index 4e9ad24e..b318ab63 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -369,15 +369,15 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, else: DECISION_VLAN = VLAN_OK + if not port.state: + return (sw_name, port.room, u'Port desactive', VLAN_NOK) + if port_profil.radius_type == 'NO': return (sw_name, "", u"Pas d'authentification sur ce port" + extra_log, DECISION_VLAN) - if port_profil.radius_type == 'BLOQ': - return (sw_name, port.room, u'Port desactive', VLAN_NOK) - if port_profil.radius_type == 'STRICT': room = port.room if not room: diff --git a/topologie/forms.py b/topologie/forms.py index 6e4d5a0d..63ffc652 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -81,7 +81,7 @@ class EditPortForm(FormRevMixin, ModelForm): lent sans)""" class Meta(PortForm.Meta): fields = ['room', 'related', 'machine_interface', 'custom_profil', - 'details'] + 'state', 'details'] def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) @@ -110,6 +110,7 @@ class AddPortForm(FormRevMixin, ModelForm): 'machine_interface', 'related', 'custom_profil', + 'state', 'details' ] diff --git a/topologie/migrations/0066_auto_20180630_1855.py b/topologie/migrations/0066_auto_20180630_1855.py new file mode 100644 index 00000000..b197f568 --- /dev/null +++ b/topologie/migrations/0066_auto_20180630_1855.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-30 16:55 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0065_auto_20180630_1703'), + ] + + operations = [ + migrations.AddField( + model_name='port', + name='state', + field=models.BooleanField(default=True, help_text='Etat du port Actif', verbose_name='Etat du port Actif'), + ), + 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='profil default'), + ), + ] diff --git a/topologie/models.py b/topologie/models.py index 3d30af76..d0534dda 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -397,7 +397,11 @@ class Port(AclMixin, RevMixin, models.Model): blank=True, null=True ) - + state = models.BooleanField( + default=True, + help_text='Etat du port Actif', + verbose_name=_("Etat du port Actif") + ) details = models.CharField(max_length=255, blank=True) class Meta: diff --git a/topologie/templates/topologie/aff_port.html b/topologie/templates/topologie/aff_port.html index f878365a..45e81e06 100644 --- a/topologie/templates/topologie/aff_port.html +++ b/topologie/templates/topologie/aff_port.html @@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% include "buttons/sort.html" with prefix='port' col='room' text='Room' %} {% include "buttons/sort.html" with prefix='port' col='interface' text='Interface machine' %} {% include "buttons/sort.html" with prefix='port' col='related' text='Related' %} + Etat du port Profil du port Détails @@ -65,6 +66,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% acl_end %} {% endif %} + {% if port.state %} Actif{% else %}Désactivé{% endif %} {% if not port.custom_profil %}Par défaut : {% endif %}{{port.get_port_profil}} {{ port.details }} diff --git a/topologie/templates/topologie/sidebar.html b/topologie/templates/topologie/sidebar.html index c7ea6337..8652690e 100644 --- a/topologie/templates/topologie/sidebar.html +++ b/topologie/templates/topologie/sidebar.html @@ -34,8 +34,8 @@ with this program; if not, write to the Free Software Foundation, Inc., Switchs - - Profil des ports switchs + + Config des ports switchs From 97eb06533759ec8307a985177307e9d7d2a5e2c8 Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Sat, 30 Jun 2018 19:19:40 +0200 Subject: [PATCH 42/48] =?UTF-8?q?Cr=C3=A9ation,=20modification,=20suppress?= =?UTF-8?q?ion=20d'adresses=20mail=20de=20contact?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- preferences/admin.py | 7 ++ preferences/forms.py | 30 ++++++- preferences/models.py | 23 +++++ .../preferences/aff_mailcontact.html | 45 ++++++++++ .../preferences/display_preferences.html | 10 ++- preferences/urls.py | 9 +- preferences/views.py | 83 ++++++++++++++++++- re2o/templatetags/acl.py | 1 + 8 files changed, 201 insertions(+), 7 deletions(-) create mode 100644 preferences/templates/preferences/aff_mailcontact.html diff --git a/preferences/admin.py b/preferences/admin.py index 296dc57c..043370db 100644 --- a/preferences/admin.py +++ b/preferences/admin.py @@ -34,6 +34,7 @@ from .models import ( OptionalTopologie, GeneralOption, Service, + MailContact, AssoOption, MailMessageOption, HomeOption @@ -65,6 +66,11 @@ class ServiceAdmin(VersionAdmin): pass +class MailContactAdmin(VersionAdmin): + """Class admin gestion des adresses mail de contact""" + pass + + class AssoOptionAdmin(VersionAdmin): """Class admin options de l'asso""" pass @@ -86,5 +92,6 @@ admin.site.register(OptionalTopologie, OptionalTopologieAdmin) admin.site.register(GeneralOption, GeneralOptionAdmin) admin.site.register(HomeOption, HomeOptionAdmin) admin.site.register(Service, ServiceAdmin) +admin.site.register(MailContact, MailContactAdmin) admin.site.register(AssoOption, AssoOptionAdmin) admin.site.register(MailMessageOption, MailMessageOptionAdmin) diff --git a/preferences/forms.py b/preferences/forms.py index afe111a2..6f727f3a 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -35,7 +35,8 @@ from .models import ( AssoOption, MailMessageOption, HomeOption, - Service + Service, + MailContact ) class EditOptionalUserForm(ModelForm): @@ -233,3 +234,30 @@ class DelServiceForm(Form): self.fields['services'].queryset = instances else: self.fields['services'].queryset = Service.objects.all() + +class MailContactForm(ModelForm): + """Edition, ajout d'adresse de contact""" + class Meta: + model = MailContact + fields = '__all__' + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(MailContactForm, self).__init__(*args, prefix=prefix, **kwargs) + + +class DelMailContactForm(Form): + """Suppression d'adresse de contact""" + mailcontacts = forms.ModelMultipleChoiceField( + queryset=MailContact.objects.none(), + label="Enregistrements adresses actuels", + widget=forms.CheckboxSelectMultiple + ) + + def __init__(self, *args, **kwargs): + instances = kwargs.pop('instances', None) + super(DelMailContactForm, self).__init__(*args, **kwargs) + if instances: + self.fields['mailcontacts'].queryset = instances + else: + self.fields['mailcontacts'].queryset = MailContact.objects.all() diff --git a/preferences/models.py b/preferences/models.py index be392b72..5c0dd5de 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -292,6 +292,29 @@ class Service(AclMixin, models.Model): def __str__(self): return str(self.name) +class MailContact(AclMixin, models.Model): + """Addresse mail de contact associée à un commentaire descriptif""" + + address = models.EmailField( + default = "contact@example.org", + help_text = "Adresse mail de contact" + ) + + commentary = models.CharField( + blank = True, + null = True, + help_text = "Description de l'utilisation de l'adresse mail associée", + max_length = 256 + ) + + class Meta: + permissions = ( + ("view_mailcontact", "Peut voir les mails de contact"), + ) + + def __str__(self): + return(self.address) + class AssoOption(AclMixin, PreferencesModel): """Options générales de l'asso : siret, addresse, nom, etc""" diff --git a/preferences/templates/preferences/aff_mailcontact.html b/preferences/templates/preferences/aff_mailcontact.html new file mode 100644 index 00000000..55d268f0 --- /dev/null +++ b/preferences/templates/preferences/aff_mailcontact.html @@ -0,0 +1,45 @@ +{% comment %} +Re2o est un logiciel d'administration développé initiallement au rezometz. Il +se veut agnostique au réseau considéré, de manière à être installable en +quelques clics. + +Copyright © 2017 Gabriel Détraz +Copyright © 2017 Goulven Kermarec +Copyright © 2017 Augustin Lemesle + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +{% endcomment %} +{% load acl %} + + + + + + + + + {% for mailcontact in mailcontact_list %} + + + + + + {% endfor %} +
    AdresseCommentaire
    {{ mailcontact.address }}{{ mailcontact.commentary }} + {% can_edit mailcontact %} + {% include 'buttons/edit.html' with href='preferences:edit-mailcontact' id=mailcontact.id %} + {% acl_end %} + {% include 'buttons/history.html' with href='preferences:history' name='mailcontact' id=mailcontact.id %} +
    diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 71bf60f8..7bd7df96 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -230,9 +230,17 @@ with this program; if not, write to the Free Software Foundation, Inc., {% can_create preferences.Service%}
    Ajouter un service {% acl_end %} - Supprimer un ou plusieurs service + Supprimer un ou plusieurs services {% include "preferences/aff_service.html" with service_list=service_list %} +

    Liste des adresses mail de contact

    + {% can_create preferences.MailContact%} + Ajouter une adresse + {% acl_end %} + Supprimer une ou plusieurs adresses + {% include "preferences/aff_mailcontact.html" with mailcontact_list=mailcontact_list %} + + Editer diff --git a/preferences/urls.py b/preferences/urls.py index bca7bb1e..b9e1663d 100644 --- a/preferences/urls.py +++ b/preferences/urls.py @@ -73,7 +73,14 @@ urlpatterns = [ views.edit_service, name='edit-service' ), - url(r'^del_services/$', views.del_services, name='del-services'), + url(r'^del_service/$', views.del_service, name='del-service'), + url(r'^add_mailcontact/$', views.add_mailcontact, name='add-mailcontact'), + url( + r'^edit_mailcontact/(?P[0-9]+)$', + views.edit_mailcontact, + name='edit-mailcontact' + ), + url(r'^del_mailcontact/$', views.del_mailcontact, name='del-mailcontact'), url( r'^history/(?P\w+)/(?P[0-9]+)$', re2o.views.history, diff --git a/preferences/views.py b/preferences/views.py index b8ca39d2..1ad0b42d 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -42,9 +42,10 @@ from reversion import revisions as reversion from re2o.views import form from re2o.acl import can_create, can_edit, can_delete_set, can_view_all -from .forms import ServiceForm, DelServiceForm +from .forms import ServiceForm, DelServiceForm, MailContactForm, DelMailContactForm from .models import ( Service, + MailContact, OptionalUser, OptionalMachine, AssoOption, @@ -71,6 +72,7 @@ def display_options(request): homeoptions, _created = HomeOption.objects.get_or_create() mailmessageoptions, _created = MailMessageOption.objects.get_or_create() service_list = Service.objects.all() + mailcontact_list = MailContact.objects.all() return form({ 'useroptions': useroptions, 'machineoptions': machineoptions, @@ -79,7 +81,8 @@ def display_options(request): 'assooptions': assooptions, 'homeoptions': homeoptions, 'mailmessageoptions': mailmessageoptions, - 'service_list': service_list + 'service_list': service_list, + 'mailcontact_list': mailcontact_list }, 'preferences/display_preferences.html', request) @@ -169,7 +172,7 @@ def edit_service(request, service_instance, **_kwargs): @login_required @can_delete_set(Service) -def del_services(request, instances): +def del_service(request, instances): """Suppression d'un service de la page d'accueil""" services = DelServiceForm(request.POST or None, instances=instances) if services.is_valid(): @@ -179,7 +182,7 @@ def del_services(request, instances): with transaction.atomic(), reversion.create_revision(): services_del.delete() reversion.set_user(request.user) - messages.success(request, "Le service a été supprimée") + messages.success(request, "Le service a été supprimé") except ProtectedError: messages.error(request, "Erreur le service\ suivant %s ne peut être supprimé" % services_del) @@ -189,3 +192,75 @@ def del_services(request, instances): 'preferences/preferences.html', request ) + + +@login_required +@can_create(MailContact) +def add_mailcontact(request): + """Ajout d'une adresse de contact""" + mailcontact = MailContactForm( + request.POST or None, + request.FILES or None + ) + if mailcontact.is_valid(): + with transaction.atomic(), reversion.create_revision(): + mailcontact.save() + reversion.set_user(request.user) + reversion.set_comment("Création") + messages.success(request, "Cette adresse a été ajoutée") + return redirect(reverse('preferences:display-options')) + return form( + {'preferenceform': mailcontact, 'action_name': 'Ajouter'}, + 'preferences/preferences.html', + request + ) + + +@login_required +@can_edit(MailContact) +def edit_mailcontact(request, mailcontact_instance, **_kwargs): + """Edition des adresses de contacte affichées""" + mailcontact = MailContactForm( + request.POST or None, + request.FILES or None, + instance=mailcontact_instance + ) + if mailcontact.is_valid(): + with transaction.atomic(), reversion.create_revision(): + mailcontact.save() + reversion.set_user(request.user) + reversion.set_comment("Modification") + messages.success(request, "Adresse modifiée") + return redirect(reverse('preferences:display-options')) + return form( + {'preferenceform': mailcontact, 'action_name': 'Editer'}, + 'preferences/preferences.html', + request + ) + + +@login_required +@can_delete_set(MailContact) +def del_mailcontact(request, instances): + """Suppression d'une adresse de contact""" + mailcontacts = DelMailContactForm( + request.POST or None, + instances=instances + ) + if mailcontacts.is_valid(): + mailcontacts_dels = mailcontacts.cleaned_data['mailcontacts'] + for mailcontacts_del in mailcontacts_dels: + try: + with transaction.atomic(), reversion.create_revision(): + mailcontacts_del.delete() + reversion.set_user(request.user) + messages.success(request, "L'adresse a été supprimée") + except ProtectedError: + messages.error(request, "Erreur le service\ + suivant %s ne peut être supprimé" % mailcontacts_del) + return redirect(reverse('preferences:display-options')) + return form( + {'preferenceform': mailcontacts, 'action_name': 'Supprimer'}, + 'preferences/preferences.html', + request + ) diff --git a/re2o/templatetags/acl.py b/re2o/templatetags/acl.py index 17a51baf..204a5697 100644 --- a/re2o/templatetags/acl.py +++ b/re2o/templatetags/acl.py @@ -121,6 +121,7 @@ MODEL_NAME = { 'OptionalTopologie': preferences.models.OptionalTopologie, 'GeneralOption': preferences.models.GeneralOption, 'preferences.Service': preferences.models.Service, + 'preferences.MailContact': preferences.models.MailContact, 'AssoOption': preferences.models.AssoOption, 'MailMessageOption': preferences.models.MailMessageOption, # topologie From a26487f5a271bd266e8fcd5b9871956b226fc94b Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Sat, 30 Jun 2018 22:56:34 +0200 Subject: [PATCH 43/48] bug fix historique alias --- users/admin.py | 6 ++++++ users/models.py | 4 ++-- .../templates/users/{aff_alias.html => aff_mailalias.html} | 2 +- users/templates/users/profil.html | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) rename users/templates/users/{aff_alias.html => aff_mailalias.html} (97%) diff --git a/users/admin.py b/users/admin.py index 8389c029..ee71713c 100644 --- a/users/admin.py +++ b/users/admin.py @@ -109,6 +109,11 @@ class BanAdmin(VersionAdmin): pass +class MailAliasAdmin(VersionAdmin): + """Gestion des alias mail""" + pass + + class WhitelistAdmin(VersionAdmin): """Gestion des whitelist""" pass @@ -212,6 +217,7 @@ admin.site.register(School, SchoolAdmin) admin.site.register(ListRight, ListRightAdmin) admin.site.register(ListShell, ListShellAdmin) admin.site.register(Ban, BanAdmin) +admin.site.register(MailAlias, MailAliasAdmin) admin.site.register(Whitelist, WhitelistAdmin) admin.site.register(Request, RequestAdmin) # Now register the new UserAdmin... diff --git a/users/models.py b/users/models.py index d31f0e59..20f640a4 100644 --- a/users/models.py +++ b/users/models.py @@ -1635,7 +1635,7 @@ class MailAlias(RevMixin, AclMixin, models.Model): """ Define a alias for a user Mail - Définit un aliase pour un Mail d'utilisateur + Définit un alias pour un Mail d'utilisateur """ user = models.ForeignKey( User, @@ -1670,7 +1670,7 @@ class MailAlias(RevMixin, AclMixin, models.Model): def can_view(self, user_request, *_args, **_kwargs): """ - Check if the user can view the aliases + Check if the user can view the alias """ if user_request.has_perm('users.view_mailalias') or user.request == self.user: diff --git a/users/templates/users/aff_alias.html b/users/templates/users/aff_mailalias.html similarity index 97% rename from users/templates/users/aff_alias.html rename to users/templates/users/aff_mailalias.html index ec5b7313..a441b9e9 100644 --- a/users/templates/users/aff_alias.html +++ b/users/templates/users/aff_mailalias.html @@ -41,7 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% can_edit alias %} {% include 'buttons/edit.html' with href='users:edit-mailalias' id=alias.id %} {% acl_end %} - {% include 'buttons/history.html' with href='users:history' name='alias' id=alias.id %} + {% include 'buttons/history.html' with href='users:history' name='mailalias' id=alias.id %} {% endfor %} diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index 62bbf1e5..863897f2 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -468,7 +468,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% acl_end %} {% if alias_list %} - {% include "users/aff_alias.html" with alias_list=alias_list %} + {% include "users/aff_mailalias.html" with alias_list=alias_list %} {% endif %} {% endif %} {% else %} From 2e092b3fde5ce23488f442abf2d84f9bceca1376 Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 30 Jun 2018 22:17:24 +0000 Subject: [PATCH 44/48] Fix langue et 802.X radius + divers --- freeradius_utils/auth.py | 39 +++++++- search/views.py | 4 +- topologie/forms.py | 6 +- topologie/migrations/0064_createprofil.py | 80 ++++++++-------- .../migrations/0067_auto_20180701_0016.py | 75 +++++++++++++++ topologie/models.py | 37 ++++---- topologie/templates/topologie/aff_port.html | 10 +- .../templates/topologie/aff_port_profile.html | 94 ++++++++++++------- topologie/templates/topologie/index.html | 1 - .../topologie/index_portprofile.html | 13 ++- topologie/templates/topologie/sidebar.html | 2 +- topologie/views.py | 8 +- 12 files changed, 250 insertions(+), 119 deletions(-) create mode 100644 topologie/migrations/0067_auto_20180701_0016.py diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index b318ab63..8450777b 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -355,30 +355,47 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, 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 + # politique à appliquer sur ce port if not port: return (sw_name, "Chambre inconnue", u'Port inconnu', VLAN_OK) # On récupère le profil du port port_profil = port.get_port_profil - # Si un vlan a été précisé, on l'utilise pour VLAN_OK + # Si un vlan a été précisé dans la config du port, + # on l'utilise pour VLAN_OK if port_profil.vlan_untagged: DECISION_VLAN = int(port_profil.vlan_untagged.vlan_id) extra_log = u"Force sur vlan " + str(DECISION_VLAN) else: DECISION_VLAN = VLAN_OK + # Si le port est désactivé, on rejette sur le vlan de déconnexion if not port.state: - return (sw_name, port.room, u'Port desactive', VLAN_NOK) + return (sw_name, port.room, u'Port desactivé', VLAN_NOK) + # Si radius est désactivé, on laisse passer if port_profil.radius_type == 'NO': return (sw_name, "", u"Pas d'authentification sur ce port" + extra_log, DECISION_VLAN) - if port_profil.radius_type == 'STRICT': + # 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 == '802.1X' and port_profil.radius_type == '802.1X': + room = port.room or "Chambre/local inconnu" + return (sw_name, room, u'Acceptation authentification 802.1X', DECISION_VLAN) + + # Sinon, cela veut dire qu'on fait de l'auth radius par mac + # Si le port est en mode strict, on vérifie que tous les users + # rattachés à ce port sont bien à jour de cotisation. Sinon on rejette (anti squattage) + # Il n'est pas possible de se connecter sur une prise strict sans adhérent à jour de cotis + # dedans + if port_profil.radius_mode == 'STRICT': room = port.room if not room: return (sw_name, "Inconnue", u'Chambre inconnue', VLAN_NOK) @@ -393,7 +410,8 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, return (sw_name, room, u'Chambre resident desactive', VLAN_NOK) # else: user OK, on passe à la verif MAC - if port_profil.radius_type == 'COMMON' or port_profil.radius_type == 'STRICT': + # Si on fait de l'auth par mac, on cherche l'interface via sa mac dans la bdd + if port_profil.radius_mode == 'COMMON' or port_profil.radius_mode == 'STRICT': # Authentification par mac interface = (Interface.objects .filter(mac_address=mac_address) @@ -402,15 +420,19 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, .first()) if not interface: room = port.room - # On essaye de register la mac + # On essaye de register la mac, si l'autocapture a été activée + # Sinon on rejette sur vlan_nok if not nas_type.autocapture_mac: return (sw_name, "", u'Machine inconnue', VLAN_NOK) + # On ne peut autocapturer que si on connait la chambre et donc l'user correspondant elif not room: return (sw_name, "Inconnue", u'Chambre et machine inconnues', VLAN_NOK) else: + # Si la chambre est vide (local club, prises en libre services) + # Impossible d'autocapturer if not room_user: room_user = User.objects.filter( Q(club__room=port.room) | Q(adherent__room=port.room) @@ -421,6 +443,8 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, u'Machine et propriétaire de la chambre ' 'inconnus', VLAN_NOK) + # Si il y a plus d'un user dans la chambre, impossible de savoir à qui + # Ajouter la machine elif room_user.count() > 1: return (sw_name, room, @@ -428,11 +452,13 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, 'dans la chambre/local -> ajout de mac ' 'automatique impossible', VLAN_NOK) + # Si l'adhérent de la chambre n'est pas à jour de cotis, pas d'autocapture elif not room_user.first().has_access(): return (sw_name, room, u'Machine inconnue et adhérent non cotisant', VLAN_NOK) + # Sinon on capture et on laisse passer sur le bon vlan else: result, reason = (room_user .first() @@ -452,6 +478,9 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, reason + str(mac_address) ), VLAN_NOK) + # L'interface a été trouvée, on vérifie qu'elle est active, sinon on reject + # Si elle n'a pas d'ipv4, on lui en met une + # Enfin on laisse passer sur le vlan pertinent else: room = port.room if not interface.is_active: diff --git a/search/views.py b/search/views.py index 0ae8470b..73333834 100644 --- a/search/views.py +++ b/search/views.py @@ -262,9 +262,9 @@ def search_single_word(word, filters, user, ) | Q( related__switch__interface__domain__name__icontains=word ) | Q( - custom_profil__name__icontains=word + custom_profile__name__icontains=word ) | Q( - custom_profil__profil_default__icontains=word + custom_profile__profil_default__icontains=word ) | Q( details__icontains=word ) diff --git a/topologie/forms.py b/topologie/forms.py index 63ffc652..ba37d395 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -80,8 +80,8 @@ class EditPortForm(FormRevMixin, ModelForm): optimiser le temps de chargement avec select_related (vraiment lent sans)""" class Meta(PortForm.Meta): - fields = ['room', 'related', 'machine_interface', 'custom_profil', - 'state', 'details'] + fields = ['room', 'related', 'machine_interface', 'custom_profile', + 'state', 'details'] def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) @@ -109,7 +109,7 @@ class AddPortForm(FormRevMixin, ModelForm): 'room', 'machine_interface', 'related', - 'custom_profil', + 'custom_profile', 'state', 'details' ] diff --git a/topologie/migrations/0064_createprofil.py b/topologie/migrations/0064_createprofil.py index 189d1812..2f165386 100644 --- a/topologie/migrations/0064_createprofil.py +++ b/topologie/migrations/0064_createprofil.py @@ -5,53 +5,49 @@ from __future__ import unicode_literals from django.db import migrations +def transfer_profil(apps, schema_editor): + db_alias = schema_editor.connection.alias + port = apps.get_model("topologie", "Port") + 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') + for vlan_instance in vlan.objects.using(db_alias).all(): + if port.objects.using(db_alias).filter(vlan_force=vlan_instance): + custom_profil = 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_profil=custom_profil) + 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_profil=common_profil) + port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=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_profil=strict_profil) + port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=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_profil=strict_profil) + port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=common_profil) + profil_room.save() + + class Migration(migrations.Migration): dependencies = [ ('topologie', '0063_port_custom_profil'), ] - def transfer_profil(apps, schema_editor): - db_alias = schema_editor.connection.alias - port = apps.get_model("topologie", "Port") - 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') - for vlan_instance in vlan.objects.using(db_alias).all(): - if port.objects.using(db_alias).filter(vlan_force=vlan_instance): - custom_profil = 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_profil=custom_profil) - 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_profil=common_profil) - port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=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_profil=strict_profil) - port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=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_profil=strict_profil) - port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=common_profil) - profil_room.save() - - - - def untransfer_profil(apps, schema_editor): - return - operations = [ - migrations.RunPython(transfer_profil, untransfer_profil), + migrations.RunPython(transfer_profil), ] diff --git a/topologie/migrations/0067_auto_20180701_0016.py b/topologie/migrations/0067_auto_20180701_0016.py new file mode 100644 index 00000000..578ee7d6 --- /dev/null +++ b/topologie/migrations/0067_auto_20180701_0016.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-30 22:16 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0066_auto_20180630_1855'), + ] + + operations = [ + migrations.RenameField( + model_name='port', + old_name='custom_profil', + new_name='custom_profile', + ), + 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 is dhcp assigned', verbose_name='Arp protect'), + ), + 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', verbose_name='Flow control'), + ), + migrations.AlterField( + model_name='portprofile', + name='loop_protect', + field=models.BooleanField(default=False, help_text='Protect again loop', verbose_name='Loop Protect'), + ), + 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='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-auth : 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 auth : 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, verbose_name='Speed'), + ), + ] diff --git a/topologie/models.py b/topologie/models.py index d0534dda..715ccf52 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -391,7 +391,7 @@ class Port(AclMixin, RevMixin, models.Model): blank=True, related_name='related_port' ) - custom_profil = models.ForeignKey( + custom_profile = models.ForeignKey( 'PortProfile', on_delete=models.PROTECT, blank=True, @@ -399,8 +399,8 @@ class Port(AclMixin, RevMixin, models.Model): ) state = models.BooleanField( default=True, - help_text='Etat du port Actif', - verbose_name=_("Etat du port Actif") + help_text='Port state Active', + verbose_name=_("Port State Active") ) details = models.CharField(max_length=255, blank=True) @@ -412,7 +412,8 @@ class Port(AclMixin, RevMixin, models.Model): @cached_property def get_port_profil(self): - """Return the config profil for this port""" + """Return the config profil for this port + :returns: the profile of self (port)""" def profil_or_nothing(profil): port_profil = PortProfile.objects.filter(profil_default=profil).first() if port_profil: @@ -423,8 +424,8 @@ class Port(AclMixin, RevMixin, models.Model): nothing = PortProfile.objects.create(profil_default='nothing', name='nothing', radius_type='NO') return nothing - if self.custom_profil: - return self.custom_profil + if self.custom_profile: + return self.custom_profile elif self.related: return profil_or_nothing('uplink') elif self.machine_interface: @@ -568,57 +569,57 @@ class PortProfile(AclMixin, RevMixin, models.Model): radius_type = models.CharField( max_length=32, choices=TYPES, - help_text="Choix du type d'authentification radius : non actif, mac ou 802.1X", + help_text="Type of radius auth : inactive, mac-address or 802.1X", verbose_name=_("RADIUS type") ) radius_mode = models.CharField( max_length=32, choices=MODES, default='COMMON', - help_text="En cas d'auth par mac, auth common ou strcit sur le port", + help_text="In case of mac-auth : mode common or strict on this port", verbose_name=_("RADIUS mode") ) speed = models.CharField( max_length=32, choices=SPEED, default='auto', - help_text='Mode de transmission et vitesse du port', + help_text='Port speed limit', verbose_name=_("Speed") ) mac_limit = models.IntegerField( null=True, blank=True, - help_text='Limit du nombre de mac sur le port', + help_text='Limit of mac-address on this port', verbose_name=_("Mac limit") ) flow_control = models.BooleanField( default=False, - help_text='Gestion des débits', + help_text='Flow control', verbose_name=_("Flow control") ) dhcp_snooping = models.BooleanField( default=False, - help_text='Protection dhcp pirate', + help_text='Protect against rogue dhcp', verbose_name=_("Dhcp snooping") ) dhcpv6_snooping = models.BooleanField( default=False, - help_text='Protection dhcpv6 pirate', + help_text='Protect against rogue dhcpv6', verbose_name=_("Dhcpv6 snooping") ) arp_protect = models.BooleanField( default=False, - help_text='Verification assignation de l\'IP par dhcp', + help_text='Check if ip is dhcp assigned', verbose_name=_("Arp protect") ) ra_guard = models.BooleanField( default=False, - help_text='Protection contre ra pirate', + help_text='Protect against rogue ra', verbose_name=_("Ra guard") ) loop_protect = models.BooleanField( default=False, - help_text='Protection contre les boucles', + help_text='Protect again loop', verbose_name=_("Loop Protect") ) @@ -635,6 +636,10 @@ class PortProfile(AclMixin, RevMixin, models.Model): def security_parameters_enabled(self): return [parameter for parameter in self.security_parameters_fields if getattr(self, parameter)] + @cached_property + def security_parameters_as_str(self): + return ','.join(self.security_parameters_enabled) + def __str__(self): return self.name diff --git a/topologie/templates/topologie/aff_port.html b/topologie/templates/topologie/aff_port.html index 45e81e06..86216f15 100644 --- a/topologie/templates/topologie/aff_port.html +++ b/topologie/templates/topologie/aff_port.html @@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% include "buttons/sort.html" with prefix='port' col='room' text='Room' %} {% include "buttons/sort.html" with prefix='port' col='interface' text='Interface machine' %} {% include "buttons/sort.html" with prefix='port' col='related' text='Related' %} - Etat du port + Etat du port Profil du port Détails @@ -67,7 +67,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endif %} {% if port.state %} Actif{% else %}Désactivé{% endif %} - {% if not port.custom_profil %}Par défaut : {% endif %}{{port.get_port_profil}} + {% if not port.custom_profile %}Par défaut : {% endif %}{{port.get_port_profil}} {{ port.details }} @@ -77,10 +77,10 @@ with this program; if not, write to the Free Software Foundation, Inc., - {% acl_end %} + {% acl_end %} {% can_delete port %} - - + + {% acl_end %} diff --git a/topologie/templates/topologie/aff_port_profile.html b/topologie/templates/topologie/aff_port_profile.html index 7e044c0a..577ac2a5 100644 --- a/topologie/templates/topologie/aff_port_profile.html +++ b/topologie/templates/topologie/aff_port_profile.html @@ -1,3 +1,25 @@ +{% 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 © 2018 Gabriel Détraz + +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 i18n %} @@ -7,45 +29,51 @@ {% include "pagination.html" with list=port_profile_list %} {% endif %} + + - - - - - - - - - - + + + + + + + + + + {% for port_profile in port_profile_list %} - - - {% endif %} - - - - + + + {% endif %} + + + + {% endfor %}
    {% trans "Nom" %}{% trans "Default pour" %}{% trans "VLANs" %}{% trans "Réglages RADIUS" %}{% trans "Vitesse" %}{% trans "Mac address limit" %}{% trans "Sécurité" %}
    {% trans "Name" %}{% trans "Default for" %}{% trans "VLANs" %}{% trans "RADIUS settings" %}{% trans "Speed" %}{% trans "Mac address limit" %}{% trans "Security" %}
    {{port_profile.name}} {{port_profile.profil_default}} - Untagged : {{port_profile.vlan_untagged}} -
    - Tagged : {{port_profile.vlan_tagged.all|join:", "}} -
    - Type : {{port_profile.radius_type}} - {% if port_profile.radius_type == "MAC-radius" %} -
    - Mode : {{port_profile.radius_mode}}
    {{port_profile.speed}}{{port_profile.mac_limit}}{{port_profile.security_parameters_enabled|join:"
    "}}
    - {% include 'buttons/history.html' with href='topologie:history' name='portprofile' id=port_profile.pk %} - {% can_edit port_profile %} - {% include 'buttons/edit.html' with href='topologie:edit-port-profile' id=port_profile.pk %} - {% acl_end %} - {% can_delete port_profile %} - {% include 'buttons/suppr.html' with href='topologie:del-port-profile' id=port_profile.pk %} - {% acl_end %} - + {% if port_profile.vlan_untagged %} + Untagged : {{port_profile.vlan_untagged}} +
    + {% endif %} + {% if port_profile.vlan_untagged %} + Tagged : {{port_profile.vlan_tagged.all|join:", "}} + {% endif %} +
    + Type : {{port_profile.radius_type}} + {% if port_profile.radius_type == "MAC-radius" %} +
    + Mode : {{port_profile.radius_mode}}
    {{port_profile.speed}}{{port_profile.mac_limit}}{{port_profile.security_parameters_enabled|join:"
    "}}
    + {% include 'buttons/history.html' with href='topologie:history' name='portprofile' id=port_profile.pk %} + {% can_edit port_profile %} + {% include 'buttons/edit.html' with href='topologie:edit-port-profile' id=port_profile.pk %} + {% acl_end %} + {% can_delete port_profile %} + {% include 'buttons/suppr.html' with href='topologie:del-port-profile' id=port_profile.pk %} + {% acl_end %} +
    diff --git a/topologie/templates/topologie/index.html b/topologie/templates/topologie/index.html index 7949f412..f596e6a5 100644 --- a/topologie/templates/topologie/index.html +++ b/topologie/templates/topologie/index.html @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load acl %} -{% load i18n %} {% block title %}Switchs{% endblock %} diff --git a/topologie/templates/topologie/index_portprofile.html b/topologie/templates/topologie/index_portprofile.html index a4287da6..f95415c8 100644 --- a/topologie/templates/topologie/index_portprofile.html +++ b/topologie/templates/topologie/index_portprofile.html @@ -4,9 +4,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 © 2018 Gabriel Détraz + 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 @@ -36,9 +35,9 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans " Add a port profile" %}
    {% acl_end %} - {% include "topologie/aff_port_profile.html" with port_profile_list=port_profile_list %} -
    -
    -
    +{% include "topologie/aff_port_profile.html" with port_profile_list=port_profile_list %} +
    +
    +
    {% endblock %} diff --git a/topologie/templates/topologie/sidebar.html b/topologie/templates/topologie/sidebar.html index 8652690e..04ee5202 100644 --- a/topologie/templates/topologie/sidebar.html +++ b/topologie/templates/topologie/sidebar.html @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Switchs - + Config des ports switchs diff --git a/topologie/views.py b/topologie/views.py index b7761e0c..e5453982 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -1014,11 +1014,11 @@ def del_port_profile(request, port_profile, **_kwargs): if request.method == 'POST': try: port_profile.delete() - messages.success(request, _("The port profile was successfully" - " deleted")) + messages.success(request, + _("The port profile was successfully deleted")) except ProtectedError: - messages.success(request, _("Impossible to delete the port" - " profile")) + messages.success(request, + _("Impossible to delete the port profile")) return redirect(reverse('topologie:index')) return form( {'objet': port_profile, 'objet_name': _("Port profile")}, From a07e0d922acd2a0f4cd511b1dfd708844ceb6772 Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 30 Jun 2018 22:23:00 +0000 Subject: [PATCH 45/48] Petit bug affichage vlans --- topologie/templates/topologie/aff_port_profile.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/topologie/templates/topologie/aff_port_profile.html b/topologie/templates/topologie/aff_port_profile.html index 577ac2a5..12c8b365 100644 --- a/topologie/templates/topologie/aff_port_profile.html +++ b/topologie/templates/topologie/aff_port_profile.html @@ -52,7 +52,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Untagged : {{port_profile.vlan_untagged}}
    {% endif %} - {% if port_profile.vlan_untagged %} + {% if port_profile.vlan_tagged.all %} Tagged : {{port_profile.vlan_tagged.all|join:", "}} {% endif %} From e42b8f9be0437548526a9572a89343cc2b9d650b Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Sun, 1 Jul 2018 10:49:47 +0200 Subject: [PATCH 46/48] =?UTF-8?q?Page=20de=20contact=20cot=C3=A9=20utilisa?= =?UTF-8?q?teur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- preferences/models.py | 4 +++ re2o/templates/re2o/contact.html | 52 ++++++++++++++++++++++++++++++++ re2o/views.py | 18 +++++++++++ 3 files changed, 74 insertions(+) create mode 100644 re2o/templates/re2o/contact.html diff --git a/preferences/models.py b/preferences/models.py index 5c0dd5de..cf4e5a33 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -307,6 +307,10 @@ class MailContact(AclMixin, models.Model): max_length = 256 ) + @cached_property + def get_name(self): + return self.address.split("@")[0] + class Meta: permissions = ( ("view_mailcontact", "Peut voir les mails de contact"), diff --git a/re2o/templates/re2o/contact.html b/re2o/templates/re2o/contact.html new file mode 100644 index 00000000..474ac7e8 --- /dev/null +++ b/re2o/templates/re2o/contact.html @@ -0,0 +1,52 @@ +{% extends "re2o/sidebar.html" %} +{% comment %} +Re2o est un logiciel d'administration développé initiallement au rezometz. Il +se veut agnostique au réseau considéré, de manière à être installable en +quelques clics. + +Copyright © 2017 Gabriel Détraz +Copyright © 2017 Goulven Kermarec +Copyright © 2017 Augustin Lemesle + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +{% endcomment %} + +{% load bootstrap3 %} +{% load i18n %} + +{% block title %}{% trans "Contact" %}{% endblock %} + +{% block content %} +

    {% blocktrans %}Contacter l'association {{asso_name}}{% endblocktrans %}

    +
    + +{% for contact in contacts %} + +
    +

    {{ contact.get_name }}

    +
    +
    +
    {{ contact.commentary}}
    + +
    +
    +
    + +{% endfor %} + + + +{% endblock %} + diff --git a/re2o/views.py b/re2o/views.py index 45b6ea73..75c8c694 100644 --- a/re2o/views.py +++ b/re2o/views.py @@ -43,6 +43,7 @@ from django.views.decorators.cache import cache_page import preferences from preferences.models import ( Service, + MailContact, GeneralOption, AssoOption, HomeOption @@ -86,6 +87,7 @@ HISTORY_BIND = { 'users': { 'user': users.models.User, 'ban': users.models.Ban, + 'mailalias': users.models.MailAlias, 'whitelist': users.models.Whitelist, 'school': users.models.School, 'listright': users.models.ListRight, @@ -94,6 +96,7 @@ HISTORY_BIND = { }, 'preferences': { 'service': preferences.models.Service, + 'mailcontact': preferences.models.MailContact, }, 'cotisations': { 'facture': cotisations.models.Facture, @@ -229,6 +232,21 @@ def about_page(request): } ) +def contact_page(request): + """The view for the contact page + Send all the objects MailContact + """ + address = MailContact.objects.all() + + return render( + request, + "re2o/contact.html", + { + 'contacts': address, + 'asso_name': AssoOption.objects.first().name + } + ) + def handler500(request): """The handler view for a 500 error""" From 8d86feab0f8095a9b5cb64f1d2e7d1de12b85bf0 Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Sun, 1 Jul 2018 15:33:16 +0200 Subject: [PATCH 47/48] =?UTF-8?q?fichiers=20oubli=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/base.html | 8 ++++++-- users/models.py | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/templates/base.html b/templates/base.html index 3755e1c2..85457292 100644 --- a/templates/base.html +++ b/templates/base.html @@ -130,8 +130,12 @@ with this program; if not, write to the Free Software Foundation, Inc., {% acl_end %} -
  • - {% trans "About" %} +
  • {% if not request.user.is_authenticated %} {% if var_sa %} diff --git a/users/models.py b/users/models.py index 20f640a4..4e1f4202 100644 --- a/users/models.py +++ b/users/models.py @@ -1649,6 +1649,10 @@ class MailAlias(RevMixin, AclMixin, models.Model): ) def __str__(self): + return self.complete_mail + + @cached_property + def complete_mail(self): return self.valeur + OptionalUser.get_cached_value('mail_extension') @staticmethod From b9b32c41ce345b40460ed758c9eab0fd7f5761d4 Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Sun, 1 Jul 2018 15:51:18 +0200 Subject: [PATCH 48/48] migration --- preferences/migrations/0038_mailcontact.py | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 preferences/migrations/0038_mailcontact.py diff --git a/preferences/migrations/0038_mailcontact.py b/preferences/migrations/0038_mailcontact.py new file mode 100644 index 00000000..6165b98a --- /dev/null +++ b/preferences/migrations/0038_mailcontact.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-30 15:27 +from __future__ import unicode_literals + +from django.db import migrations, models +import re2o.mixins + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0037_optionaluser_max_mail_alias'), + ] + + operations = [ + migrations.CreateModel( + name='MailContact', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('address', models.EmailField(default='contact@example.org', help_text='Adresse mail de contact', max_length=254)), + ('commentary', models.CharField(blank=True, help_text="Description de l'utilisation de l'adresse mail associée", max_length=256, null=True)), + ], + options={ + 'permissions': (('view_mailcontact', 'Peut voir les mails de contact'),), + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + ]