8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-11-30 08:32:26 +00:00

Merge branch 'fix_bugs_divers' into 'dev'

Fix bugs divers

See merge request federez/re2o!356
This commit is contained in:
grizzly 2018-11-16 17:36:00 +01:00
commit f6ac6e4c85
16 changed files with 325 additions and 263 deletions

View file

@ -1000,6 +1000,17 @@ class CNAMERecordSerializer(serializers.ModelSerializer):
model = machines.Domain model = machines.Domain
fields = ('alias', 'hostname') fields = ('alias', 'hostname')
class DNAMERecordSerializer(serializers.ModelSerializer):
"""Serialize `machines.models.Domain` objects with the data needed to
generate a DNAME DNS record.
"""
alias = serializers.CharField(read_only=True)
zone = serializers.CharField(read_only=True)
class Meta:
model = machines.DName
fields = ('alias', 'zone')
class DNSZonesSerializer(serializers.ModelSerializer): class DNSZonesSerializer(serializers.ModelSerializer):
"""Serialize the data about DNS Zones. """Serialize the data about DNS Zones.
@ -1014,14 +1025,14 @@ class DNSZonesSerializer(serializers.ModelSerializer):
a_records = ARecordSerializer(many=True, source='get_associated_a_records') a_records = ARecordSerializer(many=True, source='get_associated_a_records')
aaaa_records = AAAARecordSerializer(many=True, source='get_associated_aaaa_records') aaaa_records = AAAARecordSerializer(many=True, source='get_associated_aaaa_records')
cname_records = CNAMERecordSerializer(many=True, source='get_associated_cname_records') cname_records = CNAMERecordSerializer(many=True, source='get_associated_cname_records')
dname_records = DNAMERecordSerializer(many=True, source='get_associated_dname_records')
sshfp_records = SSHFPInterfaceSerializer(many=True, source='get_associated_sshfp_records') sshfp_records = SSHFPInterfaceSerializer(many=True, source='get_associated_sshfp_records')
class Meta: class Meta:
model = machines.Extension model = machines.Extension
fields = ('name', 'soa', 'ns_records', 'originv4', 'originv6', fields = ('name', 'soa', 'ns_records', 'originv4', 'originv6',
'mx_records', 'txt_records', 'srv_records', 'a_records', 'mx_records', 'txt_records', 'srv_records', 'a_records',
'aaaa_records', 'cname_records', 'sshfp_records') 'aaaa_records', 'cname_records', 'dname_records', 'sshfp_records')
#REMINDER #REMINDER

View file

@ -541,8 +541,8 @@ class ServiceRegenViewSet(viewsets.ModelViewSet):
# Config des switches # Config des switches
class SwitchPortView(generics.ListAPIView): class SwitchPortView(generics.ListAPIView):
"""Exposes the associations between hostname, mac address and IPv4 in """Output each port of a switch, to be serialized with
order to build the DHCP lease files. additionnal informations (profiles etc)
""" """
queryset = topologie.Switch.objects.all().select_related("switchbay").select_related("model__constructor").prefetch_related("ports__custom_profile__vlan_tagged").prefetch_related("ports__custom_profile__vlan_untagged").prefetch_related("ports__machine_interface__domain__extension").prefetch_related("ports__room") queryset = topologie.Switch.objects.all().select_related("switchbay").select_related("model__constructor").prefetch_related("ports__custom_profile__vlan_tagged").prefetch_related("ports__custom_profile__vlan_untagged").prefetch_related("ports__machine_interface__domain__extension").prefetch_related("ports__room")
@ -551,16 +551,14 @@ class SwitchPortView(generics.ListAPIView):
# Rappel fin adhésion # Rappel fin adhésion
class ReminderView(generics.ListAPIView): class ReminderView(generics.ListAPIView):
"""Exposes the associations between hostname, mac address and IPv4 in """Output for users to remind an end of their subscription.
order to build the DHCP lease files.
""" """
queryset = preferences.Reminder.objects.all() queryset = preferences.Reminder.objects.all()
serializer_class = serializers.ReminderSerializer serializer_class = serializers.ReminderSerializer
class RoleView(generics.ListAPIView): class RoleView(generics.ListAPIView):
"""Exposes the associations between hostname, mac address and IPv4 in """Output of roles for each server
order to build the DHCP lease files.
""" """
queryset = machines.Role.objects.all().prefetch_related('servers') queryset = machines.Role.objects.all().prefetch_related('servers')
serializer_class = serializers.RoleSerializer serializer_class = serializers.RoleSerializer

