...
 
Commits (7)
# Re2o
Gnu public license v2.0
GNU public license v2.0
## Avant propos
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
Re2o est un logiciel d'administration développé initialement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
......@@ -31,15 +31,15 @@ Pour cela :
## Fonctionnement général
Re2o est séparé entre les models, qui sont visible sur le schéma des
dépendances. Il s'agit en réalité des tables sql, et les fields etant les
Re2o est séparé entre les models, qui sont visibles sur le schéma des
dépendances. Il s'agit en réalité des tables sql, et les fields étant les
colonnes.
Ceci dit il n'est jamais nécessaire de toucher directement au sql, django
procédant automatiquement à tout cela.
On crée donc différents models (user, right pour les droits des users,
interfaces, IpList pour l'ensemble des adresses ip, etc)
Du coté des forms, il s'agit des formulaire d'édition des models. Il
Du coté des forms, il s'agit des formulaires d'édition des models. Il
s'agit de ModelForms django, qui héritent des models très simplement, voir la
documentation django models forms.
......@@ -56,12 +56,20 @@ d'accéder à ces vues, utilisé par re2o-tools.
# Requète en base de donnée
Pour avoir un shell, il suffit de lancer '''python3 manage.py shell'''
Pour charger des objets, example avec User, faire :
''' from users.models import User'''
Pour charger les objets django, il suffit de faire User.objects.all()
pour tous les users par exemple.
Il est ensuite aisé de faire des requètes, par exemple
User.objects.filter(pseudo='test')
Des exemples et la documentation complète sur les requètes django sont
Pour avoir un shell, lancer :
```.bash
python3 manage.py shell
```
Pour charger des objets (exemple avec User), faire :
```.python
from users.models import User
```
Pour charger les objets django, il suffit de faire `User.objects.all()`
pour tous les users par exemple.
Il est ensuite aisé de faire des requêtes, par exemple
`User.objects.filter(pseudo='test')`
Des exemples et la documentation complète sur les requêtes django sont
disponible sur le site officiel.
......@@ -148,7 +148,7 @@ def new_facture(request, user, userid):
p.facture = new_invoice_instance
p.save()
send_mail_invoice(new_invoice_instance)
#send_mail_invoice(new_invoice_instance)
return new_invoice_instance.paiement.end_payment(
new_invoice_instance,
......
......@@ -34,7 +34,11 @@ from .models import (
OptionalTopologie,
GeneralOption,
Service,
<<<<<<< HEAD
MailContact,
=======
>>>>>>> 5a88882... Ajoute le réglage reminder et factorise la liste des services
Reminder,
AssoOption,
MailMessageOption,
HomeOption
......@@ -85,6 +89,9 @@ class HomeOptionAdmin(VersionAdmin):
"""Class admin options home"""
pass
class ReminderAdmin(VersionAdmin):
"""Class reminder"""
pass
admin.site.register(OptionalUser, OptionalUserAdmin)
admin.site.register(OptionalMachine, OptionalMachineAdmin)
......@@ -93,5 +100,6 @@ admin.site.register(GeneralOption, GeneralOptionAdmin)
admin.site.register(HomeOption, HomeOptionAdmin)
admin.site.register(Service, ServiceAdmin)
admin.site.register(MailContact, MailContactAdmin)
admin.site.register(Reminder, ReminderAdmin)
admin.site.register(AssoOption, AssoOptionAdmin)
admin.site.register(MailMessageOption, MailMessageOptionAdmin)
......@@ -24,6 +24,7 @@ Formulaire d'edition des réglages : user, machine, topologie, asso...
"""
from __future__ import unicode_literals
from re2o.mixins import FormRevMixin
from django.forms import ModelForm, Form
from django import forms
......@@ -39,6 +40,7 @@ from .models import (
HomeOption,
Service,
MailContact
Reminder
)
......@@ -211,7 +213,7 @@ class EditHomeOptionForm(ModelForm):
self.fields['twitter_account_name'].label = _("Twitter account name")
class ServiceForm(ModelForm):
class ServiceForm(FormRevMixin, ModelForm):
"""Edition, ajout de services sur la page d'accueil"""
class Meta:
model = Service
......@@ -226,6 +228,7 @@ class ServiceForm(ModelForm):
self.fields['image'].label = _("Image")
class DelServiceForm(Form):
"""Suppression de services sur la page d'accueil"""
services = forms.ModelMultipleChoiceField(
......@@ -269,3 +272,24 @@ class DelMailContactForm(Form):
else:
self.fields['mailcontacts'].queryset = MailContact.objects.all()
class ReminderForm(FormRevMixin, ModelForm):
"""Edition, ajout de rappel"""
class Meta:
model = Reminder
fields = '__all__'
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(ReminderForm, self).__init__(*args, prefix=prefix, **kwargs)
class ReminderForm(FormRevMixin, ModelForm):
"""Edition, ajout de rappel"""
class Meta:
model = Reminder
fields = '__all__'
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(ReminderForm, self).__init__(*args, prefix=prefix, **kwargs)
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2018-05-05 10:53
from __future__ import unicode_literals
from django.db import migrations, models
import re2o.mixins
class Migration(migrations.Migration):
dependencies = [
('preferences', '0034_auto_20180416_1120'),
]
operations = [
migrations.CreateModel(
name='Reminder',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('days', models.IntegerField(default=7, help_text="Délais entre le mail et la fin d'adhésion", unique=True)),
('message', models.CharField(blank=True, default='', max_length=255, null=True)),
],
options={
'permissions': (('view_reminder', 'Peut voir un objet reminder'),),
},
bases=(re2o.mixins.AclMixin, models.Model),
),
]
......@@ -215,6 +215,32 @@ def optionaltopologie_post_save(**kwargs):
topologie_pref.set_in_cache()
class Reminder(AclMixin, models.Model):
"""Options pour les mails de notification de fin d'adhésion.
Days: liste des nombres de jours pour lesquells un mail est envoyé
optionalMessage: message additionel pour le mail
"""
PRETTY_NAME="Options pour le mail de fin d'adhésion"
days = models.IntegerField(
default=7,
unique=True,
help_text="Délais entre le mail et la fin d'adhésion"
)
message = models.CharField(
max_length=255,
default="",
null=True,
blank=True,
help_text="Message affiché spécifiquement pour ce rappel"
)
class Meta:
permissions = (
("view_reminder", "Peut voir un objet reminder"),
)
class GeneralOption(AclMixin, PreferencesModel):
"""Options générales : nombre de resultats par page, nom du site,
temps où les liens sont valides"""
......
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
{% load acl %}
<table class="table table-striped">
<thead>
<tr>
<th>Nombre de jours avant le rappel</th>
<th>Message custom pour ce rappel</th>
<th></th>
<th></th>
</tr>
</thead>
{% for reminder in reminder_list %}
<tr>
<td>{{ reminder.days }}</td>
<td>{{ reminder.message }}</td>
<td class="text-right">
{% can_edit reminder %}
{% include 'buttons/edit.html' with href='preferences:edit-reminder' id=reminder.id %}
{% acl_end %}
{% include 'buttons/history.html' with href='preferences:history' name='reminder' id=reminder.id %}
</td>
</tr>
{% endfor %}
</table>
......@@ -224,8 +224,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% acl_end %}
<a class="btn btn-danger btn-sm" role="button" href="{% url 'preferences:del-mailcontact' %}"><i class="fa fa-trash"></i>{% trans "Delete one or several addresses" %}</a>
{% include "preferences/aff_mailcontact.html" with mailcontact_list=mailcontact_list %}
<p>
</p>
<table class="table table-striped">
<tr>
<th>{% trans "Twitter account URL" %}</th>
......@@ -238,5 +237,36 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ homeoptions.facebook_url }}</td>
</tr>
</table>
<table class="table table-striped">
{% for line in assooptions %}
<tr>
{% for text, field in line %}
<th>{{ field }}</th>
<td>{{ text }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<table class="table table-striped">
{% for line in assooptions %}
<tr>
{% for text, field in line %}
<th>{{ field }}</th>
<td>{{ text }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<h2>Options pour le mail de fin d'adhésion</h2>
{% can_create preferences.Reminder%}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:add-reminder' %}"><i class="fa fa-plus"></i> Ajouter un rappel</a>
{% acl_end %}
{% include "preferences/aff_reminder.html" with reminder_list=reminder_list %}
<br />
<br />
<br />
{% endblock %}
......@@ -74,6 +74,20 @@ urlpatterns = [
),
url(r'^del_service/$', views.del_service, name='del-service'),
url(r'^add_mailcontact/$', views.add_mailcontact, name='add-mailcontact'),
url(r'^add_reminder/$', views.add_reminder, name='add-reminder'),
url(r'^add_reminder/$', views.add_reminder, name='add-reminder'),
url(
r'^edit_reminder/(?P<reminderid>[0-9]+)$',
views.edit_reminder,
name='edit-reminder'
),
url(r'^del_reminder/$', views.del_reminder, name='del-reminder'),
url(
r'^edit_reminder/(?P<reminderid>[0-9]+)$',
views.edit_reminder,
name='edit-reminder'
),
url(r'^del_reminder/$', views.del_reminder, name='del-reminder'),
url(
r'^edit_mailcontact/(?P<mailcontactid>[0-9]+)$',
views.edit_mailcontact,
......
......@@ -41,11 +41,12 @@ from django.utils.translation import ugettext as _
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 re2o.acl import can_create, can_edit, can_delete, can_view_all
from .forms import (
ServiceForm, DelServiceForm, MailContactForm, DelMailContactForm
ServiceForm, DelServiceForm, MailContactForm, DelMailContactForm, ReminderForm, ReminderForm
)
from .models import (
Service,
MailContact,
......@@ -133,11 +134,8 @@ def add_service(request):
"""Ajout d'un service de la page d'accueil"""
service = ServiceForm(request.POST or None, request.FILES or None)
if service.is_valid():
with transaction.atomic(), reversion.create_revision():
service.save()
reversion.set_user(request.user)
reversion.set_comment("Creation")
messages.success(request, _("The service was added."))
service.save()
messages.success(request, "Ce service a été ajouté")
return redirect(reverse('preferences:display-options'))
return form(
{'preferenceform': service, 'action_name': _("Add a service")},
......@@ -156,15 +154,8 @@ def edit_service(request, service_instance, **_kwargs):
instance=service_instance
)
if service.is_valid():
with transaction.atomic(), reversion.create_revision():
service.save()
reversion.set_user(request.user)
reversion.set_comment(
"Field(s) edited: %s" % ', '.join(
field for field in service.changed_data
)
)
messages.success(request, _("The service was edited."))
service.save()
messages.success(request, "Service modifié")
return redirect(reverse('preferences:display-options'))
return form(
{'preferenceform': service, 'action_name': _("Edit")},
......@@ -172,6 +163,34 @@ def edit_service(request, service_instance, **_kwargs):
request
)
@login_required
@can_delete(Service)
def del_service(request, service_instance, **_kwargs):
"""Destruction d'un service"""
if request.method == "POST":
service_instance.delete()
messages.success(request, "Le service a été détruit")
return redirect(reverse('preferences:display-options'))
return form(
{'objet': service_instance, 'objet_name': 'service'},
'preferences/delete.html',
request
)
@login_required
@can_create(Reminder)
def add_reminder(request):
"""Ajout d'un rappel"""
reminder = ReminderForm(request.POST or None)
if reminder.is_valid():
reminder.save()
messages.success(request, "Ce rappel a été ajouté")
return redirect(reverse('preferences:display-options'))
return form(
{'preferenceform': reminder, 'action_name': 'Ajouter'},
'preferences/preferences.html',
request
)
@login_required
@can_delete_set(Service)
......@@ -232,6 +251,17 @@ def edit_mailcontact(request, mailcontact_instance, **_kwargs):
return redirect(reverse('preferences:display-options'))
return form(
{'preferenceform': mailcontact, 'action_name': _("Edit")},
@can_edit(Reminder)
def edit_reminder(request, reminder_instance, **_kwargs):
"""Edition des rappels"""
reminder = ReminderForm(request.POST or None, instance=reminder_instance)
if reminder.is_valid():
reminder.save()
messages.success(request, "Service modifié")
return redirect(reverse('preferences:display-options'))
return form(
{'preferenceform': reminder, 'action_name': 'Editer'},
'preferences/preferences.html',
request
)
......@@ -258,3 +288,18 @@ def del_mailcontact(request, instances):
request
)
@login_required
@can_delete(Reminder)
def del_reminder(request, reminder_instance, **_kwargs):
"""Destruction d'un reminder"""
if request.method == "POST":
reminder_instance.delete()
messages.success(request, "Le reminder a été détruit")
return redirect(reverse('preferences:display-options'))
return form(
{'objet': reminder_instance, 'objet_name': 'reminder'},
'preferences/delete.html',
request
)
......@@ -232,7 +232,7 @@ msgstr "Accéder à mon profil"
#: templates/re2o/index.html:79
msgid "Services of the organisation"
msgstr "Serices de l'association"
msgstr "Services de l'association"
#: templates/re2o/index.html:93
msgid "Go there"
......
......@@ -79,6 +79,65 @@ from django.contrib.contenttypes.models import ContentType
register = template.Library()
MODEL_NAME = {
# cotisations
'Facture': cotisations.models.Facture,
'Vente': cotisations.models.Vente,
'Article': cotisations.models.Article,
'Banque': cotisations.models.Banque,
'Paiement': cotisations.models.Paiement,
'Cotisation': cotisations.models.Cotisation,
# machines
'Machine': machines.models.Machine,
'MachineType': machines.models.MachineType,
'IpType': machines.models.IpType,
'Vlan': machines.models.Vlan,
'Nas': machines.models.Nas,
'SOA': machines.models.SOA,
'Extension': machines.models.Extension,
'Mx': machines.models.Mx,
'Ns': machines.models.Ns,
'Txt': machines.models.Txt,
'Srv': machines.models.Srv,
'Interface': machines.models.Interface,
'Domain': machines.models.Domain,
'IpList': machines.models.IpList,
'Ipv6List': machines.models.Ipv6List,
'machines.Service': machines.models.Service,
'Service_link': machines.models.Service_link,
'OuverturePortList': machines.models.OuverturePortList,
'OuverturePort': machines.models.OuverturePort,
# preferences
'OptionalUser': preferences.models.OptionalUser,
'OptionalMachine': preferences.models.OptionalMachine,
'OptionalTopologie': preferences.models.OptionalTopologie,
'GeneralOption': preferences.models.GeneralOption,
'preferences.Service': preferences.models.Service,
'preferences.Reminder': preferences.models.Reminder,
'AssoOption': preferences.models.AssoOption,
'MailMessageOption': preferences.models.MailMessageOption,
# topologie
'Stack': topologie.models.Stack,
'Switch': topologie.models.Switch,
'AccessPoint': topologie.models.AccessPoint,
'ModelSwitch': topologie.models.ModelSwitch,
'ConstructorSwitch': topologie.models.ConstructorSwitch,
'Port': topologie.models.Port,
'Room': topologie.models.Room,
'Building': topologie.models.Building,
'SwitchBay': topologie.models.SwitchBay,
# users
'User': users.models.User,
'Adherent': users.models.Adherent,
'Club': users.models.Club,
'ServiceUser': users.models.ServiceUser,
'School': users.models.School,
'ListRight': users.models.ListRight,
'ListShell': users.models.ListShell,
'Ban': users.models.Ban,
'Whitelist': users.models.Whitelist,
}
def get_model(model_name):
"""Retrieve the model object from its name"""
......
......@@ -62,12 +62,128 @@ def index(request):
twitter_account_name = HomeOption.get_cached_value('twitter_account_name')
asso_name = AssoOption.get_cached_value('pseudo')
return form({
<<<<<<< HEAD
'services_urls': services,
'twitter_url': twitter_url,
'twitter_account_name': twitter_account_name,
'facebook_url': facebook_url,
'asso_name': asso_name
}, 're2o/index.html', request)
=======
'services_urls': services,
'twitter_url': twitter_url,
'twitter_account_name' : twitter_account_name,
'facebook_url': facebook_url,
'asso_name': asso_name
}, 're2o/index.html', request)
#: Binding the corresponding char sequence of history url to re2o models.
HISTORY_BIND = {
'users': {
'user': users.models.User,
'ban': users.models.Ban,
'whitelist': users.models.Whitelist,
'school': users.models.School,
'listright': users.models.ListRight,
'serviceuser': users.models.ServiceUser,
'listshell': users.models.ListShell,
},
'preferences': {
'service': preferences.models.Service,
'reminder': preferences.models.Reminder,
},
'cotisations': {
'facture': cotisations.models.Facture,
'article': cotisations.models.Article,
'paiement': cotisations.models.Paiement,
'banque': cotisations.models.Banque,
},
'topologie': {
'switch': topologie.models.Switch,
'port': topologie.models.Port,
'room': topologie.models.Room,
'stack': topologie.models.Stack,
'modelswitch': topologie.models.ModelSwitch,
'constructorswitch': topologie.models.ConstructorSwitch,
'accesspoint': topologie.models.AccessPoint,
'switchbay': topologie.models.SwitchBay,
'building': topologie.models.Building,
},
'machines': {
'machine': machines.models.Machine,
'interface': machines.models.Interface,
'domain': machines.models.Domain,
'machinetype': machines.models.MachineType,
'iptype': machines.models.IpType,
'extension': machines.models.Extension,
'soa': machines.models.SOA,
'mx': machines.models.Mx,
'txt': machines.models.Txt,
'srv': machines.models.Srv,
'ns': machines.models.Ns,
'service': machines.models.Service,
'vlan': machines.models.Vlan,
'nas': machines.models.Nas,
'ipv6list': machines.models.Ipv6List,
},
}
@login_required
def history(request, application, object_name, object_id):
"""Render history for a model.
The model is determined using the `HISTORY_BIND` dictionnary if none is
found, raises a Http404. The view checks if the user is allowed to see the
history using the `can_view` method of the model.
Args:
request: The request sent by the user.
object_name: Name of the model.
object_id: Id of the object you want to acces history.
Returns:
The rendered page of history if access is granted, else the user is
redirected to their profile page, with an error message.
Raises:
Http404: This kind of models doesn't have history.
"""
try:
model = HISTORY_BIND[application][object_name]
except KeyError:
raise Http404(u"Il n'existe pas d'historique pour ce modèle.")
object_name_id = object_name + 'id'
kwargs = {object_name_id: object_id}
try:
instance = model.get_instance(**kwargs)
except model.DoesNotExist:
messages.error(request, u"Entrée inexistante")
return redirect(reverse(
'users:profil',
kwargs={'userid': str(request.user.id)}
))
can, msg = instance.can_view(request.user)
if not can:
messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu")
return redirect(reverse(
'users:profil',
kwargs={'userid': str(request.user.id)}
))
pagination_number = GeneralOption.get_cached_value('pagination_number')
reversions = Version.objects.get_for_object(instance)
if hasattr(instance, 'linked_objects'):
for related_object in chain(instance.linked_objects()):
reversions = (reversions |
Version.objects.get_for_object(related_object))
reversions = re2o_paginator(request, reversions, pagination_number)
return render(
request,
're2o/history.html',
{'reversions': reversions, 'object': instance}
)
>>>>>>> 5a88882... Ajoute le réglage reminder et factorise la liste des services
def about_page(request):
......
......@@ -41,6 +41,8 @@ from django.utils import timezone
from django.contrib.auth.models import Group, Permission
from django.utils.translation import ugettext_lazy as _
from machines.models import Interface, Machine, Nas
from topologie.models import Port
from preferences.models import OptionalUser
from re2o.utils import remove_user_room, get_input_formats_help_text
from re2o.mixins import FormRevMixin
......@@ -660,3 +662,53 @@ class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
model = User
fields = ['email','local_email_enabled', 'local_email_redirect']
class InitialRegisterForm(forms.Form):
register_room = forms.BooleanField(required=False)
register_machine = forms.BooleanField(required=False)
def __init__(self, *args, **kwargs):
switch_ip = kwargs.pop('switch_ip')
switch_port = kwargs.pop('switch_port')
client_mac = kwargs.pop('client_mac')
self.user = kwargs.pop('user')
if switch_ip and switch_port:
# Looking for a port
port = Port.objects.filter(switch__interface__ipv4__ipv4=switch_ip, port=switch_port).first()
# If a port exists, checking there is a room AND radius
if port:
if port.get_port_profile.radius_type != 'NO' and port.get_port_profile.radius_mode == 'STRICT' and hasattr(port, 'room'):
# Requesting user is not in this room ?
if self.user.room != port.room:
self.new_room = port.room
if client_mac and switch_ip:
# If this interface doesn't already exists
if not Interface.objects.filter(mac_address=client_mac):
self.mac_address = client_mac
self.nas_type = Nas.objects.filter(nas_type__interface__ipv4__ipv4=switch_ip).first()
super(InitialRegisterForm, self).__init__(*args, **kwargs)
if hasattr(self, 'new_room'):
self.fields['register_room'].label = _("New connection from room %s. Is it yours? If that is the case, type OK." % self.new_room)
else:
self.fields.pop('register_room')
if hasattr(self, 'mac_address'):
self.fields['register_machine'].label = _("New connection from new device. Register it? Say Yes to get Internet access from it (MAC Address : %s)." % self.mac_address)
else:
self.fields.pop('register_machine')
def clean_register_room(self):
if self.cleaned_data['register_room']:
if self.user.is_class_adherent:
remove_user_room(self.new_room)
user = self.user.adherent
user.room = self.new_room
user.save()
if self.user.is_class_club:
user = self.user.club
user.room = self.new_room
user.save()
def clean_register_machine(self):
if self.cleaned_data['register_machine']:
if self.mac_address and self.nas_type:
self.user.autoregister_machine(self.mac_address, self.nas_type)
......@@ -21,7 +21,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 2.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-08-23 15:36+0200\n"
"POT-Creation-Date: 2018-08-28 23:07+0200\n"
"PO-Revision-Date: 2018-06-27 23:35+0200\n"
"Last-Translator: Laouen Fernet <laouen.fernet@supelec.fr>\n"
"Language-Team: \n"
......@@ -35,181 +35,197 @@ msgstr ""
msgid "You don't have the right to view this application."
msgstr "Vous n'avez pas le droit de voir cette application."
#: forms.py:70
#: forms.py:72
msgid "Current password"
msgstr "Mot de passe actuel"
#: forms.py:75 forms.py:447
#: forms.py:77 forms.py:449
msgid "New password"
msgstr "Nouveau mot de passe"
#: forms.py:81
#: forms.py:83
msgid "New password confirmation"
msgstr "Confirmation du nouveau mot de passe"
#: forms.py:98
#: forms.py:100
msgid "The new passwords don't match."
msgstr "Les nouveaux mots de passe ne correspondent pas."
#: forms.py:107
#: forms.py:109
msgid "The current password is incorrect."
msgstr "Le mot de passe actuel est incorrect."
#: forms.py:125 forms.py:178 models.py:1550
#: forms.py:127 forms.py:180 models.py:1550
msgid "Password"
msgstr "Mot de passe"
#: forms.py:131 forms.py:184
#: forms.py:133 forms.py:186
msgid "Password confirmation"
msgstr "Confirmation du mot de passe"
#: forms.py:136 forms.py:227
#: forms.py:138 forms.py:229
msgid "Is admin"
msgstr "Est admin"
#: forms.py:146
#: forms.py:148
msgid "You can't use an internal address as your external address."
msgstr ""
"Vous ne pouvez pas utiliser une adresse interne pour votre adresse externe."
#: forms.py:159 forms.py:208
#: forms.py:161 forms.py:210
msgid "The passwords don't match."
msgstr "Les mots de passe ne correspondent pas."
#: forms.py:236
#: forms.py:238
#, python-format
msgid "User is admin: %s"
msgstr "L'utilisateur est admin : %s"
#: forms.py:284 templates/users/aff_clubs.html:38
#: forms.py:286 templates/users/aff_clubs.html:38
#: templates/users/aff_listright.html:63 templates/users/aff_listright.html:168
#: templates/users/aff_users.html:39 templates/users/profil.html:169
#: templates/users/profil.html:283 templates/users/profil.html:302
msgid "Username"
msgstr "Pseudo"
#: forms.py:299
#: forms.py:301
msgid "Impossible to archive users whose end access date is in the future."
msgstr ""
"Impossible d'archiver des utilisateurs dont la date de fin de connexion est "
"dans le futur."
#: forms.py:311 templates/users/profil.html:282 templates/users/profil.html:301
#: forms.py:313 templates/users/profil.html:282 templates/users/profil.html:301
msgid "First name"
msgstr "Prénom"
#: forms.py:312 templates/users/aff_users.html:37
#: forms.py:314 templates/users/aff_users.html:37
#: templates/users/profil.html:165 templates/users/profil.html:281
#: templates/users/profil.html:300
msgid "Surname"
msgstr "Nom"
#: forms.py:313 models.py:1551 templates/users/aff_emailaddress.html:36
#: forms.py:315 models.py:1551 templates/users/aff_emailaddress.html:36
#: templates/users/profil.html:171
msgid "Email address"
msgstr "Adresse mail"
#: forms.py:314 forms.py:385 forms.py:516 templates/users/aff_schools.html:37
#: forms.py:316 forms.py:387 forms.py:518 templates/users/aff_schools.html:37
#: templates/users/profil.html:181
msgid "School"
msgstr "Établissement"
#: forms.py:315 forms.py:386 models.py:1204
#: forms.py:317 forms.py:388 models.py:1204
#: templates/users/aff_serviceusers.html:34 templates/users/profil.html:183
msgid "Comment"
msgstr "Commentaire"
#: forms.py:316 forms.py:387 templates/users/aff_clubs.html:40
#: forms.py:318 forms.py:389 templates/users/aff_clubs.html:40
#: templates/users/aff_users.html:41 templates/users/profil.html:175
msgid "Room"
msgstr "Chambre"
#: forms.py:317 forms.py:388
#: forms.py:319 forms.py:390
msgid "No room"
msgstr "Pas de chambre"
#: forms.py:318 forms.py:389
#: forms.py:320 forms.py:391
msgid "Select a school"
msgstr "Sélectionnez un établissement"
#: forms.py:319
#: forms.py:321
msgid "Leave empty if you don't have any GPG key."
msgstr "Laissez vide si vous n'avez pas de clé GPG."
#: forms.py:321
#: forms.py:323
msgid "Default shell"
msgstr "Interface système par défaut"
#: forms.py:328 forms.py:656
#: forms.py:330 forms.py:658
msgid "You can't use a {} address."
msgstr "Vous ne pouvez pas utiliser une adresse {}."
#: forms.py:353 forms.py:411
#: forms.py:355 forms.py:413
msgid "A valid telephone number is required."
msgstr "Un numéro de téléphone valide est requis."
#: forms.py:364
#: forms.py:366
msgid "Force the move?"
msgstr "Forcer le déménagement ?"
#: forms.py:384 templates/users/aff_clubs.html:36
#: forms.py:386 templates/users/aff_clubs.html:36
#: templates/users/aff_serviceusers.html:32
msgid "Name"
msgstr "Nom"
#: forms.py:390
#: forms.py:392
msgid "Use a mailing list"
msgstr "Utiliser une liste de diffusion"
#: forms.py:504 templates/users/aff_listright.html:38
#: forms.py:506 templates/users/aff_listright.html:38
msgid "Superuser"
msgstr "Superutilisateur"
#: forms.py:528
#: forms.py:530
msgid "Shell name"
msgstr "Nom de l'interface système"
#: forms.py:547
#: forms.py:549
msgid "Name of the group of rights"
msgstr "Nom du groupe de droits"
#: forms.py:558
#: forms.py:560
msgid "GID. Warning: this field must not be edited after creation."
msgstr "GID. Attention : ce champ ne doit pas être modifié après création."
#: forms.py:566
#: forms.py:568
msgid "Current groups of rights"
msgstr "Groupes de droits actuels"
#: forms.py:583
#: forms.py:585
msgid "Current schools"
msgstr "Établissements actuels"
#: forms.py:601 forms.py:615 templates/users/aff_bans.html:41
#: forms.py:603 forms.py:617 templates/users/aff_bans.html:41
#: templates/users/aff_whitelists.html:41
msgid "End date"
msgstr "Date de fin"
#: forms.py:629 models.py:1748
#: forms.py:631 models.py:1748
msgid "Local part of the email address"
msgstr "Partie locale de l'adresse mail"
#: forms.py:630
#: forms.py:632
msgid "Can't contain @"
msgstr "Ne peut pas contenir @"
#: forms.py:645
#: forms.py:647
msgid "Main email address"
msgstr "Adresse mail principale"
#: forms.py:647
#: forms.py:649
msgid "Redirect local emails"
msgstr "Rediriger les mails locaux"
#: forms.py:649
#: forms.py:651
msgid "Use local emails"
msgstr "Utiliser les mails locaux"
#: forms.py:691
#, python-format
msgid "New connection from room %s. Is it yours? If that is the case, type OK."
msgstr ""
"Nouvelle connexion depuis la chambre %s. Est-ce la vôtre ? Si c'est le cas, "
"tapez OK."
#: forms.py:695
#, python-format
msgid ""
"New connection from new device. Register it? Say Yes to get Internet access "
"from it (MAC Address : %s)."
msgstr ""
"Nouvelle connexion depuis un nouvel appareil. L'enregistrer ? Dites Oui pour "
"avoir accès à Internet depuis cet appareil (adresse MAC : %s)."
#: models.py:105
#, python-format
msgid "The username '%(label)s' contains forbidden characters."
......@@ -722,7 +738,8 @@ msgstr "Confirmer"
#: templates/users/index_schools.html:30
#: templates/users/index_serviceusers.html:30
#: templates/users/index_shell.html:30 templates/users/index_whitelist.html:29
#: templates/users/sidebar.html:52 templates/users/user.html:30
#: templates/users/plugin_out.html:31 templates/users/sidebar.html:52
#: templates/users/user.html:30
msgid "Users"
msgstr "Utilisateurs"
......@@ -816,6 +833,15 @@ msgid "The following users will be archived (%(to_archive_list|length)s):"
msgstr ""
"Les utilisateus suivants vont être archivés (%(to_archive_list|length)s :"
#: templates/users/plugin_out.html:35
msgid ""
"Your machine and your room were successfully registered. Please disconnect "
"and reconnect your Ethernet cable to benefit from a wired connection."
msgstr ""
"Votre machine et votre chambre ont bien été enregistrées. Veuillez"
" débrancher et rebrancher votre câble Ethernet pour bénéficier d'une"
" connexion filaire."
#: templates/users/profil.html:37
#, python-format
msgid "Welcome %(name)s %(surname)s"
......@@ -881,7 +907,7 @@ msgstr " Informations détaillées"
msgid "Edit"
msgstr "Modifier"
#: templates/users/profil.html:133 views.py:282 views.py:1079
#: templates/users/profil.html:133 views.py:283 views.py:1080
msgid "Change the password"
msgstr "Changer le mot de passe"
......@@ -889,7 +915,7 @@ msgstr "Changer le mot de passe"
msgid "Change the state"
msgstr "Changer l'état"
#: templates/users/profil.html:144 views.py:260
#: templates/users/profil.html:144 views.py:261
msgid "Edit the groups"
msgstr "Modifier les groupes"
......@@ -956,7 +982,7 @@ msgstr "Solde"
#: templates/users/profil.html:241
msgid "Refill"
msgstr "Rechager"
msgstr "Recharger"
#: templates/users/profil.html:246
msgid "GPG fingerprint"
......@@ -1002,7 +1028,7 @@ msgstr "Modifier le solde"
msgid "No invoice"
msgstr "Pas de facture"
#: templates/users/profil.html:385 views.py:384
#: templates/users/profil.html:385 views.py:385
msgid "Add a ban"
msgstr "Ajouter un bannissement"
......@@ -1058,7 +1084,7 @@ msgstr " Ajouter une adresse mail"
msgid "Create a club or organisation"
msgstr "Créer un club ou une association"
#: templates/users/sidebar.html:39 views.py:134
#: templates/users/sidebar.html:39 views.py:135
msgid "Create a user"
msgstr "Créer un utilisateur"
......@@ -1095,178 +1121,178 @@ msgstr "Conditions Générales d'Utilisation"
msgid "Summary of the General Terms of Use"
msgstr "Résumé des Conditions Générales d'Utilisation"
#: views.py:122
#: views.py:123
#, python-format
msgid "The user %s was created, an email to set the password was sent."
msgstr ""
"L'utilisateur %s a été créé, un mail pour initialiser le mot de passe a été "
"envoyé."
#: views.py:151
#: views.py:152
#, python-format
msgid "The club %s was created, an email to set the password was sent."
msgstr ""
"Le club %s a été créé, un mail pour initialiser le mot de passe a été envoyé."
#: views.py:158
#: views.py:159
msgid "Create a club"
msgstr "Créer un club"
#: views.py:176
#: views.py:177
msgid "The club was edited."
msgstr "Le club a été modifié."
#: views.py:185
#: views.py:186
msgid "Edit the admins and members"
msgstr "Modifier les admins et les membres"
#: views.py:213
#: views.py:214
msgid "The user was edited."
msgstr "L'utilisateur a été modifié."
#: views.py:219
#: views.py:220
msgid "Edit the user"
msgstr "Modifier l'utilisateur"
#: views.py:233
#: views.py:234
msgid "The state was edited."
msgstr "L'état a été modifié."
#: views.py:239
#: views.py:240
msgid "Edit the state"
msgstr "Modifier l'état"
#: views.py:254
#: views.py:255
msgid "The groups were edited."
msgstr "Les groupes ont été modifiés."
#: views.py:276 views.py:1076
#: views.py:277 views.py:1077
msgid "The password was changed."
msgstr "Le mot de passe a été changé."
#: views.py:294
#: views.py:295
#, python-format
msgid "%s was removed from the group."
msgstr "%s a été retiré du groupe."
#: views.py:304
#: views.py:305
#, python-format
msgid "%s is no longer superuser."
msgstr "%s n'est plus superutilisateur."
#: views.py:317
#: views.py:318
msgid "The service user was created."
msgstr "L'utilisateur service a été créé."
#: views.py:321
#: views.py:322
msgid "Create a service user"
msgstr "Créer un utilisateur service"
#: views.py:338
#: views.py:339
msgid "The service user was edited."
msgstr "L'utilisateur service a été modifié."
#: views.py:341
#: views.py:342
msgid "Edit a service user"
msgstr "Modifier un utilisateur service"
#: views.py:353
#: views.py:354
msgid "The service user was deleted."
msgstr "L'utilisateur service a été supprimé."
#: views.py:373
#: views.py:374
msgid "The ban was added."
msgstr "Le bannissement a été ajouté."
#: views.py:381
#: views.py:382
msgid "Warning: this user already has an active ban."
msgstr "Attention : cet utilisateur a déjà un bannissement actif."
#: views.py:400
#: views.py:401
msgid "The ban was edited."
msgstr "Le bannissement a été modifié."
#: views.py:403
#: views.py:404
msgid "Edit a ban"
msgstr "Modifier un bannissement"
#: views.py:415
#: views.py:416
msgid "The ban was deleted."
msgstr "Le bannissement a été supprimé."
#: views.py:442
#: views.py:443
msgid "The whitelist was added."
msgstr "L'accès gracieux a été ajouté."
#: views.py:450
#: views.py:451
msgid "Warning: this user already has an active whitelist."
msgstr "Attention : cet utilisateur a déjà un accès gracieux actif."
#: views.py:453
#: views.py:454
msgid "Add a whitelist"
msgstr "Ajouter un accès gracieux"
#: views.py:473
#: views.py:474
msgid "The whitelist was edited."
msgstr "L'accès gracieux a été ajouté."
#: views.py:476
#: views.py:477
msgid "Edit a whitelist"
msgstr "Modifier un accès gracieux"
#: views.py:488
#: views.py:489
msgid "The whitelist was deleted."
msgstr "L'accès gracieux a été supprimé."
#: views.py:512
#: views.py:513
msgid "The local email account was created."
msgstr "Le compte mail local a été créé."
#: views.py:520
#: views.py:521
msgid "Add a local email account"
msgstr "Ajouter un compte mail local"
#: views.py:537
#: views.py:538
msgid "The local email account was edited."
msgstr "Le compte mail local a été modifié."
#: views.py:545
#: views.py:546
msgid "Edit a local email account"
msgstr "Modifier un compte mail local"
#: views.py:557
#: views.py:558
msgid "The local email account was deleted."
msgstr "Le compte mail local a été supprimé."
#: views.py:581
#: views.py:582
msgid "The email settings were edited."
msgstr "Les paramètres mail ont été modifiés."
#: views.py:590
#: views.py:591
msgid "Edit the email settings"
msgstr "Modifier les paramètres mail"
#: views.py:604
#: views.py:605
msgid "The school was added."
msgstr "L'établissement a été ajouté."
#: views.py:607
#: views.py:608
msgid "Add a school"
msgstr "Ajouter un établissement"
#: views.py:622
#: views.py:623
msgid "The school was edited."
msgstr "L'établissement a été modifié."
#: views.py:625
#: views.py:626
msgid "Edit a school"
msgstr "Modifier un établissement"
#: views.py:644
#: views.py:645
msgid "The school was deleted."
msgstr "L'établissement a été supprimé."
#: views.py:648
#: views.py:649
#, python-format
msgid ""
"The school %s is assigned to at least one user, impossible to delete it."
......@@ -1274,51 +1300,51 @@ msgstr ""
"L'établissement %s est affecté à au moins un utilisateur, impossible de le "
"supprimer."
#: views.py:652 views.py:764
#: views.py:653 views.py:765
msgid "Delete"
msgstr "Supprimer"
#: views.py:665
#: views.py:666
msgid "The shell was added."
msgstr "L'interface système a été ajoutée."
#: views.py:668
#: views.py:669
msgid "Add a shell"
msgstr "Ajouter une interface système"
#: views.py:682
#: views.py:683
msgid "The shell was edited."
msgstr "L'interface système a été modifiée."
#: views.py:685
#: views.py:686
msgid "Edit a shell"
msgstr "Modifier une interface système"
#: views.py:697
#: views.py:698
msgid "The shell was deleted."
msgstr "L'interface système a été supprimée."
#: views.py:714
#: views.py:715
msgid "The group of rights was added."
msgstr "Le groupe de droits a été ajouté."
#: views.py:717
#: views.py:718
msgid "Add a group of rights"
msgstr "Ajouter un groupe de droits"
#: views.py:735
#: views.py:736
msgid "The group of rights was edited."
msgstr "Le groupe de droits a été modifié."
#: views.py:738
#: views.py:739
msgid "Edit a group of rights"
msgstr "Modifier un groupe de droits"
#: views.py:755
#: views.py:756
msgid "The group of rights was deleted."
msgstr "Le groupe de droits a été supprimé."
#: views.py:760
#: views.py:761
#, python-format
msgid ""
"The group of rights %s is assigned to at least one user, impossible to "
......@@ -1327,36 +1353,52 @@ msgstr ""
"Le groupe de droits %s est affecté à au moins un utilisateur, impossible de "
"le supprimer."
#: views.py:788
#: views.py:789
msgid "Archiving"
msgstr "Archivage"
#: views.py:789
#: views.py:790
#, python-format
msgid "%s users were archived."
msgstr "%s utilisateurs ont été archivés."
#: views.py:1038
#: views.py:1039
msgid "The user doesn't exist."
msgstr "L'utilisateur n'existe pas."
#: views.py:1040 views.py:1048
#: views.py:1041 views.py:1049
msgid "Reset"
msgstr "Réinitialiser"
#: views.py:1045
#: views.py:1046
msgid "An email to reset the password was sent."
msgstr "Un mail pour réinitialiser le mot de passe a été envoyé."
#: views.py:1062
#: views.py:1063
msgid "Error: please contact an admin."
msgstr "Erreur : veuillez contacter un admin."
#: views.py:1074
#: views.py:1075
msgid "Password reset"
msgstr "Réinitialisation du mot de passe"
#: views.py:1114 views.py:1138 views.py:1153
#: views.py:1089
msgid "Incorrect URL, or already registered device"
msgstr "URL incorrect, ou appareil déjà enregistré"
#: views.py:1095
msgid ""
"Successful registration! Please disconnect and reconnect your Ethernet cable "
"to get Internet access."
msgstr ""
"Enregistrement réussi ! Veuillez débrancher et rebrancher votre câble Ethernet"
" pour avoir accès à Internet."
#: views.py:1104
msgid "Register device or room"
msgstr "Enregistrer un appareil ou une chambre"
#: views.py:1139 views.py:1163 views.py:1178
msgid "The mailing list doesn't exist."
msgstr "La liste de diffusion n'existe pas."
......
{% extends "users/sidebar.html" %}
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 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 massive_bootstrap_form %}
{% load static %}
{% load i18n %}
{% block title %}{% trans "Users" %}{% endblock %}
{% block content %}
<h3>{% trans "Your machine and your room were successfully registered. Please disconnect and reconnect your Ethernet cable to benefit from a wired connection." %}</h3>
<center><img src="{% static '/images/rj45.gif' %}" alt="rj45_in_out"></center>
<br/>
<br/>
<br/>
{% endblock %}
......@@ -109,6 +109,7 @@ urlpatterns = [
url(r'^mass_archive/$', views.mass_archive, name='mass-archive'),
url(r'^$', views.index, name='index'),
url(r'^index_clubs/$', views.index_clubs, name='index-clubs'),
url(r'^initial_register/$', views.initial_register, name='initial-register'),
url(r'^rest/ml/std/$',
views.ml_std_list,
name='ml-std-list'),
......
......@@ -105,7 +105,8 @@ from .forms import (
PassForm,
ResetPasswordForm,
ClubAdminandMembersForm,
GroupForm
GroupForm,
InitialRegisterForm
)
......@@ -1081,6 +1082,30 @@ def process_passwd(request, req):
request
)
@login_required
def initial_register(request):
u_form = InitialRegisterForm(request.POST or None, user=request.user, switch_ip=request.GET.get('switch_ip', None), switch_port=request.GET.get('switch_port', None), client_mac=request.GET.get('client_mac', None))
if not u_form.fields:
messages.error(request, _("Incorrect URL, or already registered device"))
return redirect(reverse(
'users:profil',
kwargs={'userid': str(request.user.id)}
))
if u_form.is_valid():
messages.success(request, _("Successful registration! Please"
" disconnect and reconnect your Ethernet"
" cable to get Internet access."))
return form(
{},
'users/plugin_out.html',
request
)
return form(
{'userform': u_form, 'action_name': _("Register device or room")},
'users/user.html',
request
)
class JSONResponse(HttpResponse):
""" Framework Rest """
......