View file

@ -47,7 +47,10 @@ from users.models import User
from re2o.settings import LOGO_PATH from re2o.settings import LOGO_PATH
from re2o import settings from re2o import settings
from re2o.views import form from re2o.views import form
from re2o.utils import SortTable, re2o_paginator from re2o.base import (
SortTable,
re2o_paginator,
)
from re2o.acl import ( from re2o.acl import (
can_create, can_create,
can_edit, can_edit,

View file

@ -102,15 +102,18 @@ from re2o.utils import (
all_baned, all_baned,
all_has_access, all_has_access,
all_adherent, all_adherent,
all_active_assigned_interfaces_count,
all_active_interfaces_count,
)
from re2o.base import (
re2o_paginator, re2o_paginator,
SortTable
) )
from re2o.acl import ( from re2o.acl import (
can_view_all, can_view_all,
can_view_app, can_view_app,
can_edit_history, can_edit_history,
) )
from re2o.utils import all_active_assigned_interfaces_count
from re2o.utils import all_active_interfaces_count, SortTable
@login_required @login_required

View file

@ -741,6 +741,9 @@ class Extension(RevMixin, AclMixin, models.Model):
.filter(cname__interface_parent__in=all_active_assigned_interfaces()) .filter(cname__interface_parent__in=all_active_assigned_interfaces())
.prefetch_related('cname')) .prefetch_related('cname'))
def get_associated_dname_records(self):
return (DName.objects.filter(alias=self))
@staticmethod @staticmethod
def can_use_all(user_request, *_args, **_kwargs): def can_use_all(user_request, *_args, **_kwargs):
"""Superdroit qui permet d'utiliser toutes les extensions sans """Superdroit qui permet d'utiliser toutes les extensions sans
@ -1631,18 +1634,6 @@ class Role(RevMixin, AclMixin, models.Model):
verbose_name = _("server role") verbose_name = _("server role")
verbose_name_plural = _("server roles") verbose_name_plural = _("server roles")
@classmethod
def get_instance(cls, roleid, *_args, **_kwargs):
"""Get the Role instance with roleid.
Args:
roleid: The id
Returns:
The role.
"""
return cls.objects.get(pk=roleid)
@classmethod @classmethod
def interface_for_roletype(cls, roletype): def interface_for_roletype(cls, roletype):
"""Return interfaces for a roletype""" """Return interfaces for a roletype"""
@ -1657,14 +1648,6 @@ class Role(RevMixin, AclMixin, models.Model):
machine__interface__role=cls.objects.filter(specific_role=roletype) machine__interface__role=cls.objects.filter(specific_role=roletype)
) )
@classmethod
def get_instance(cls, roleid, *_args, **_kwargs):
"""Get the Machine instance with machineid.
:param userid: The id
:return: The user
"""
return cls.objects.get(pk=roleid)
@classmethod @classmethod
def interface_for_roletype(cls, roletype): def interface_for_roletype(cls, roletype):
"""Return interfaces for a roletype""" """Return interfaces for a roletype"""

View file

@ -95,9 +95,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% if interfaceform %} {% if interfaceform %}
<h3>{% trans "Interface" %}</h3> <h3>{% trans "Interface" %}</h3>
{% if i_mbf_param %} {% if i_mbf_param %}
{% massive_bootstrap_form interfaceform 'ipv4,machine' mbf_param=i_mbf_param %} {% massive_bootstrap_form interfaceform 'ipv4,machine,port_lists' mbf_param=i_mbf_param %}
{% else %} {% else %}
{% massive_bootstrap_form interfaceform 'ipv4,machine' %} {% massive_bootstrap_form interfaceform 'ipv4,machine,port_lists' %}
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if domainform %} {% if domainform %}
@ -146,7 +146,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endif %} {% endif %}
{% if aliasform %} {% if aliasform %}
<h3>{% trans "Alias" %}</h3> <h3>{% trans "Alias" %}</h3>
{% bootstrap_form aliasform %} {% massive_bootstrap_form aliasform 'extension' %}
{% endif %} {% endif %}
{% if serviceform %} {% if serviceform %}
<h3>{% trans "Service" %}</h3> <h3>{% trans "Service" %}</h3>

View file

@ -55,6 +55,8 @@ from re2o.acl import (
from re2o.utils import ( from re2o.utils import (
all_active_assigned_interfaces, all_active_assigned_interfaces,
filter_active_interfaces, filter_active_interfaces,
)
from re2o.base import (
SortTable, SortTable,
re2o_paginator, re2o_paginator,
) )

267
re2o/base.py Normal file
View file

@ -0,0 +1,267 @@
# -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
# se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics.
#
# Copyright © 2018 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.
# -*- coding: utf-8 -*-
"""
Regroupe les fonctions transversales utiles
Et non corrélées/dépendantes des autres applications
"""
import smtplib
from django.utils.translation import ugettext_lazy as _
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from re2o.settings import EMAIL_HOST
# Mapping of srtftime format for better understanding
# https://docs.python.org/3.6/library/datetime.html#strftime-strptime-behavior
datetime_mapping={
'%a': '%a',
'%A': '%A',
'%w': '%w',
'%d': 'dd',
'%b': '%b',
'%B': '%B',
'%m': 'mm',
'%y': 'yy',
'%Y': 'yyyy',
'%H': 'HH',
'%I': 'HH(12h)',
'%p': 'AMPM',
'%M': 'MM',
'%S': 'SS',
'%f': 'µµ',
'%z': 'UTC(+/-HHMM)',
'%Z': 'UTC(TZ)',
'%j': '%j',
'%U': 'ww',
'%W': 'ww',
'%c': '%c',
'%x': '%x',
'%X': '%X',
'%%': '%%',
}
def smtp_check(local_part):
"""Return True if the local_part is already taken
False if available"""
try:
srv = smtplib.SMTP(EMAIL_HOST)
srv.putcmd("vrfy", local_part)
reply_code = srv.getreply()[0]
srv.close()
if reply_code in [250, 252]:
return True, _("This domain is already taken")
except:
return True, _("Smtp unreachable")
return False, None
def convert_datetime_format(format):
i=0
new_format = ""
while i < len(format):
if format[i] == '%':
char = format[i:i+2]
new_format += datetime_mapping.get(char, char)
i += 2
else:
new_format += format[i]
i += 1
return new_format
def get_input_formats_help_text(input_formats):
"""Returns a help text about the possible input formats"""
if len(input_formats) > 1:
help_text_template="Format: {main} {more}"
else:
help_text_template="Format: {main}"
more_text_template="<i class=\"fa fa-question-circle\" title=\"{}\"></i>"
help_text = help_text_template.format(
main=convert_datetime_format(input_formats[0]),
more=more_text_template.format(
'\n'.join(map(convert_datetime_format, input_formats))
)
)
return help_text
class SortTable:
""" Class gathering uselful stuff to sort the colums of a table, according
to the column and order requested. It's used with a dict of possible
values and associated model_fields """
# All the possible possible values
# The naming convention is based on the URL or the views function
# The syntax to describe the sort to apply is a dict where the keys are
# the url value and the values are a list of model field name to use to
# order the request. They are applied in the order they are given.
# A 'default' might be provided to specify what to do if the requested col
# doesn't match any keys.
USERS_INDEX = {
'user_name': ['name'],
'user_surname': ['surname'],
'user_pseudo': ['pseudo'],
'user_room': ['room'],
'default': ['state', 'pseudo']
}
USERS_INDEX_BAN = {
'ban_user': ['user__pseudo'],
'ban_start': ['date_start'],
'ban_end': ['date_end'],
'default': ['-date_end']
}
USERS_INDEX_WHITE = {
'white_user': ['user__pseudo'],
'white_start': ['date_start'],
'white_end': ['date_end'],
'default': ['-date_end']
}
USERS_INDEX_SCHOOL = {
'school_name': ['name'],
'default': ['name']
}
MACHINES_INDEX = {
'machine_name': ['name'],
'default': ['pk']
}
COTISATIONS_INDEX = {
'cotis_user': ['user__pseudo'],
'cotis_paiement': ['paiement__moyen'],
'cotis_date': ['date'],
'cotis_id': ['id'],
'default': ['-date']
}
COTISATIONS_CUSTOM = {
'invoice_date': ['date'],
'invoice_id': ['id'],
'invoice_recipient': ['recipient'],
'invoice_address': ['address'],
'invoice_payment': ['payment'],
'default': ['-date']
}
COTISATIONS_CONTROL = {
'control_name': ['user__adherent__name'],
'control_surname': ['user__surname'],
'control_paiement': ['paiement'],
'control_date': ['date'],
'control_valid': ['valid'],
'control_control': ['control'],
'control_id': ['id'],
'control_user-id': ['user__id'],
'default': ['-date']
}
TOPOLOGIE_INDEX = {
'switch_dns': ['interface__domain__name'],
'switch_ip': ['interface__ipv4__ipv4'],
'switch_loc': ['switchbay__name'],
'switch_ports': ['number'],
'switch_stack': ['stack__name'],
'default': ['switchbay', 'stack', 'stack_member_id']
}
TOPOLOGIE_INDEX_PORT = {
'port_port': ['port'],
'port_room': ['room__name'],
'port_interface': ['machine_interface__domain__name'],
'port_related': ['related__switch__name'],
'port_radius': ['radius'],
'port_vlan': ['vlan_force__name'],
'default': ['port']
}
TOPOLOGIE_INDEX_ROOM = {
'room_name': ['name'],
'default': ['name']
}
TOPOLOGIE_INDEX_BUILDING = {
'building_name': ['name'],
'default': ['name']
}
TOPOLOGIE_INDEX_BORNE = {
'ap_name': ['interface__domain__name'],
'ap_ip': ['interface__ipv4__ipv4'],
'ap_mac': ['interface__mac_address'],
'default': ['interface__domain__name']
}
TOPOLOGIE_INDEX_STACK = {
'stack_name': ['name'],
'stack_id': ['stack_id'],
'default': ['stack_id'],
}
TOPOLOGIE_INDEX_MODEL_SWITCH = {
'model-switch_name': ['reference'],
'model-switch_contructor': ['constructor__name'],
'default': ['reference'],
}
TOPOLOGIE_INDEX_SWITCH_BAY = {
'switch-bay_name': ['name'],
'switch-bay_building': ['building__name'],
'default': ['name'],
}
TOPOLOGIE_INDEX_CONSTRUCTOR_SWITCH = {
'constructor-switch_name': ['name'],
'default': ['name'],
}
LOGS_INDEX = {
'sum_date': ['revision__date_created'],
'default': ['-revision__date_created'],
}
LOGS_STATS_LOGS = {
'logs_author': ['user__name'],
'logs_date': ['date_created'],
'default': ['-date_created']
}
@staticmethod
def sort(request, col, order, values):
""" Check if the given values are possible and add .order_by() and
a .reverse() as specified according to those values """
fields = values.get(col, None)
if not fields:
fields = values.get('default', [])
request = request.order_by(*fields)
if values.get(col, None) and order == 'desc':
return request.reverse()
else:
return request
def re2o_paginator(request, query_set, pagination_number):
"""Paginator script for list display in re2o.
:request:
:query_set: Query_set to paginate
:pagination_number: Number of entries to display"""
paginator = Paginator(query_set, pagination_number)
page = request.GET.get('page')
try:
results = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
results = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
results = paginator.page(paginator.num_pages)
return results

View file

@ -114,9 +114,9 @@ class CryptPasswordHasher(hashers.BasePasswordHasher):
Check password against encoded using CRYPT algorithm Check password against encoded using CRYPT algorithm
""" """
assert encoded.startswith(self.algorithm) assert encoded.startswith(self.algorithm)
salt = hash_password_salt(challenge_password) salt = hash_password_salt(encoded)
return constant_time_compare(crypt.crypt(password.encode(), salt), return constant_time_compare(crypt.crypt(password, salt),
challenge.encode()) encoded)
def safe_summary(self, encoded): def safe_summary(self, encoded):
""" """

View file

@ -38,55 +38,11 @@ from __future__ import unicode_literals
from django.utils import timezone from django.utils import timezone
from django.db.models import Q from django.db.models import Q
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from cotisations.models import Cotisation, Facture, Vente from cotisations.models import Cotisation, Facture, Vente
from machines.models import Interface, Machine from machines.models import Interface, Machine
from users.models import Adherent, User, Ban, Whitelist from users.models import Adherent, User, Ban, Whitelist
# Mapping of srtftime format for better understanding
# https://docs.python.org/3.6/library/datetime.html#strftime-strptime-behavior
datetime_mapping={
'%a': '%a',
'%A': '%A',
'%w': '%w',
'%d': 'dd',
'%b': '%b',
'%B': '%B',
'%m': 'mm',
'%y': 'yy',
'%Y': 'yyyy',
'%H': 'HH',
'%I': 'HH(12h)',
'%p': 'AMPM',
'%M': 'MM',
'%S': 'SS',
'%f': 'µµ',
'%z': 'UTC(+/-HHMM)',
'%Z': 'UTC(TZ)',
'%j': '%j',
'%U': 'ww',
'%W': 'ww',
'%c': '%c',
'%x': '%x',
'%X': '%X',
'%%': '%%',
}
def convert_datetime_format(format):
i=0
new_format = ""
while i < len(format):
if format[i] == '%':
char = format[i:i+2]
new_format += datetime_mapping.get(char, char)
i += 2
else:
new_format += format[i]
i += 1
return new_format
def all_adherent(search_time=None): def all_adherent(search_time=None):
""" Fonction renvoyant tous les users adherents. Optimisee pour n'est """ Fonction renvoyant tous les users adherents. Optimisee pour n'est
@ -203,164 +159,6 @@ def all_active_assigned_interfaces_count():
return all_active_interfaces_count().filter(ipv4__isnull=False) return all_active_interfaces_count().filter(ipv4__isnull=False)
class SortTable:
""" Class gathering uselful stuff to sort the colums of a table, according
to the column and order requested. It's used with a dict of possible
values and associated model_fields """
# All the possible possible values
# The naming convention is based on the URL or the views function
# The syntax to describe the sort to apply is a dict where the keys are
# the url value and the values are a list of model field name to use to
# order the request. They are applied in the order they are given.
# A 'default' might be provided to specify what to do if the requested col
# doesn't match any keys.
USERS_INDEX = {
'user_name': ['name'],
'user_surname': ['surname'],
'user_pseudo': ['pseudo'],
'user_room': ['room'],
'default': ['state', 'pseudo']
}
USERS_INDEX_BAN = {
'ban_user': ['user__pseudo'],
'ban_start': ['date_start'],
'ban_end': ['date_end'],
'default': ['-date_end']
}
USERS_INDEX_WHITE = {
'white_user': ['user__pseudo'],
'white_start': ['date_start'],
'white_end': ['date_end'],
'default': ['-date_end']
}
USERS_INDEX_SCHOOL = {
'school_name': ['name'],
'default': ['name']
}
MACHINES_INDEX = {
'machine_name': ['name'],
'default': ['pk']
}
COTISATIONS_INDEX = {
'cotis_user': ['user__pseudo'],
'cotis_paiement': ['paiement__moyen'],
'cotis_date': ['date'],
'cotis_id': ['id'],
'default': ['-date']
}
COTISATIONS_CUSTOM = {
'invoice_date': ['date'],
'invoice_id': ['id'],
'invoice_recipient': ['recipient'],
'invoice_address': ['address'],
'invoice_payment': ['payment'],
'default': ['-date']
}
COTISATIONS_CONTROL = {
'control_name': ['user__adherent__name'],
'control_surname': ['user__surname'],
'control_paiement': ['paiement'],
'control_date': ['date'],
'control_valid': ['valid'],
'control_control': ['control'],
'control_id': ['id'],
'control_user-id': ['user__id'],
'default': ['-date']
}
TOPOLOGIE_INDEX = {
'switch_dns': ['interface__domain__name'],
'switch_ip': ['interface__ipv4__ipv4'],
'switch_loc': ['switchbay__name'],
'switch_ports': ['number'],
'switch_stack': ['stack__name'],
'default': ['switchbay', 'stack', 'stack_member_id']
}
TOPOLOGIE_INDEX_PORT = {
'port_port': ['port'],
'port_room': ['room__name'],
'port_interface': ['machine_interface__domain__name'],
'port_related': ['related__switch__name'],
'port_radius': ['radius'],
'port_vlan': ['vlan_force__name'],
'default': ['port']
}
TOPOLOGIE_INDEX_ROOM = {
'room_name': ['name'],
'default': ['name']
}
TOPOLOGIE_INDEX_BUILDING = {
'building_name': ['name'],
'default': ['name']
}
TOPOLOGIE_INDEX_BORNE = {
'ap_name': ['interface__domain__name'],
'ap_ip': ['interface__ipv4__ipv4'],
'ap_mac': ['interface__mac_address'],
'default': ['interface__domain__name']
}
TOPOLOGIE_INDEX_STACK = {
'stack_name': ['name'],
'stack_id': ['stack_id'],
'default': ['stack_id'],
}
TOPOLOGIE_INDEX_MODEL_SWITCH = {
'model-switch_name': ['reference'],
'model-switch_contructor': ['constructor__name'],
'default': ['reference'],
}
TOPOLOGIE_INDEX_SWITCH_BAY = {
'switch-bay_name': ['name'],
'switch-bay_building': ['building__name'],
'default': ['name'],
}
TOPOLOGIE_INDEX_CONSTRUCTOR_SWITCH = {
'constructor-switch_name': ['name'],
'default': ['name'],
}
LOGS_INDEX = {
'sum_date': ['revision__date_created'],
'default': ['-revision__date_created'],
}
LOGS_STATS_LOGS = {
'logs_author': ['user__name'],
'logs_date': ['date_created'],
'default': ['-date_created']
}
@staticmethod
def sort(request, col, order, values):
""" Check if the given values are possible and add .order_by() and
a .reverse() as specified according to those values """
fields = values.get(col, None)
if not fields:
fields = values.get('default', [])
request = request.order_by(*fields)
if values.get(col, None) and order == 'desc':
return request.reverse()
else:
return request
def re2o_paginator(request, query_set, pagination_number):
"""Paginator script for list display in re2o.
:request:
:query_set: Query_set to paginate
:pagination_number: Number of entries to display"""
paginator = Paginator(query_set, pagination_number)
page = request.GET.get('page')
try:
results = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
results = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
results = paginator.page(paginator.num_pages)
return results
def remove_user_room(room): def remove_user_room(room):
""" Déménage de force l'ancien locataire de la chambre """ """ Déménage de force l'ancien locataire de la chambre """
try: try:
@ -370,18 +168,3 @@ def remove_user_room(room):
user.room = None user.room = None
user.save() user.save()
def get_input_formats_help_text(input_formats):
"""Returns a help text about the possible input formats"""
if len(input_formats) > 1:
help_text_template="Format: {main} {more}"
else:
help_text_template="Format: {main}"
more_text_template="<i class=\"fa fa-question-circle\" title=\"{}\"></i>"
help_text = help_text_template.format(
main=convert_datetime_format(input_formats[0]),
more=more_text_template.format(
'\n'.join(map(convert_datetime_format, input_formats))
)
)
return help_text

View file

@ -27,7 +27,7 @@ from __future__ import unicode_literals
from django import forms from django import forms
from django.forms import Form from django.forms import Form
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from re2o.utils import get_input_formats_help_text from re2o.base import get_input_formats_help_text
CHOICES_USER = ( CHOICES_USER = (
('0', _("Active")), ('0', _("Active")),

View file

@ -46,7 +46,7 @@ from search.forms import (
CHOICES_AFF, CHOICES_AFF,
initial_choices initial_choices
) )
from re2o.utils import SortTable from re2o.base import SortTable
from re2o.acl import can_view_all from re2o.acl import can_view_all

View file

@ -48,7 +48,10 @@ from django.utils.translation import ugettext as _
import tempfile import tempfile
from users.views import form from users.views import form
from re2o.utils import re2o_paginator, SortTable from re2o.base import (
re2o_paginator,
SortTable,
)
from re2o.acl import ( from re2o.acl import (
can_create, can_create,
can_edit, can_edit,

View file

@ -45,7 +45,8 @@ from django.utils.safestring import mark_safe
from machines.models import Interface, Machine, Nas from machines.models import Interface, Machine, Nas
from topologie.models import Port from topologie.models import Port
from preferences.models import OptionalUser from preferences.models import OptionalUser
from re2o.utils import remove_user_room, get_input_formats_help_text from re2o.utils import remove_user_room
from re2o.base import get_input_formats_help_text
from re2o.mixins import FormRevMixin from re2o.mixins import FormRevMixin
from re2o.field_permissions import FieldPermissionFormMixin from re2o.field_permissions import FieldPermissionFormMixin
@ -444,6 +445,7 @@ class ClubForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
'school', 'school',
'comment', 'comment',
'room', 'room',
'email',
'telephone', 'telephone',
'email', 'email',
'shell', 'shell',

View file

@ -81,6 +81,7 @@ from re2o.settings import LDAP, GID_RANGES, UID_RANGES
from re2o.login import hashNT from re2o.login import hashNT
from re2o.field_permissions import FieldPermissionModelMixin from re2o.field_permissions import FieldPermissionModelMixin
from re2o.mixins import AclMixin, RevMixin from re2o.mixins import AclMixin, RevMixin
from re2o.base import smtp_check
from cotisations.models import Cotisation, Facture, Paiement, Vente from cotisations.models import Cotisation, Facture, Paiement, Vente
from machines.models import Domain, Interface, Machine, regen from machines.models import Domain, Interface, Machine, regen
@ -576,7 +577,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser,
mac_refresh : synchronise les machines de l'user mac_refresh : synchronise les machines de l'user
group_refresh : synchronise les group de l'user group_refresh : synchronise les group de l'user
Si l'instance n'existe pas, on crée le ldapuser correspondant""" Si l'instance n'existe pas, on crée le ldapuser correspondant"""
if sys.version_info[0] >= 3: if sys.version_info[0] >= 3 and self.state != self.STATE_ARCHIVE and\
self.state != self.STATE_DISABLED:
self.refresh_from_db() self.refresh_from_db()
try: try:
user_ldap = LdapUser.objects.get(uidNumber=self.uid_number) user_ldap = LdapUser.objects.get(uidNumber=self.uid_number)
@ -1889,6 +1891,9 @@ class EMailAddress(RevMixin, AclMixin, models.Model):
def clean(self, *args, **kwargs): def clean(self, *args, **kwargs):
self.local_part = self.local_part.lower() self.local_part = self.local_part.lower()
if "@" in self.local_part: if "@" in self.local_part or "+" in self.local_part:
raise ValidationError(_("The local part must not contain @.")) raise ValidationError(_("The local part must not contain @ or +."))
result, reason = smtp_check(self.local_part)
if result:
raise ValidationError(reason)
super(EMailAddress, self).clean(*args, **kwargs) super(EMailAddress, self).clean(*args, **kwargs)

View file

@ -57,8 +57,10 @@ from preferences.models import OptionalUser, GeneralOption, AssoOption
from re2o.views import form from re2o.views import form
from re2o.utils import ( from re2o.utils import (
all_has_access, all_has_access,
SortTable, )
re2o_paginator from re2o.base import (
re2o_paginator,
SortTable
) )
from re2o.acl import ( from re2o.acl import (
can_create, can_create,