Compare commits
4 commits
69d2625eb7
...
9e06b199b5
Author | SHA1 | Date | |
---|---|---|---|
9e06b199b5 | |||
9f06547759 | |||
21f774a4da | |||
8ecf006137 |
40 changed files with 360 additions and 410 deletions
|
@ -36,6 +36,6 @@ from re2o.views import AutocompleteViewMixin
|
|||
from .models import Banque
|
||||
|
||||
|
||||
# pylint disable=too-many-ancestors
|
||||
# pylint: disable=too-many-ancestors
|
||||
class BanqueAutocomplete(AutocompleteViewMixin):
|
||||
obj_type = Banque
|
||||
|
|
|
@ -41,6 +41,6 @@ def can_view(user, *args, **kwargs):
|
|||
can = user.has_module_perms("admin")
|
||||
return (
|
||||
can,
|
||||
None if can else _("You don't have the right to view this" " application."),
|
||||
None if can else _("You don't have the right to view this application."),
|
||||
("logs",),
|
||||
)
|
||||
|
|
|
@ -30,7 +30,6 @@ from django.utils.translation import ugettext_lazy as _
|
|||
# Import all models in which there are classes to be filtered on
|
||||
import cotisations.models
|
||||
import machines.models
|
||||
import preferences.models
|
||||
import topologie.models
|
||||
import users.models
|
||||
from re2o.base import get_input_formats_help_text
|
||||
|
@ -79,31 +78,24 @@ def classes_for_action_type(action_type):
|
|||
A list containing the class names corresponding to the action type
|
||||
filter.
|
||||
"""
|
||||
if action_type == "users":
|
||||
return [
|
||||
action_type_map = {
|
||||
"users": [
|
||||
users.models.User.__name__,
|
||||
users.models.Adherent.__name__,
|
||||
users.models.Club.__name__,
|
||||
users.models.EMailAddress.__name__,
|
||||
]
|
||||
],
|
||||
"machines": [
|
||||
machines.models.Machine.__name__,
|
||||
machines.models.Interface.__name__,
|
||||
],
|
||||
"subscriptions": [all_classes(cotisations.models)],
|
||||
"whitelists": [users.models.Whitelist.__name__],
|
||||
"bans": [users.models.Ban.__name__],
|
||||
"topology": all_classes(topologie.models),
|
||||
}
|
||||
|
||||
if action_type == "machines":
|
||||
return [machines.models.Machine.__name__, machines.models.Interface.__name__]
|
||||
|
||||
if action_type == "subscriptions":
|
||||
return all_classes(cotisations.models)
|
||||
|
||||
if action_type == "whitelists":
|
||||
return [users.models.Whitelist.__name__]
|
||||
|
||||
if action_type == "bans":
|
||||
return [users.models.Ban.__name__]
|
||||
|
||||
if action_type == "topology":
|
||||
return all_classes(topologie.models)
|
||||
|
||||
# "all" is a special case, just return None
|
||||
return None
|
||||
return action_type_map.get(action_type, None)
|
||||
|
||||
|
||||
class ActionsSearchForm(Form):
|
||||
|
@ -126,7 +118,7 @@ class ActionsSearchForm(Form):
|
|||
end_date = forms.DateField(required=False, label=_("End date"))
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ActionsSearchForm, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["start_date"].help_text = get_input_formats_help_text(
|
||||
self.fields["start_date"].input_formats
|
||||
)
|
||||
|
@ -146,7 +138,7 @@ class MachineHistorySearchForm(Form):
|
|||
e = forms.DateField(required=False, label=_("End date"))
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MachineHistorySearchForm, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["s"].help_text = get_input_formats_help_text(
|
||||
self.fields["s"].input_formats
|
||||
)
|
||||
|
|
130
logs/models.py
130
logs/models.py
|
@ -46,13 +46,13 @@ def make_version_filter(key, value):
|
|||
"""
|
||||
# The lookup is done in a json string, so it has to be formated
|
||||
# based on the value's type (to add " or not)
|
||||
if type(value) is str:
|
||||
formatted_value = '"{}"'.format(value)
|
||||
if isinstance(value, str):
|
||||
formatted_value = f'"{value}"'
|
||||
else:
|
||||
formatted_value = str(value)
|
||||
|
||||
return Q(serialized_data__contains='"{}": {},'.format(key, formatted_value)) | Q(
|
||||
serialized_data__contains='"{}": {}}}'.format(key, formatted_value)
|
||||
return Q(serialized_data__contains=f'"{key}": {formatted_value},') | Q(
|
||||
serialized_data__contains=f'"{key}": {formatted_value}}}'
|
||||
)
|
||||
|
||||
|
||||
|
@ -101,20 +101,15 @@ class MachineHistorySearchEvent:
|
|||
)
|
||||
|
||||
def __repr__(self):
|
||||
return "{} ({} - ): from {} to {} ({})".format(
|
||||
self.machine,
|
||||
self.mac,
|
||||
self.ipv4,
|
||||
self.start_date,
|
||||
self.end_date,
|
||||
self.comment or "No comment",
|
||||
)
|
||||
return f'{self.machine} ({self.mac} - {self.ipv4}): from {self.start_date} to {self.end_date} ({self.comment or "No comment"})'
|
||||
|
||||
|
||||
class MachineHistorySearch:
|
||||
def __init__(self):
|
||||
self.events = []
|
||||
self._last_evt = None
|
||||
self.start = None
|
||||
self.end = None
|
||||
|
||||
def get(self, search, params):
|
||||
"""Get the events in machine histories related to the search.
|
||||
|
@ -309,7 +304,7 @@ class RelatedHistory:
|
|||
self.name = version.object_repr
|
||||
|
||||
if self.model_name:
|
||||
self.name = "{}: {}".format(self.model_name.title(), self.name)
|
||||
self.name = f"{self.model_name.title()}: {self.name}"
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.model_name == other.model_name and self.object_id == other.object_id
|
||||
|
@ -336,7 +331,7 @@ class HistoryEvent:
|
|||
self.performed_by = version.revision.user
|
||||
self.comment = version.revision.get_comment() or None
|
||||
|
||||
def _repr(self, name, value):
|
||||
def _repr(self, _name, value):
|
||||
"""Get the appropriate representation of the given field.
|
||||
|
||||
Args:
|
||||
|
@ -352,16 +347,19 @@ class HistoryEvent:
|
|||
|
||||
return value
|
||||
|
||||
def edits(self, hide=["password", "pwd_ntlm"]):
|
||||
def edits(self, hide=None):
|
||||
"""Get the list of the changes performed during this event.
|
||||
|
||||
Args:
|
||||
hide: the list of fields for which not to show details (default:
|
||||
[]).
|
||||
["password", "pwd_ntlm"]).
|
||||
|
||||
Returns:
|
||||
The list of fields edited by the event to display.
|
||||
"""
|
||||
if hide is None:
|
||||
hide = ["password", "pwd_ntlm"]
|
||||
|
||||
edits = []
|
||||
|
||||
for field in self.edited_fields:
|
||||
|
@ -424,7 +422,7 @@ class History:
|
|||
self.name = self._last_version.object_repr
|
||||
return self.events[::-1]
|
||||
|
||||
def _compute_diff(self, v1, v2, ignoring=[]):
|
||||
def _compute_diff(self, v1, v2, ignoring=None):
|
||||
"""Find the edited fields between two versions.
|
||||
|
||||
Args:
|
||||
|
@ -436,9 +434,12 @@ class History:
|
|||
The list of field names in v1 that are different from the ones in
|
||||
v2.
|
||||
"""
|
||||
if ignoring is None:
|
||||
ignoring = []
|
||||
|
||||
fields = []
|
||||
v1_keys = set([k for k in v1.field_dict.keys() if k not in ignoring])
|
||||
v2_keys = set([k for k in v2.field_dict.keys() if k not in ignoring])
|
||||
v1_keys = {k for k in v1.field_dict.keys() if k not in ignoring}
|
||||
v2_keys = {k for k in v2.field_dict.keys() if k not in ignoring}
|
||||
|
||||
common_keys = v1_keys.intersection(v2_keys)
|
||||
fields += list(v2_keys - v1_keys)
|
||||
|
@ -478,6 +479,7 @@ class History:
|
|||
|
||||
class VersionAction(HistoryEvent):
|
||||
def __init__(self, version):
|
||||
super().__init__(version)
|
||||
self.version = version
|
||||
|
||||
def name(self):
|
||||
|
@ -495,7 +497,7 @@ class VersionAction(HistoryEvent):
|
|||
def object_type(self):
|
||||
return apps.get_model(self.application(), self.model_name())
|
||||
|
||||
def edits(self, hide=["password", "pwd_ntlm", "gpg_fingerprint"]):
|
||||
def edits(self, hide=None):
|
||||
"""Get the list of the changes performed during this event.
|
||||
|
||||
Args:
|
||||
|
@ -505,13 +507,16 @@ class VersionAction(HistoryEvent):
|
|||
Returns:
|
||||
The list of fields edited by the event to display.
|
||||
"""
|
||||
if hide is None:
|
||||
hide = ["password", "pwd_ntlm", "gpg_fingerprint"]
|
||||
|
||||
self.previous_version = self._previous_version()
|
||||
|
||||
if self.previous_version is None:
|
||||
return None, None, None
|
||||
|
||||
self.edited_fields = self._compute_diff(self.version, self.previous_version)
|
||||
return super(VersionAction, self).edits(hide)
|
||||
return super().edits(hide)
|
||||
|
||||
def _previous_version(self):
|
||||
"""Get the previous version of self.
|
||||
|
@ -533,7 +538,7 @@ class VersionAction(HistoryEvent):
|
|||
except Exception:
|
||||
return None
|
||||
|
||||
def _compute_diff(self, v1, v2, ignoring=["pwd_ntlm"]):
|
||||
def _compute_diff(self, v1, v2, ignoring=None):
|
||||
"""Find the edited fields between two versions.
|
||||
|
||||
Args:
|
||||
|
@ -545,9 +550,12 @@ class VersionAction(HistoryEvent):
|
|||
The list of field names in v1 that are different from the ones in
|
||||
v2.
|
||||
"""
|
||||
if ignoring is None:
|
||||
ignoring = ["pwd_ntlm"]
|
||||
|
||||
fields = []
|
||||
v1_keys = set([k for k in v1.field_dict.keys() if k not in ignoring])
|
||||
v2_keys = set([k for k in v2.field_dict.keys() if k not in ignoring])
|
||||
v1_keys = {k for k in v1.field_dict.keys() if k not in ignoring}
|
||||
v2_keys = {k for k in v2.field_dict.keys() if k not in ignoring}
|
||||
|
||||
common_keys = v1_keys.intersection(v2_keys)
|
||||
fields += list(v2_keys - v1_keys)
|
||||
|
@ -662,27 +670,25 @@ class UserHistoryEvent(HistoryEvent):
|
|||
groups.append(Group.objects.get(id=gid).name)
|
||||
except Group.DoesNotExist:
|
||||
# TODO: Find the group name in the versions?
|
||||
groups.append("{} ({})".format(_("Deleted"), gid))
|
||||
groups.append(f'{_("Deleted")} ({gid})')
|
||||
|
||||
return ", ".join(groups)
|
||||
elif name == "state":
|
||||
if name == "state":
|
||||
if value is not None:
|
||||
return User.STATES[value][1]
|
||||
else:
|
||||
return _("Unknown")
|
||||
elif name == "email_state":
|
||||
return _("Unknown")
|
||||
if name == "email_state":
|
||||
if value is not None:
|
||||
return User.EMAIL_STATES[value][1]
|
||||
else:
|
||||
return _("Unknown")
|
||||
elif name == "room_id" and value is not None:
|
||||
return _("Unknown")
|
||||
if name == "room_id" and value is not None:
|
||||
# Try to get the room name, if it's not deleted
|
||||
try:
|
||||
return Room.objects.get(id=value)
|
||||
except Room.DoesNotExist:
|
||||
# TODO: Find the room name in the versions?
|
||||
return "{} ({})".format(_("Deleted"), value)
|
||||
elif name == "members" or name == "administrators":
|
||||
return f'{_("Deleted")} ({value})'
|
||||
if name in ("members", "administrators"):
|
||||
if len(value) == 0:
|
||||
# Removed all the club's members
|
||||
return _("None")
|
||||
|
@ -695,13 +701,13 @@ class UserHistoryEvent(HistoryEvent):
|
|||
users.append(User.objects.get(id=uid).pseudo)
|
||||
except User.DoesNotExist:
|
||||
# TODO: Find the user's name in the versions?
|
||||
users.append("{} ({})".format(_("Deleted"), uid))
|
||||
users.append(f'{_("Deleted")} ({uid})')
|
||||
|
||||
return ", ".join(users)
|
||||
|
||||
return super(UserHistoryEvent, self)._repr(name, value)
|
||||
return super()._repr(name, value)
|
||||
|
||||
def edits(self, hide=["password", "pwd_ntlm", "gpg_fingerprint"]):
|
||||
def edits(self, hide=None):
|
||||
"""Get the list of the changes performed during this event.
|
||||
|
||||
Args:
|
||||
|
@ -711,7 +717,10 @@ class UserHistoryEvent(HistoryEvent):
|
|||
Returns:
|
||||
The list of fields edited by the event to display.
|
||||
"""
|
||||
return super(UserHistoryEvent, self).edits(hide)
|
||||
if hide is None:
|
||||
hide = ["password", "pwd_ntlm", "gpg_fingerprint"]
|
||||
|
||||
return super().edits(hide)
|
||||
|
||||
def __eq__(self, other):
|
||||
return (
|
||||
|
@ -727,29 +736,26 @@ class UserHistoryEvent(HistoryEvent):
|
|||
)
|
||||
|
||||
def __repr__(self):
|
||||
return "{} edited fields {} ({})".format(
|
||||
self.performed_by,
|
||||
self.edited_fields or "nothing",
|
||||
self.comment or "No comment",
|
||||
)
|
||||
return f'{self.performed_by} edited fields {self.edited_fields or "nothing"} ({self.comment or "No comment"})'
|
||||
|
||||
|
||||
class UserHistory(History):
|
||||
def __init__(self):
|
||||
super(UserHistory, self).__init__()
|
||||
super().__init__()
|
||||
self.event_type = UserHistoryEvent
|
||||
|
||||
def get(self, user_id, model):
|
||||
def get(self, instance_id, model):
|
||||
"""Get the the list of UserHistoryEvent related to the object.
|
||||
|
||||
Args:
|
||||
user_id: int, the id of the user to lookup.
|
||||
instance_id: int, the id of the user to lookup.
|
||||
|
||||
Returns:
|
||||
The list of UserHistoryEvent, in reverse chronological order,
|
||||
related to the object, or None if nothing was found.
|
||||
"""
|
||||
self.events = []
|
||||
user_id = instance_id
|
||||
|
||||
# Try to find an Adherent object
|
||||
# If it exists, its id will be the same as the user's
|
||||
|
@ -858,21 +864,21 @@ class MachineHistoryEvent(HistoryEvent):
|
|||
try:
|
||||
return User.objects.get(id=value).pseudo
|
||||
except User.DoesNotExist:
|
||||
return "{} ({})".format(_("Deleted"), value)
|
||||
return f'{_("Deleted")} ({value}))'
|
||||
|
||||
return super(MachineHistoryEvent, self)._repr(name, value)
|
||||
return super()._repr(name, value)
|
||||
|
||||
|
||||
class MachineHistory(History):
|
||||
def __init__(self):
|
||||
super(MachineHistory, self).__init__()
|
||||
super().__init__()
|
||||
self.event_type = MachineHistoryEvent
|
||||
|
||||
def get(self, machine_id, model):
|
||||
def get(self, instance_id, model):
|
||||
"""Get the the list of MachineHistoryEvent related to the object.
|
||||
|
||||
Args:
|
||||
machine_id: int, the id of the machine to lookup.
|
||||
instance_id: int, the id of the machine to lookup.
|
||||
|
||||
Returns:
|
||||
The list of MachineHistoryEvent, in reverse chronological order,
|
||||
|
@ -880,7 +886,7 @@ class MachineHistory(History):
|
|||
"""
|
||||
self.related = (
|
||||
Version.objects.get_for_model(Interface)
|
||||
.filter(make_version_filter("machine", machine_id))
|
||||
.filter(make_version_filter("machine", instance_id))
|
||||
.order_by("content_type__model")
|
||||
)
|
||||
|
||||
|
@ -888,7 +894,7 @@ class MachineHistory(History):
|
|||
self.related = [RelatedHistory(v) for v in self.related]
|
||||
self.related = list(dict.fromkeys(self.related))
|
||||
|
||||
return super(MachineHistory, self).get(machine_id, Machine)
|
||||
return super().get(instance_id, Machine)
|
||||
|
||||
|
||||
class InterfaceHistoryEvent(HistoryEvent):
|
||||
|
@ -907,17 +913,17 @@ class InterfaceHistoryEvent(HistoryEvent):
|
|||
try:
|
||||
return IpList.objects.get(id=value)
|
||||
except IpList.DoesNotExist:
|
||||
return "{} ({})".format(_("Deleted"), value)
|
||||
return f'{_("Deleted")} ({value})'
|
||||
elif name == "machine_type_id":
|
||||
try:
|
||||
return MachineType.objects.get(id=value).name
|
||||
except MachineType.DoesNotExist:
|
||||
return "{} ({})".format(_("Deleted"), value)
|
||||
return f'{_("Deleted")} ({value})'
|
||||
elif name == "machine_id":
|
||||
try:
|
||||
return Machine.objects.get(id=value).get_name() or _("No name")
|
||||
except Machine.DoesNotExist:
|
||||
return "{} ({})".format(_("Deleted"), value)
|
||||
return f'{_("Deleted")} ({value})'
|
||||
elif name == "port_lists":
|
||||
if len(value) == 0:
|
||||
return _("None")
|
||||
|
@ -927,27 +933,27 @@ class InterfaceHistoryEvent(HistoryEvent):
|
|||
try:
|
||||
ports.append(Port.objects.get(id=pid).pretty_name())
|
||||
except Group.DoesNotExist:
|
||||
ports.append("{} ({})".format(_("Deleted"), pid))
|
||||
ports.append(f'{_("Deleted")} ({pid})')
|
||||
|
||||
return super(InterfaceHistoryEvent, self)._repr(name, value)
|
||||
return super()._repr(name, value)
|
||||
|
||||
|
||||
class InterfaceHistory(History):
|
||||
def __init__(self):
|
||||
super(InterfaceHistory, self).__init__()
|
||||
super().__init__()
|
||||
self.event_type = InterfaceHistoryEvent
|
||||
|
||||
def get(self, interface_id, model):
|
||||
def get(self, instance_id, model):
|
||||
"""Get the the list of InterfaceHistoryEvent related to the object.
|
||||
|
||||
Args:
|
||||
interface_id: int, the id of the interface to lookup.
|
||||
instance_id: int, the id of the interface to lookup.
|
||||
|
||||
Returns:
|
||||
The list of InterfaceHistoryEvent, in reverse chronological order,
|
||||
related to the object.
|
||||
"""
|
||||
return super(InterfaceHistory, self).get(interface_id, Interface)
|
||||
return super().get(instance_id, Interface)
|
||||
|
||||
|
||||
############################
|
||||
|
|
|
@ -69,7 +69,6 @@ from preferences.models import GeneralOption
|
|||
from re2o.acl import (
|
||||
acl_error_message,
|
||||
can_edit_history,
|
||||
can_view,
|
||||
can_view_all,
|
||||
can_view_app,
|
||||
)
|
||||
|
@ -134,10 +133,10 @@ def index(request):
|
|||
# Items to remove later because invalid
|
||||
to_remove = []
|
||||
# Parse every item (max = pagination_number)
|
||||
for i in range(len(versions.object_list)):
|
||||
if versions.object_list[i].object:
|
||||
version = versions.object_list[i]
|
||||
versions.object_list[i] = {
|
||||
for i, item in enumerate(versions.object_list):
|
||||
if item.object:
|
||||
version = item
|
||||
item = {
|
||||
"rev_id": version.revision.id,
|
||||
"comment": version.revision.comment,
|
||||
"datetime": version.revision.date_created,
|
||||
|
@ -212,7 +211,7 @@ def stats_general(request):
|
|||
disabled, archived etc.) and IP addresses (ranges, number of assigned
|
||||
addresses etc.).
|
||||
"""
|
||||
ip_dict = dict()
|
||||
ip_dict = {}
|
||||
for ip_range in IpType.objects.select_related("vlan").all():
|
||||
all_ip = IpList.objects.filter(ip_type=ip_range)
|
||||
used_ip = Interface.objects.filter(ipv4__in=all_ip).count()
|
||||
|
@ -519,8 +518,8 @@ def stats_search_machine_history(request):
|
|||
"""
|
||||
history_form = MachineHistorySearchForm(request.GET or None)
|
||||
if history_form.is_valid():
|
||||
history = MachineHistorySearch()
|
||||
events = history.get(
|
||||
hist = MachineHistorySearch()
|
||||
events = hist.get(
|
||||
history_form.cleaned_data.get("q", ""), history_form.cleaned_data
|
||||
)
|
||||
max_result = GeneralOption.get_cached_value("pagination_number")
|
||||
|
@ -536,7 +535,7 @@ def stats_search_machine_history(request):
|
|||
)
|
||||
|
||||
|
||||
def get_history_object(request, model, object_name, object_id):
|
||||
def get_history_object(request, model, _object_name, object_id):
|
||||
"""Get the objet of type model with the given object_id
|
||||
Handles permissions and DoesNotExist errors
|
||||
"""
|
||||
|
@ -588,15 +587,15 @@ def history(request, application, object_name, object_id):
|
|||
"""
|
||||
try:
|
||||
model = apps.get_model(application, object_name)
|
||||
except LookupError:
|
||||
raise Http404(_("No model found."))
|
||||
except LookupError as e:
|
||||
raise Http404(_("No model found.")) from e
|
||||
|
||||
authorized, instance = get_history_object(request, model, object_name, object_id)
|
||||
if not authorized:
|
||||
return instance
|
||||
|
||||
history = get_history_class(model)
|
||||
events = history.get(int(object_id), model)
|
||||
hist = get_history_class(model)
|
||||
events = hist.get(int(object_id), model)
|
||||
|
||||
# Events is None if object wasn't found
|
||||
if events is None:
|
||||
|
@ -610,7 +609,7 @@ def history(request, application, object_name, object_id):
|
|||
events = re2o_paginator(request, events, max_result)
|
||||
|
||||
# Add a default title in case the object was deleted
|
||||
title = instance or "{} ({})".format(history.name, _("Deleted"))
|
||||
title = instance or f'{history.name} ({_("Deleted")})'
|
||||
|
||||
return render(
|
||||
request,
|
||||
|
|
|
@ -41,6 +41,6 @@ def can_view(user, *args, **kwargs):
|
|||
can = user.has_module_perms("preferences")
|
||||
return (
|
||||
can,
|
||||
None if can else _("You don't have the right to view this" " application."),
|
||||
None if can else _("You don't have the right to view this application."),
|
||||
("preferences",),
|
||||
)
|
||||
|
|
|
@ -49,86 +49,58 @@ from .models import (
|
|||
class OptionalUserAdmin(VersionAdmin):
|
||||
"""Admin class for user options."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class OptionalTopologieAdmin(VersionAdmin):
|
||||
"""Admin class for topology options."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class OptionalMachineAdmin(VersionAdmin):
|
||||
"""Admin class for machines options."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class GeneralOptionAdmin(VersionAdmin):
|
||||
"""Admin class for general options."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ServiceAdmin(VersionAdmin):
|
||||
"""Admin class for services (on the homepage)."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class MailContactAdmin(VersionAdmin):
|
||||
"""Admin class for contact email addresses."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class AssoOptionAdmin(VersionAdmin):
|
||||
"""Admin class for organisation options."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class MailMessageOptionAdmin(VersionAdmin):
|
||||
"""Admin class for email messages options."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class HomeOptionAdmin(VersionAdmin):
|
||||
"""Admin class for home options."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class RadiusKeyAdmin(VersionAdmin):
|
||||
"""Admin class for RADIUS keys options."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class SwitchManagementCredAdmin(VersionAdmin):
|
||||
"""Admin class for switch management credentials options."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ReminderAdmin(VersionAdmin):
|
||||
"""Admin class for reminder options."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class DocumentTemplateAdmin(VersionAdmin):
|
||||
"""Admin class for document templates."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class MandateAdmin(VersionAdmin):
|
||||
"""Admin class for mandates."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
admin.site.register(OptionalUser, OptionalUserAdmin)
|
||||
admin.site.register(OptionalMachine, OptionalMachineAdmin)
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
import preferences.models as preferences
|
||||
from api.serializers import NamespacedHIField, NamespacedHMSerializer, NamespacedHRField
|
||||
from api.serializers import NamespacedHMSerializer
|
||||
|
||||
|
||||
class OptionalUserSerializer(NamespacedHMSerializer):
|
||||
|
|
|
@ -33,6 +33,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
from re2o.mixins import FormRevMixin
|
||||
from re2o.widgets import AutocompleteModelWidget, AutocompleteMultipleModelWidget
|
||||
from topologie.models import Switch
|
||||
from cotisations.models import Banque
|
||||
|
||||
from .models import (
|
||||
AssoOption,
|
||||
|
@ -64,7 +65,7 @@ class EditOptionalUserForm(ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(EditOptionalUserForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields["is_tel_mandatory"].label = _("Telephone number required")
|
||||
self.fields["gpg_fingerprint"].label = _("GPG fingerprint")
|
||||
self.fields["all_can_create_club"].label = _("All can create a club")
|
||||
|
@ -101,7 +102,7 @@ class EditOptionalMachineForm(ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(EditOptionalMachineForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields["password_machine"].label = _(
|
||||
"Possibility to set a password per machine"
|
||||
)
|
||||
|
@ -135,7 +136,7 @@ class EditOptionalTopologieForm(ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(EditOptionalTopologieForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
self.initial["automatic_provision_switchs"] = Switch.objects.filter(
|
||||
automatic_provision=True
|
||||
|
@ -159,7 +160,7 @@ class EditGeneralOptionForm(ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(EditGeneralOptionForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields["general_message_fr"].label = _("General message in French")
|
||||
self.fields["general_message_en"].label = _("General message in English")
|
||||
self.fields["search_display_page"].label = _(
|
||||
|
@ -194,7 +195,7 @@ class EditAssoOptionForm(ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(EditAssoOptionForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields["name"].label = _("Organisation name")
|
||||
self.fields["siret"].label = _("SIRET number")
|
||||
self.fields["adresse1"].label = _("Address (line 1)")
|
||||
|
@ -217,7 +218,7 @@ class EditMailMessageOptionForm(ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(EditMailMessageOptionForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields["welcome_mail_fr"].label = _("Message for the French welcome email")
|
||||
self.fields["welcome_mail_en"].label = _(
|
||||
"Message for the English welcome email"
|
||||
|
@ -235,7 +236,7 @@ class EditHomeOptionForm(ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(EditHomeOptionForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields["facebook_url"].label = _("Facebook URL")
|
||||
self.fields["twitter_url"].label = _("Twitter URL")
|
||||
self.fields["twitter_account_name"].label = _("Twitter account name")
|
||||
|
@ -283,7 +284,7 @@ class MandateForm(ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(MandateForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
def clean_start_date(self):
|
||||
date = self.cleaned_data.get("start_date")
|
||||
|
@ -293,7 +294,8 @@ class MandateForm(ModelForm):
|
|||
if existing_mandates:
|
||||
raise forms.ValidationError(
|
||||
_(
|
||||
"There is already a mandate taking place at the specified start date."
|
||||
"There is already a mandate taking place at the specified "
|
||||
"start date."
|
||||
)
|
||||
)
|
||||
return date
|
||||
|
@ -312,7 +314,7 @@ class MandateForm(ModelForm):
|
|||
return date
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(MandateForm, self).clean()
|
||||
cleaned_data = super().clean()
|
||||
start_date, end_date = cleaned_data["start_date"], cleaned_data["end_date"]
|
||||
if end_date:
|
||||
included_mandates = Mandate.objects.filter(
|
||||
|
@ -330,7 +332,7 @@ class MandateForm(ModelForm):
|
|||
"""Warning, side effect : if a mandate with a null end_date
|
||||
exists, its end_date will be set to instance.start_date, no matter the
|
||||
value of commit."""
|
||||
instance = super(MandateForm, self).save(commit=False)
|
||||
instance = super().save(commit=False)
|
||||
if instance.end_date is None:
|
||||
try:
|
||||
previous_mandate = Mandate.objects.get(end_date__isnull=True)
|
||||
|
@ -352,7 +354,7 @@ class ServiceForm(ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(ServiceForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields["name"].label = _("Name")
|
||||
self.fields["url"].label = _("URL")
|
||||
self.fields["description"].label = _("Description")
|
||||
|
@ -370,7 +372,7 @@ class DelServiceForm(Form):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop("instances", None)
|
||||
super(DelServiceForm, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields["services"].queryset = instances
|
||||
else:
|
||||
|
@ -386,7 +388,7 @@ class ReminderForm(FormRevMixin, ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(ReminderForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class RadiusKeyForm(FormRevMixin, ModelForm):
|
||||
|
@ -404,7 +406,7 @@ class RadiusKeyForm(FormRevMixin, ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(RadiusKeyForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
instance = kwargs.get("instance", None)
|
||||
if instance:
|
||||
self.initial["members"] = Switch.objects.filter(radius_key=instance)
|
||||
|
@ -430,7 +432,7 @@ class SwitchManagementCredForm(FormRevMixin, ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(SwitchManagementCredForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
instance = kwargs.get("instance", None)
|
||||
if instance:
|
||||
self.initial["members"] = Switch.objects.filter(management_creds=instance)
|
||||
|
@ -450,7 +452,7 @@ class MailContactForm(ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(MailContactForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class DelMailContactForm(Form):
|
||||
|
@ -464,7 +466,7 @@ class DelMailContactForm(Form):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop("instances", None)
|
||||
super(DelMailContactForm, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields["mailcontacts"].queryset = instances
|
||||
else:
|
||||
|
@ -480,7 +482,7 @@ class DocumentTemplateForm(FormRevMixin, ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(DocumentTemplateForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class DelDocumentTemplateForm(FormRevMixin, Form):
|
||||
|
@ -494,7 +496,7 @@ class DelDocumentTemplateForm(FormRevMixin, Form):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop("instances", None)
|
||||
super(DelDocumentTemplateForm, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields["document_templates"].queryset = instances
|
||||
else:
|
||||
|
@ -510,7 +512,7 @@ class RadiusAttributeForm(ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(RadiusAttributeForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class DelRadiusAttributeForm(Form):
|
||||
|
@ -524,8 +526,8 @@ class DelRadiusAttributeForm(Form):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances = kwargs.pop("instances", None)
|
||||
super(DelServiceForm, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
if instances:
|
||||
self.fields["attributes"].queryset = instances
|
||||
else:
|
||||
self.fields["attributes"].queryset = Attributes.objects.all()
|
||||
self.fields["attributes"].queryset = Attribute.objects.all()
|
||||
|
|
|
@ -109,7 +109,8 @@ class OptionalUser(AclMixin, PreferencesModel):
|
|||
(
|
||||
ONLY_INACTIVE,
|
||||
_(
|
||||
"Users can only select a room occupied by a user with a disabled connection."
|
||||
"Users can only select a room occupied by a user with "
|
||||
"a disabled connection."
|
||||
),
|
||||
),
|
||||
(ALL_ROOM, _("Users can select all rooms")),
|
||||
|
@ -157,7 +158,8 @@ class OptionalUser(AclMixin, PreferencesModel):
|
|||
disable_emailnotyetconfirmed = models.IntegerField(
|
||||
default=2,
|
||||
help_text=_(
|
||||
"Users with an email address not yet confirmed will be disabled after this number of days."
|
||||
"Users with an email address not yet confirmed will be disabled after "
|
||||
"this number of days."
|
||||
),
|
||||
)
|
||||
self_adhesion = models.BooleanField(
|
||||
|
@ -338,8 +340,7 @@ class OptionalTopologie(AclMixin, PreferencesModel):
|
|||
.filter(machine_type__ip_type=self.switchs_ip_type)
|
||||
.first()
|
||||
)
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
|
||||
@cached_property
|
||||
def switchs_management_interface_ip(self):
|
||||
|
@ -355,15 +356,14 @@ class OptionalTopologie(AclMixin, PreferencesModel):
|
|||
"""Get the switch credentials for SFTP provisioning."""
|
||||
if self.sftp_login and self.sftp_pass:
|
||||
return {"login": self.sftp_login, "pass": self.sftp_pass}
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
|
||||
@cached_property
|
||||
def switchs_management_utils(self):
|
||||
"""Get the dictionary of IP addresses for the configuration of
|
||||
switches.
|
||||
"""
|
||||
from machines.models import Interface, Ipv6List, Role
|
||||
from machines.models import Ipv6List, Role
|
||||
|
||||
def return_ips_dict(interfaces):
|
||||
return {
|
||||
|
@ -622,7 +622,7 @@ class MailContact(AclMixin, models.Model):
|
|||
|
||||
@cached_property
|
||||
def get_name(self):
|
||||
return self.address.split("@")[0]
|
||||
return self.address.split("@", maxsplit=1)[0]
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("contact email address")
|
||||
|
@ -676,7 +676,8 @@ class Mandate(RevMixin, AclMixin, models.Model):
|
|||
if not mandate:
|
||||
raise cls.DoesNotExist(
|
||||
_(
|
||||
"No mandates have been created. Please go to the preferences page to create one."
|
||||
"No mandates have been created. Please go to the preferences page "
|
||||
"to create one."
|
||||
)
|
||||
)
|
||||
return mandate
|
||||
|
@ -977,7 +978,9 @@ class RadiusOption(AclMixin, PreferencesModel):
|
|||
)
|
||||
|
||||
@classmethod
|
||||
def get_attributes(cls, name, attribute_kwargs={}):
|
||||
def get_attributes(cls, name, attribute_kwargs=None):
|
||||
if attribute_kwargs is None:
|
||||
attribute_kwargs = {}
|
||||
return (
|
||||
(str(attribute.attribute), str(attribute.value % attribute_kwargs))
|
||||
for attribute in cls.get_cached_value(name).all()
|
||||
|
@ -1082,3 +1085,4 @@ def auto_delete_file_on_change(sender, instance, **kwargs):
|
|||
if not old_file == new_file:
|
||||
if os.path.isfile(old_file.path):
|
||||
os.remove(old_file.path)
|
||||
return True
|
||||
|
|
|
@ -56,7 +56,6 @@ from . import forms, models
|
|||
from .forms import (
|
||||
DelDocumentTemplateForm,
|
||||
DelMailContactForm,
|
||||
DelRadiusAttributeForm,
|
||||
DocumentTemplateForm,
|
||||
MailContactForm,
|
||||
MandateForm,
|
||||
|
@ -108,8 +107,7 @@ def edit_options_template_function(request, section, forms, models):
|
|||
options.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment(
|
||||
"Field(s) edited: %s"
|
||||
% ", ".join(field for field in options.changed_data)
|
||||
f'Field(s) edited: {", ".join(field for field in options.changed_data)}'
|
||||
)
|
||||
messages.success(request, _("The preferences were edited."))
|
||||
return redirect(reverse("preferences:display-options"))
|
||||
|
|
29
re2o/acl.py
29
re2o/acl.py
|
@ -32,7 +32,6 @@ import sys
|
|||
from itertools import chain
|
||||
|
||||
from django.contrib import messages
|
||||
from django.db.models import Model
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
|
@ -51,12 +50,11 @@ def acl_error_message(msg, permissions):
|
|||
return (
|
||||
message + _("You need to be a member of one of these groups: %s.") % groups
|
||||
)
|
||||
else:
|
||||
return message + _("No group has the %s permission(s)!") % " or ".join(
|
||||
[",".join(permissions[:-1]), permissions[-1]]
|
||||
if len(permissions) > 2
|
||||
else permissions
|
||||
)
|
||||
return message + _("No group has the %s permission(s)!") % " or ".join(
|
||||
[",".join(permissions[:-1]), permissions[-1]]
|
||||
if len(permissions) > 2
|
||||
else permissions
|
||||
)
|
||||
|
||||
|
||||
# This is the function of main interest of this file. Almost all the decorators
|
||||
|
@ -269,14 +267,13 @@ ModelC)
|
|||
"users:profil", kwargs={"userid": str(request.user.id)}
|
||||
)
|
||||
)
|
||||
else:
|
||||
return Response(
|
||||
data={
|
||||
"errors": error_messages,
|
||||
"warning": warning_messages,
|
||||
},
|
||||
status=403,
|
||||
)
|
||||
return Response(
|
||||
data={
|
||||
"errors": error_messages,
|
||||
"warning": warning_messages,
|
||||
},
|
||||
status=403,
|
||||
)
|
||||
else:
|
||||
return redirect(reverse("index"))
|
||||
return view(request, *chain(instances, args), **kwargs)
|
||||
|
@ -382,7 +379,7 @@ def can_list(*targets):
|
|||
def can_view_app(*apps_name):
|
||||
"""Decorator to check if an user can view the applications."""
|
||||
for app_name in apps_name:
|
||||
assert app_name in sys.modules.keys()
|
||||
assert app_name in sys.modules
|
||||
return acl_base_decorator(
|
||||
"can_view",
|
||||
*chain(sys.modules[app_name] for app_name in apps_name),
|
||||
|
|
|
@ -37,8 +37,8 @@ from django import forms
|
|||
from django.conf import settings
|
||||
from django.db import models
|
||||
|
||||
EOD_asbyte = b"`%EofD%`" # This should be something that will not occur in strings
|
||||
EOD = EOD_asbyte.decode("utf-8")
|
||||
EOD_ASBYTE = b"`%EofD%`" # This should be something that will not occur in strings
|
||||
EOD = EOD_ASBYTE.decode("utf-8")
|
||||
|
||||
|
||||
def genstring(length=16, chars=string.printable):
|
||||
|
@ -63,7 +63,7 @@ def decrypt(key, secret):
|
|||
"""AES Decrypt a secret with the key `key`"""
|
||||
obj = AES.new(key, AES.MODE_ECB)
|
||||
uncrypted_secret = obj.decrypt(secret)
|
||||
return uncrypted_secret.split(EOD_asbyte)[0]
|
||||
return uncrypted_secret.split(EOD_ASBYTE)[0]
|
||||
|
||||
|
||||
class AESEncryptedFormField(forms.CharField):
|
||||
|
@ -88,9 +88,9 @@ class AESEncryptedField(models.CharField):
|
|||
return decrypt(settings.AES_KEY, binascii.a2b_base64(value)).decode("utf-8")
|
||||
except UnicodeDecodeError as e:
|
||||
raise ValueError(
|
||||
"Could not decode your field %s, your settings.AES_KEY "
|
||||
"is probably wrong." % self.name
|
||||
)
|
||||
f"Could not decode your field {self.name}, your settings.AES_KEY "
|
||||
"is probably wrong."
|
||||
) from e
|
||||
|
||||
def from_db_value(self, value, *args, **kwargs):
|
||||
if value is None:
|
||||
|
@ -99,9 +99,9 @@ class AESEncryptedField(models.CharField):
|
|||
return decrypt(settings.AES_KEY, binascii.a2b_base64(value)).decode("utf-8")
|
||||
except UnicodeDecodeError as e:
|
||||
raise ValueError(
|
||||
"Could not decode your field %s, your settings.AES_KEY "
|
||||
"is probably wrong." % self.name
|
||||
)
|
||||
f"Could not decode your field {self.name}, your settings.AES_KEY "
|
||||
"is probably wrong."
|
||||
) from e
|
||||
|
||||
def get_prep_value(self, value):
|
||||
if value is None:
|
||||
|
|
13
re2o/base.py
13
re2o/base.py
|
@ -76,16 +76,16 @@ def smtp_check(local_part):
|
|||
return False, None
|
||||
|
||||
|
||||
def convert_datetime_format(format):
|
||||
def convert_datetime_format(date_format):
|
||||
i = 0
|
||||
new_format = ""
|
||||
while i < len(format):
|
||||
if format[i] == "%":
|
||||
char = format[i : i + 2]
|
||||
while i < len(date_format):
|
||||
if date_format[i] == "%":
|
||||
char = date_format[i : i + 2]
|
||||
new_format += datetime_mapping.get(char, char)
|
||||
i += 2
|
||||
else:
|
||||
new_format += format[i]
|
||||
new_format += date_format[i]
|
||||
i += 1
|
||||
return new_format
|
||||
|
||||
|
@ -235,8 +235,7 @@ class SortTable:
|
|||
request = request.order_by(*fields)
|
||||
if values.get(col, None) and order == "desc":
|
||||
return request.reverse()
|
||||
else:
|
||||
return request
|
||||
return request
|
||||
|
||||
|
||||
def re2o_paginator(request, query_set, pagination_number, page_arg="page"):
|
||||
|
|
|
@ -64,7 +64,7 @@ class FieldPermissionModelMixin:
|
|||
checks.append(perm_label)
|
||||
|
||||
# No requirements means no restrictions.
|
||||
if not len(checks):
|
||||
if not checks:
|
||||
return self.FIELD_PERMISSION_MISSING_DEFAULT
|
||||
|
||||
# Try to find a user setting that qualifies them for permission.
|
||||
|
@ -97,7 +97,7 @@ class FieldPermissionFormMixin:
|
|||
def __init__(self, *args, **kwargs):
|
||||
user = kwargs.pop("user")
|
||||
|
||||
super(FieldPermissionFormMixin, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
to_be_deleted = []
|
||||
for name in self.fields:
|
||||
if not self.instance.has_field_perm(user, field=name):
|
||||
|
|
|
@ -44,7 +44,7 @@ ALGO_LEN = len(ALGO_NAME + "$")
|
|||
DIGEST_LEN = 20
|
||||
|
||||
|
||||
def makeSecret(password):
|
||||
def make_secret(password):
|
||||
"""Build a hashed and salted version of the password with SSHA
|
||||
|
||||
Parameters:
|
||||
|
@ -59,7 +59,7 @@ def makeSecret(password):
|
|||
return ALGO_NAME + "$" + encodebytes(h.digest() + salt).decode()[:-1]
|
||||
|
||||
|
||||
def hashNT(password):
|
||||
def hash_nt(password):
|
||||
"""Build a md4 hash of the password to use as the NT-password
|
||||
|
||||
Parameters:
|
||||
|
@ -73,7 +73,7 @@ def hashNT(password):
|
|||
return binascii.hexlify(hash_str).upper().decode("utf-8")
|
||||
|
||||
|
||||
def checkPassword(challenge_password, password):
|
||||
def check_password(challenge_password, password):
|
||||
"""Check if a given password match the hash of a stored password
|
||||
|
||||
Parameters:
|
||||
|
@ -106,28 +106,26 @@ def hash_password_salt(hashed_password):
|
|||
hashed_password = hashed_password[7:]
|
||||
if hashed_password.startswith("$"):
|
||||
return "$".join(hashed_password.split("$")[:-1])
|
||||
else:
|
||||
return hashed_password[:2]
|
||||
elif hashed_password.upper().startswith("{SSHA}"):
|
||||
return hashed_password[:2]
|
||||
if hashed_password.upper().startswith("{SSHA}"):
|
||||
try:
|
||||
digest = b64decode(hashed_password[6:])
|
||||
except TypeError as error:
|
||||
raise ValueError("b64 error for `hashed_password`: %s." % error)
|
||||
raise ValueError(f"b64 error for `hashed_password`: {error}.") from error
|
||||
if len(digest) < 20:
|
||||
raise ValueError("`hashed_password` too short.")
|
||||
return digest[20:]
|
||||
elif hashed_password.upper().startswith("{SMD5}"):
|
||||
if hashed_password.upper().startswith("{SMD5}"):
|
||||
try:
|
||||
digest = b64decode(hashed_password[7:])
|
||||
except TypeError as error:
|
||||
raise ValueError("b64 error for `hashed_password`: %s." % error)
|
||||
raise ValueError(f"b64 error for `hashed_password`: {error}.") from error
|
||||
if len(digest) < 16:
|
||||
raise ValueError("`hashed_password` too short.")
|
||||
return digest[16:]
|
||||
else:
|
||||
raise ValueError(
|
||||
"`hashed_password` should start with '{SSHA}' or '{CRYPT}' or '{SMD5}'."
|
||||
)
|
||||
raise ValueError(
|
||||
"`hashed_password` should start with '{SSHA}' or '{CRYPT}' or '{SMD5}'."
|
||||
)
|
||||
|
||||
|
||||
class CryptPasswordHasher(hashers.BasePasswordHasher):
|
||||
|
@ -174,7 +172,6 @@ class CryptPasswordHasher(hashers.BasePasswordHasher):
|
|||
|
||||
As we are not using multiple iterations the method is pretty useless
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class MD5PasswordHasher(hashers.BasePasswordHasher):
|
||||
|
@ -223,7 +220,6 @@ class MD5PasswordHasher(hashers.BasePasswordHasher):
|
|||
|
||||
As we are not using multiple iterations the method is pretty useless
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class SSHAPasswordHasher(hashers.BasePasswordHasher):
|
||||
|
@ -240,14 +236,14 @@ class SSHAPasswordHasher(hashers.BasePasswordHasher):
|
|||
salt is overridden
|
||||
"""
|
||||
assert password is not None
|
||||
return makeSecret(password)
|
||||
return make_secret(password)
|
||||
|
||||
def verify(self, password, encoded):
|
||||
"""
|
||||
Check password against encoded using SSHA algorithm
|
||||
"""
|
||||
assert encoded.startswith(self.algorithm)
|
||||
return checkPassword(encoded, password)
|
||||
return check_password(encoded, password)
|
||||
|
||||
def safe_summary(self, encoded):
|
||||
"""
|
||||
|
@ -271,12 +267,11 @@ class SSHAPasswordHasher(hashers.BasePasswordHasher):
|
|||
|
||||
As we are not using multiple iterations the method is pretty useless
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class RecryptBackend(ModelBackend):
|
||||
"""Function for legacy users. During auth, if their hash password is different from SSHA or ntlm
|
||||
password is empty, rehash in SSHA or NTLM
|
||||
"""Function for legacy users. During auth, if their hash password is different from
|
||||
SSHA or ntlm password is empty, rehash in SSHA or NTLM
|
||||
|
||||
Returns:
|
||||
model user instance: Instance of the user logged
|
||||
|
@ -285,16 +280,14 @@ class RecryptBackend(ModelBackend):
|
|||
|
||||
def authenticate(self, request, username=None, password=None, **kwargs):
|
||||
# we obtain from the classical auth backend the user
|
||||
user = super(RecryptBackend, self).authenticate(
|
||||
request, username, password, **kwargs
|
||||
)
|
||||
user = super().authenticate(request, username, password, **kwargs)
|
||||
if user:
|
||||
if not (user.pwd_ntlm):
|
||||
if not user.pwd_ntlm:
|
||||
# if we dont have NT hash, we create it
|
||||
user.pwd_ntlm = hashNT(password)
|
||||
user.pwd_ntlm = hash_nt(password)
|
||||
user.save()
|
||||
if not ("SSHA" in user.password):
|
||||
if not "SSHA" in user.password:
|
||||
# if the hash is too old, we update it
|
||||
user.password = makeSecret(password)
|
||||
user.password = make_secret(password)
|
||||
user.save()
|
||||
return user
|
||||
|
|
|
@ -54,7 +54,7 @@ class Command(BaseCommand):
|
|||
|
||||
# Put it back together
|
||||
name_text = " ".join(names)
|
||||
buffer += " '{}',\n".format(name_text)
|
||||
buffer += f" '{name_text}',\n"
|
||||
buffer += "]"
|
||||
|
||||
return buffer
|
||||
|
|
|
@ -29,7 +29,7 @@ from django.utils.translation import ugettext as _
|
|||
from reversion import revisions as reversion
|
||||
|
||||
|
||||
class RevMixin(object):
|
||||
class RevMixin:
|
||||
"""A mixin to subclass the save and delete function of a model
|
||||
to enforce the versioning of the object before those actions
|
||||
really happen"""
|
||||
|
@ -39,17 +39,17 @@ class RevMixin(object):
|
|||
if self.pk is None:
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
reversion.set_comment("Creation")
|
||||
return super(RevMixin, self).save(*args, **kwargs)
|
||||
return super(RevMixin, self).save(*args, **kwargs)
|
||||
return super().save(*args, **kwargs)
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
"""Creates a version of this object and delete it from database"""
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
reversion.set_comment("Deletion")
|
||||
return super(RevMixin, self).delete(*args, **kwargs)
|
||||
return super().delete(*args, **kwargs)
|
||||
|
||||
|
||||
class FormRevMixin(object):
|
||||
class FormRevMixin:
|
||||
"""A mixin to subclass the save function of a form
|
||||
to enforce the versionning of the object before it is really edited"""
|
||||
|
||||
|
@ -57,17 +57,16 @@ class FormRevMixin(object):
|
|||
"""Create a version of this object and save it to database"""
|
||||
if reversion.get_comment() != "" and self.changed_data != []:
|
||||
reversion.set_comment(
|
||||
reversion.get_comment()
|
||||
+ ",%s" % ", ".join(field for field in self.changed_data)
|
||||
f'{reversion.get_comment()},{", ".join(field for field in self.changed_data)}'
|
||||
)
|
||||
elif self.changed_data:
|
||||
reversion.set_comment(
|
||||
"Field(s) edited: %s" % ", ".join(field for field in self.changed_data)
|
||||
f'Field(s) edited: {", ".join(field for field in self.changed_data)}'
|
||||
)
|
||||
return super(FormRevMixin, self).save(*args, **kwargs)
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class AclMixin(object):
|
||||
class AclMixin:
|
||||
"""This mixin is used in nearly every class/models defined in re2o apps.
|
||||
It is used by acl, in models (decorators can_...) and in templates tags
|
||||
:get_instance: Applied on a class, take an id argument, return an instance
|
||||
|
@ -92,7 +91,7 @@ class AclMixin(object):
|
|||
@classmethod
|
||||
def get_modulename(cls):
|
||||
"""Returns the name of the module where this mixin is used"""
|
||||
return str(cls.__module__).split(".")[0].lower()
|
||||
return str(cls.__module__).split(".", maxsplit=1)[0].lower()
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls, object_id, *_args, **kwargs):
|
||||
|
|
|
@ -68,32 +68,32 @@ def get_system_user():
|
|||
return pwd.getpwuid(int(os.getenv("SUDO_UID") or os.getuid())).pw_name
|
||||
|
||||
|
||||
def form_cli(Form, user, action, *args, **kwargs):
|
||||
def form_cli(form, user, action, *args, **kwargs):
|
||||
"""
|
||||
Fill-in a django form from cli
|
||||
|
||||
Parameters
|
||||
Form : a django class form to fill-in
|
||||
form : a django class form to fill-in
|
||||
user : a re2o user doign the modification
|
||||
action: the action done with that form, for logs purpose
|
||||
|
||||
"""
|
||||
data = {}
|
||||
dumb_form = Form(user=user, *args, **kwargs)
|
||||
dumb_form = form(user=user, *args, **kwargs)
|
||||
for key in dumb_form.fields:
|
||||
if not dumb_form.fields[key].widget.input_type == "hidden":
|
||||
if dumb_form.fields[key].widget.input_type == "password":
|
||||
data[key] = getpass("%s : " % dumb_form.fields[key].label)
|
||||
data[key] = getpass(f"{dumb_form.fields[key].label} : ")
|
||||
else:
|
||||
data[key] = input("%s : " % dumb_form.fields[key].label)
|
||||
data[key] = input(f"{dumb_form.fields[key].label} : ")
|
||||
|
||||
form = Form(data, user=user, *args, **kwargs)
|
||||
form = form(data, user=user, *args, **kwargs)
|
||||
if not form.is_valid():
|
||||
sys.stderr.write("Errors: \n")
|
||||
for err in form.errors:
|
||||
# Oui, oui, on gère du HTML là où d'autres ont eu la
|
||||
# lumineuse idée de le mettre
|
||||
sys.stderr.write("\t%s : %s\n" % (err, strip_tags(form.errors[err])))
|
||||
sys.stderr.write(f"\t{err} : {strip_tags(form.errors[err])}\n")
|
||||
raise CommandError("Invalid form.")
|
||||
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
|
@ -101,4 +101,4 @@ def form_cli(Form, user, action, *args, **kwargs):
|
|||
reversion.set_user(user)
|
||||
reversion.set_comment(action)
|
||||
|
||||
sys.stdout.write("%s: done. The edit may take several minutes to apply.\n" % action)
|
||||
sys.stdout.write("{action}: done. The edit may take several minutes to apply.\n")
|
||||
|
|
|
@ -36,7 +36,6 @@ https://docs.djangoproject.com/en/1.8/ref/settings/
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
import importlib
|
||||
from decouple import config
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
|
|
@ -59,7 +59,8 @@ DEFAULT_GID = 500
|
|||
|
||||
# If you want to add a database routers, please fill in above and add your databse.
|
||||
# Then, add a file "local_routers.py" in folder app re2o, and add your router path in
|
||||
# the LOCAL_ROUTERS var as "re2o.local_routers.DbRouter". You can also add extra routers.
|
||||
# the LOCAL_ROUTERS var as "re2o.local_routers.DbRouter".
|
||||
# You can also add extra routers.
|
||||
LOCAL_ROUTERS = []
|
||||
|
||||
# Some optionnal Re2o Apps
|
||||
|
|
|
@ -108,7 +108,8 @@ DEFAULT_GID = 500
|
|||
|
||||
# If you want to add a database routers, please fill in above and add your databse.
|
||||
# Then, add a file "local_routers.py" in folder app re2o, and add your router path in
|
||||
# the LOCAL_ROUTERS var as "re2o.local_routers.DbRouter". You can also add extra routers.
|
||||
# the LOCAL_ROUTERS var as "re2o.local_routers.DbRouter".
|
||||
# You can also add extra routers.
|
||||
# To use ldap you need to add "ldapdb.router.Router"
|
||||
LOCAL_ROUTERS = []
|
||||
|
||||
|
@ -118,12 +119,16 @@ OPTIONNAL_APPS_RE2O = ()
|
|||
# Some Django apps you want to add in you local project
|
||||
OPTIONNAL_APPS = OPTIONNAL_APPS_RE2O + ()
|
||||
|
||||
# Some optinnal link for the navbar in a tuple (link,icon class,text,position (left or right))
|
||||
# Some optinnal link for the navbar in a tuple
|
||||
# (link, icon class, text, position (left or right))
|
||||
NAVBAR_LINKS = ()
|
||||
|
||||
# Add statiffiles dir that were installed using system packaging
|
||||
# Example to reproduce re2o2.9 behavior
|
||||
# SYSTEM_STATICFILES_DIRS = ("/usr/share/fonts-font-awesome/", "/usr/share/javascript/")
|
||||
# SYSTEM_STATICFILES_DIRS = (
|
||||
# "/usr/share/fonts-font-awesome/",
|
||||
# "/usr/share/javascript/"
|
||||
# )
|
||||
SYSTEM_STATICFILES_DIRS = ()
|
||||
|
||||
# Wether to use CDN to retrieve bootstrap, font-aweseome and jquery files
|
||||
|
|
|
@ -81,7 +81,10 @@ EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
|||
|
||||
# Add statiffiles dir that were installed using system packaging
|
||||
# Example to reproduce re2o2.9 behavior
|
||||
# SYSTEM_STATICFILES_DIRS = ("/usr/share/fonts-font-awesome/", "/usr/share/javascript/")
|
||||
# SYSTEM_STATICFILES_DIRS = (
|
||||
# "/usr/share/fonts-font-awesome/",
|
||||
# "/usr/share/javascript/"
|
||||
# )
|
||||
SYSTEM_STATICFILES_DIRS = ()
|
||||
|
||||
# Wether to use CDN to retrieve bootstrap, font-aweseome and jquery files
|
||||
|
@ -107,7 +110,8 @@ OPTIONNAL_APPS_RE2O = (
|
|||
# Some Django apps you want to add in you local project
|
||||
OPTIONNAL_APPS = OPTIONNAL_APPS_RE2O + ()
|
||||
|
||||
# Some optinnal link for the navbar in a tuple (link,icon class,text,position (left or right))
|
||||
# Some optinnal link for the navbar in a tuple
|
||||
# (link, icon class, text, position (left or right))
|
||||
NAVBAR_LINKS = ()
|
||||
|
||||
# LDAP = {
|
||||
|
|
|
@ -85,10 +85,10 @@ def get_model(model_name):
|
|||
if len(splitted) > 1:
|
||||
try:
|
||||
app_label, name = splitted
|
||||
except ValueError:
|
||||
except ValueError as e:
|
||||
raise template.TemplateSyntaxError(
|
||||
"%r is an inconsistent model name." % model_name
|
||||
)
|
||||
f"{model_name} is an inconsistent model name."
|
||||
) from e
|
||||
else:
|
||||
app_label, name = None, splitted[0]
|
||||
try:
|
||||
|
@ -98,14 +98,14 @@ def get_model(model_name):
|
|||
)
|
||||
else:
|
||||
content_type = ContentType.objects.get(model=name.lower())
|
||||
except ContentType.DoesNotExist:
|
||||
except ContentType.DoesNotExist as e:
|
||||
raise template.TemplateSyntaxError(
|
||||
"%r is not a valid model for an acl tag." % model_name
|
||||
)
|
||||
except ContentType.MultipleObjectsReturned:
|
||||
f"{model_name} is not a valid model for an acl tag."
|
||||
) from e
|
||||
except ContentType.MultipleObjectsReturned as e:
|
||||
raise template.TemplateSyntaxError(
|
||||
"More than one model found for %r. Try with `app.model`." % model_name
|
||||
)
|
||||
f"More than one model found for {model_name}. Try with `app.model`."
|
||||
) from e
|
||||
return content_type.model_class()
|
||||
|
||||
|
||||
|
@ -179,7 +179,7 @@ def get_callback(tag_name, obj=None):
|
|||
True,
|
||||
)
|
||||
|
||||
raise template.TemplateSyntaxError("%r tag is not a valid can_xxx tag." % tag_name)
|
||||
raise template.TemplateSyntaxError(f"{tag_name} tag is not a valid can_xxx tag.")
|
||||
|
||||
|
||||
def acl_fct(callback, reverse):
|
||||
|
@ -227,14 +227,14 @@ def acl_app_filter(parser, token):
|
|||
contents = token.split_contents()
|
||||
tag_name = contents[0]
|
||||
app_name = contents[1:]
|
||||
except ValueError:
|
||||
except ValueError as e:
|
||||
raise template.TemplateSyntaxError(
|
||||
"%r tag require 1 argument: an application" % token.contents.split()[0]
|
||||
)
|
||||
f"{token.contents.split()[0]} tag require 1 argument: an application"
|
||||
) from e
|
||||
for name in app_name:
|
||||
if name not in sys.modules.keys():
|
||||
if name not in sys.modules:
|
||||
raise template.TemplateSyntaxError(
|
||||
"%r is not a registered application for acl." % name
|
||||
f"{name} is not a registered application for acl."
|
||||
)
|
||||
|
||||
callback = get_callback(tag_name, app_name)
|
||||
|
@ -263,11 +263,11 @@ def acl_change_filter(parser, token):
|
|||
model_name = tag_content[1]
|
||||
field_name = tag_content[2]
|
||||
args = tag_content[3:]
|
||||
except ValueError:
|
||||
except ValueError as e:
|
||||
raise template.TemplateSyntaxError(
|
||||
"%r tag require at least 2 argument: the model and the field."
|
||||
% token.contents.split()[0]
|
||||
)
|
||||
f"{token.contents.split()[0]} tag require at least 2 argument: "
|
||||
"the model and the field."
|
||||
) from e
|
||||
|
||||
model = get_model(model_name)
|
||||
callback = getattr(model, "can_change_" + field_name)
|
||||
|
@ -306,10 +306,10 @@ def acl_model_filter(parser, token):
|
|||
tag_name = tag_content[0]
|
||||
model_name = tag_content[1]
|
||||
args = tag_content[2:]
|
||||
except ValueError:
|
||||
except ValueError as e:
|
||||
raise template.TemplateSyntaxError(
|
||||
"%r tag require at least 1 argument: the model." % token.contents.split()[0]
|
||||
)
|
||||
f"{token.contents.split()[0]} tag require at least 1 argument: the model."
|
||||
) from e
|
||||
|
||||
model = get_model(model_name)
|
||||
callback = get_callback(tag_name, model)
|
||||
|
@ -345,11 +345,11 @@ def acl_instance_filter(parser, token):
|
|||
tag_name = tag_content[0]
|
||||
instance_name = tag_content[1]
|
||||
args = tag_content[2:]
|
||||
except ValueError:
|
||||
except ValueError as e:
|
||||
raise template.TemplateSyntaxError(
|
||||
"%r tag require at least 1 argument: the instance."
|
||||
% token.contents.split()[0]
|
||||
)
|
||||
f"{token.contents.split()[0]} tag require at least 1 argument: "
|
||||
"the instance."
|
||||
) from e
|
||||
|
||||
# {% can_create %}
|
||||
oknodes = parser.parse(("acl_else", "acl_end"))
|
||||
|
@ -390,7 +390,7 @@ class AclNode(Node):
|
|||
return self.konodes.render(context)
|
||||
|
||||
def __repr__(self):
|
||||
return "<AclNode %s>" % self.tag_name
|
||||
return f"<AclNode {self.tag_name}>"
|
||||
|
||||
|
||||
class AclInstanceNode(Node):
|
||||
|
@ -415,4 +415,4 @@ class AclInstanceNode(Node):
|
|||
return self.konodes.render(context)
|
||||
|
||||
def __repr__(self):
|
||||
return "<AclInstanceNode %s>" % self.tag_name
|
||||
return f"<AclInstanceNode {self.tag_name}>"
|
||||
|
|
|
@ -25,7 +25,7 @@ register = template.Library()
|
|||
|
||||
@register.simple_tag
|
||||
def nav_link(position):
|
||||
template = """
|
||||
link_template = """
|
||||
<li>
|
||||
<a href="{}">
|
||||
<i class="fa {}"></i> {}
|
||||
|
@ -35,5 +35,5 @@ def nav_link(position):
|
|||
res = ""
|
||||
for link in settings.NAVBAR_LINKS:
|
||||
if position == link[3]:
|
||||
res += template.format(link[0], link[1], link[2])
|
||||
res += link_template.format(link[0], link[1], link[2])
|
||||
return res
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
from django import template
|
||||
from django.utils.html import conditional_escape
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
register = template.Library()
|
||||
|
@ -30,11 +29,11 @@ register = template.Library()
|
|||
def tick(valeur, autoescape=False):
|
||||
|
||||
if isinstance(valeur, bool):
|
||||
if valeur == True:
|
||||
if valeur is True:
|
||||
result = '<i class="fa fa-check text-success"></i>'
|
||||
else:
|
||||
result = '<i class="fa fa-times text-danger"></i>'
|
||||
return mark_safe(result)
|
||||
|
||||
else: # if the value is not a boolean, display it as if tick was not called
|
||||
return valeur
|
||||
# if the value is not a boolean, display it as if tick was not called
|
||||
return valeur
|
||||
|
|
|
@ -18,5 +18,4 @@ def font_awesome_url():
|
|||
"""
|
||||
if settings.USE_CDN:
|
||||
return "https://pro.fontawesome.com/releases/v5.10.0/css/all.css"
|
||||
else:
|
||||
return static("rest_framework/css/font-awesome-4.0.3.css")
|
||||
return static("rest_framework/css/font-awesome-4.0.3.css")
|
||||
|
|
|
@ -89,7 +89,7 @@ def pagination_insert_page_and_id(url, page=1, id=None, **kwargs):
|
|||
args = {"url": url, page_arg: page}
|
||||
new_url = url_insert_param(**args)
|
||||
|
||||
if id != None:
|
||||
if id is not None:
|
||||
new_url += "#" + str(id)
|
||||
|
||||
return new_url
|
||||
|
|
|
@ -60,7 +60,7 @@ urlpatterns = [
|
|||
|
||||
|
||||
urlpatterns += [
|
||||
path("{}/".format(app), include("{}.urls".format(app), namespace=app))
|
||||
path(f"{app}/", include(f"{app}.urls", namespace=app))
|
||||
for app in OPTIONNAL_APPS_RE2O
|
||||
]
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ from django.contrib.auth.models import Group, Permission
|
|||
from django.db.models import Q
|
||||
from django.utils import timezone
|
||||
|
||||
from cotisations.models import Cotisation, Facture, Vente
|
||||
from cotisations.models import Cotisation, Facture
|
||||
from machines.models import Interface, Machine
|
||||
from preferences.models import AssoOption
|
||||
from users.models import Adherent, Ban, User, Whitelist
|
||||
|
@ -177,9 +177,9 @@ def all_whitelisted(search_time=None, dormitory=None, user_type="all"):
|
|||
|
||||
|
||||
def all_conn(search_time=None, including_asso=True, dormitory=None, user_type="all"):
|
||||
"""Return all people who have a valid connection payment at org. Optimised to make only one
|
||||
sql query. Build a filter and then apply it to User. Check for each user if a valid
|
||||
connection is registered at the desired search_time.
|
||||
"""Return all people who have a valid connection payment at org. Optimised to make
|
||||
only one sql query. Build a filter and then apply it to User. Check for each user
|
||||
if a valid connection is registered at the desired search_time.
|
||||
|
||||
Parameters:
|
||||
search_time (django datetime): Datetime to perform this search,
|
||||
|
@ -187,7 +187,8 @@ def all_conn(search_time=None, including_asso=True, dormitory=None, user_type="a
|
|||
including_asso (boolean): Decide if org itself is included in results
|
||||
|
||||
Returns:
|
||||
django queryset: Django queryset containing all users with valid connection perdiod
|
||||
django queryset: Django queryset containing all users with valid connection
|
||||
perdiod
|
||||
|
||||
"""
|
||||
if search_time is None:
|
||||
|
@ -212,9 +213,9 @@ def all_conn(search_time=None, including_asso=True, dormitory=None, user_type="a
|
|||
def all_has_access(
|
||||
search_time=None, including_asso=True, dormitory=None, user_type="all"
|
||||
):
|
||||
"""Return all people who have an valid internet access at org. Call previously buid filters.
|
||||
Can't do that in one sql query unfortunatly. Apply each filters, and return users
|
||||
with a whitelist, or a valid paid access, except banned users.
|
||||
"""Return all people who have an valid internet access at org. Call previously
|
||||
buid filters. Can't do that in one sql query unfortunatly. Apply each filters,
|
||||
and return users with a whitelist, or a valid paid access, except banned users.
|
||||
|
||||
Parameters:
|
||||
search_time (django datetime): Datetime to perform this search,
|
||||
|
@ -306,8 +307,8 @@ def filter_complete_interfaces(interface_set):
|
|||
"""Return a filter for filtering all interfaces of people who have an valid
|
||||
internet access at org.
|
||||
Call all_active_interfaces and then apply filter of theses active users on an
|
||||
interfaces_set. Less efficient than filter_active_interfaces, with a prefetch_related
|
||||
on ipv6
|
||||
interfaces_set. Less efficient than filter_active_interfaces, with a
|
||||
prefetch_related on ipv6
|
||||
|
||||
Parameters:
|
||||
interface_set (django queryset): A queryset of interfaces to perform filter
|
||||
|
@ -327,18 +328,17 @@ def all_active_interfaces(full=False):
|
|||
Call filter_active_interfaces or filter_complete_interfaces.
|
||||
|
||||
Parameters:
|
||||
full (boolean): A queryset of interfaces to perform filter. If true, will perform
|
||||
a complete filter with filter_complete_interfaces
|
||||
full (boolean): A queryset of interfaces to perform filter. If true, will
|
||||
perform a complete filter with filter_complete_interfaces
|
||||
|
||||
Returns:
|
||||
django queryset: Django queryset containing all active interfaces, related with
|
||||
a user with valid membership
|
||||
django queryset: Django queryset containing all active interfaces, related
|
||||
with a user with valid membership
|
||||
|
||||
"""
|
||||
if full:
|
||||
return filter_complete_interfaces(Interface.objects)
|
||||
else:
|
||||
return filter_active_interfaces(Interface.objects)
|
||||
return filter_active_interfaces(Interface.objects)
|
||||
|
||||
|
||||
def all_active_assigned_interfaces(full=False):
|
||||
|
@ -347,12 +347,12 @@ def all_active_assigned_interfaces(full=False):
|
|||
Call filter_active_interfaces or filter_complete_interfaces, with parameter full.
|
||||
|
||||
Parameters:
|
||||
full (boolean): A queryset of interfaces to perform filter. If true, will perform
|
||||
a complete filter with filter_complete_interfaces
|
||||
full (boolean): A queryset of interfaces to perform filter. If true, will
|
||||
perform a complete filter with filter_complete_interfaces
|
||||
|
||||
Returns:
|
||||
django queryset: Django queryset containing all active interfaces, related with
|
||||
a user with valid membership, and with valid assigned ipv4 address
|
||||
django queryset: Django queryset containing all active interfaces, related
|
||||
with a user with valid membership, and with valid assigned ipv4 address
|
||||
|
||||
"""
|
||||
return all_active_interfaces(full=full).filter(ipv4__isnull=False)
|
||||
|
@ -409,7 +409,6 @@ def permission_tree(queryset=None):
|
|||
permissions = queryset or Permission.objects.all()
|
||||
for p in permissions:
|
||||
key, app, model = p.natural_key()
|
||||
name = p.name
|
||||
if app not in r:
|
||||
r[app] = {}
|
||||
if model not in r[app]:
|
||||
|
|
|
@ -34,7 +34,6 @@ from django.conf import settings
|
|||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.shortcuts import render
|
||||
from django.template.context_processors import csrf
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from preferences.models import (
|
||||
|
@ -51,8 +50,8 @@ from .contributors import CONTRIBUTORS
|
|||
|
||||
|
||||
def form(ctx, template, request):
|
||||
"""Global template function, used in all re2o views, for building a render with context,
|
||||
template and request. Adding csrf.
|
||||
"""Global template function, used in all re2o views, for building a render with
|
||||
context, template and request. Adding csrf.
|
||||
|
||||
Parameters:
|
||||
ctx (dict): Dict of values to transfer to template
|
||||
|
@ -109,11 +108,11 @@ def about_page(request):
|
|||
git_info_commit = last_commit.hexsha
|
||||
git_info_commit_date = last_commit.committed_datetime
|
||||
except:
|
||||
NO_GIT_MSG = _("Unable to get the information.")
|
||||
git_info_remote = NO_GIT_MSG
|
||||
git_info_branch = NO_GIT_MSG
|
||||
git_info_commit = NO_GIT_MSG
|
||||
git_info_commit_date = NO_GIT_MSG
|
||||
no_msg_git = _("Unable to get the information.")
|
||||
git_info_remote = no_msg_git
|
||||
git_info_branch = no_msg_git
|
||||
git_info_commit = no_msg_git
|
||||
git_info_commit_date = no_msg_git
|
||||
|
||||
dependencies = settings.INSTALLED_APPS + settings.MIDDLEWARE
|
||||
|
||||
|
@ -179,7 +178,9 @@ class AutocompleteLoggedOutViewMixin(autocomplete.Select2QuerySetView):
|
|||
query_filter = "name__icontains" # Override this if necessary
|
||||
|
||||
def get_queryset(self):
|
||||
can, reason, _permission, query_set = self.obj_type.can_list(self.request.user)
|
||||
_can, _reason, _permission, query_set = self.obj_type.can_list(
|
||||
self.request.user
|
||||
)
|
||||
|
||||
if query_set:
|
||||
self.query_set = query_set
|
||||
|
|
|
@ -30,6 +30,7 @@ from django.utils.translation import ugettext as _
|
|||
|
||||
|
||||
class AutocompleteModelWidget(autocomplete.ModelSelect2):
|
||||
# pylint: disable=line-too-long
|
||||
"""A mixin subclassing django-autocomplete-light's Select2 model to pass default options
|
||||
See https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#passing-options-to-select2
|
||||
"""
|
||||
|
@ -46,7 +47,8 @@ class AutocompleteModelWidget(autocomplete.ModelSelect2):
|
|||
"""
|
||||
# Display the "x" button to clear the input by default
|
||||
attrs["data-allow-clear"] = attrs.get("data-allow-clear", "true")
|
||||
# If there are less than 10 results, just show all of them (no need to autocomplete)
|
||||
# If there are less than 10 results, just show all of them
|
||||
# (no need to autocomplete)
|
||||
attrs["data-minimum-results-for-search"] = attrs.get(
|
||||
"data-minimum-results-for-search", 10
|
||||
)
|
||||
|
@ -54,6 +56,7 @@ class AutocompleteModelWidget(autocomplete.ModelSelect2):
|
|||
|
||||
|
||||
class AutocompleteMultipleModelWidget(autocomplete.ModelSelect2Multiple):
|
||||
# pylint: disable=line-too-long
|
||||
"""A mixin subclassing django-autocomplete-light's Select2 model to pass default options
|
||||
See https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#passing-options-to-select2
|
||||
"""
|
||||
|
@ -70,7 +73,8 @@ class AutocompleteMultipleModelWidget(autocomplete.ModelSelect2Multiple):
|
|||
"""
|
||||
# Display the "x" button to clear the input by default
|
||||
attrs["data-allow-clear"] = attrs.get("data-allow-clear", "true")
|
||||
# If there are less than 10 results, just show all of them (no need to autocomplete)
|
||||
# If there are less than 10 results, just show all of them
|
||||
# (no need to autocomplete)
|
||||
attrs["data-minimum-results-for-search"] = attrs.get(
|
||||
"data-minimum-results-for-search", 10
|
||||
)
|
||||
|
|
|
@ -47,68 +47,46 @@ from .models import (
|
|||
class StackAdmin(VersionAdmin):
|
||||
"""Admin class of stacks (includes switches)."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class SwitchAdmin(VersionAdmin):
|
||||
"""Admin class of switches."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class PortAdmin(VersionAdmin):
|
||||
"""Admin class of switch ports."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class AccessPointAdmin(VersionAdmin):
|
||||
"""Admin class of APs."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class RoomAdmin(VersionAdmin):
|
||||
"""Admin class of rooms."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ModelSwitchAdmin(VersionAdmin):
|
||||
"""Admin class of switch models."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ConstructorSwitchAdmin(VersionAdmin):
|
||||
"""Admin class of switch constructors."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class SwitchBayAdmin(VersionAdmin):
|
||||
"""Admin class of switch bays."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class BuildingAdmin(VersionAdmin):
|
||||
"""Admin class of buildings."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class DormitoryAdmin(VersionAdmin):
|
||||
"""Admin class of dormitories."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class PortProfileAdmin(VersionAdmin):
|
||||
"""Admin class of port profiles."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
admin.site.register(Port, PortAdmin)
|
||||
admin.site.register(AccessPoint, AccessPointAdmin)
|
||||
|
|
|
@ -23,7 +23,7 @@ from rest_framework import serializers
|
|||
|
||||
import machines.models as machines
|
||||
import topologie.models as topologie
|
||||
from api.serializers import NamespacedHIField, NamespacedHMSerializer, NamespacedHRField
|
||||
from api.serializers import NamespacedHMSerializer
|
||||
from machines.api.serializers import Ipv6ListSerializer, VlanSerializer
|
||||
|
||||
|
||||
|
|
|
@ -77,14 +77,15 @@ class PortForm(FormRevMixin, ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(PortForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class EditPortForm(FormRevMixin, ModelForm):
|
||||
"""Form used to edit a switch's port: change in RADIUS or VLANs settings,
|
||||
assignement to a room, port or machine.
|
||||
|
||||
A port is related to either a room, another port (uplink) or a machine (server or AP).
|
||||
A port is related to either a room, another port (uplink)
|
||||
or a machine (server or AP).
|
||||
"""
|
||||
|
||||
class Meta(PortForm.Meta):
|
||||
|
@ -99,7 +100,7 @@ class EditPortForm(FormRevMixin, ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(EditPortForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields["machine_interface"].queryset = (
|
||||
Interface.objects.all().select_related("domain__extension")
|
||||
)
|
||||
|
@ -127,7 +128,7 @@ class AddPortForm(FormRevMixin, ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(AddPortForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields["machine_interface"].queryset = (
|
||||
Interface.objects.all().select_related("domain__extension")
|
||||
)
|
||||
|
@ -152,7 +153,7 @@ class StackForm(FormRevMixin, ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(StackForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class AddAccessPointForm(NewMachineForm):
|
||||
|
@ -204,7 +205,7 @@ class EditRoomForm(FormRevMixin, ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(EditRoomForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class CreatePortsForm(forms.Form):
|
||||
|
@ -229,7 +230,7 @@ class EditModelSwitchForm(FormRevMixin, ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(EditModelSwitchForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
instance = kwargs.get("instance", None)
|
||||
if instance:
|
||||
self.initial["members"] = Switch.objects.filter(model=instance)
|
||||
|
@ -249,7 +250,7 @@ class EditConstructorSwitchForm(FormRevMixin, ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(EditConstructorSwitchForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class EditSwitchBayForm(FormRevMixin, ModelForm):
|
||||
|
@ -270,7 +271,7 @@ class EditSwitchBayForm(FormRevMixin, ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(EditSwitchBayForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
instance = kwargs.get("instance", None)
|
||||
if instance:
|
||||
self.initial["members"] = Switch.objects.filter(switchbay=instance)
|
||||
|
@ -290,7 +291,7 @@ 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)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class EditDormitoryForm(FormRevMixin, ModelForm):
|
||||
|
@ -302,7 +303,7 @@ class EditDormitoryForm(FormRevMixin, ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(EditDormitoryForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class EditPortProfileForm(FormRevMixin, ModelForm):
|
||||
|
@ -320,7 +321,7 @@ class EditPortProfileForm(FormRevMixin, ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(EditPortProfileForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class EditModuleForm(FormRevMixin, ModelForm):
|
||||
|
@ -332,7 +333,7 @@ class EditModuleForm(FormRevMixin, ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(EditModuleForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class EditSwitchModuleForm(FormRevMixin, ModelForm):
|
||||
|
@ -344,4 +345,4 @@ class EditSwitchModuleForm(FormRevMixin, ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||
super(EditSwitchModuleForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super().__init__(*args, prefix=prefix, **kwargs)
|
||||
|
|
|
@ -34,11 +34,9 @@ The following models are defined:
|
|||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import itertools
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import IntegrityError, models, transaction
|
||||
from django.db import models, transaction
|
||||
from django.db.models.signals import post_delete, post_save
|
||||
from django.dispatch import receiver
|
||||
from django.utils.functional import cached_property
|
||||
|
@ -78,7 +76,7 @@ class Stack(AclMixin, RevMixin, models.Model):
|
|||
self.clean()
|
||||
if not self.name:
|
||||
self.name = self.stack_id
|
||||
super(Stack, self).save(*args, **kwargs)
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
"""Check if id_max < id_min."""
|
||||
|
@ -143,7 +141,8 @@ class AccessPoint(Machine):
|
|||
|
||||
# We want to retrieve the default behaviour given by AclMixin rather
|
||||
# than the one overwritten by Machine. If you are not familiar with
|
||||
# the behaviour of `super`, please check https://docs.python.org/3/library/functions.html#super
|
||||
# the behaviour of `super`, please check
|
||||
# https://docs.python.org/3/library/functions.html#super
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls, *args, **kwargs):
|
||||
|
@ -208,7 +207,8 @@ class Server(Machine):
|
|||
|
||||
# We want to retrieve the default behaviour given by AclMixin rather
|
||||
# than the one overwritten by Machine. If you are not familiar with
|
||||
# the behaviour of `super`, please check https://docs.python.org/3/library/functions.html#super
|
||||
# the behaviour of `super`, please check
|
||||
# https://docs.python.org/3/library/functions.html#super
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls, *args, **kwargs):
|
||||
|
@ -285,7 +285,7 @@ class Switch(Machine):
|
|||
"""Check if the stack member ID is in the range of the stack's IDs and
|
||||
calls the clean of the parent class.
|
||||
"""
|
||||
super(Switch, self).clean()
|
||||
super().clean()
|
||||
if self.stack is not None:
|
||||
if self.stack_member_id is not None:
|
||||
if (self.stack_member_id > self.stack.member_id_max) or (
|
||||
|
@ -356,8 +356,7 @@ class Switch(Machine):
|
|||
"""
|
||||
if self.get_radius_key:
|
||||
return self.get_radius_key.radius_key
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
|
||||
@cached_property
|
||||
def get_radius_servers_objects(self):
|
||||
|
@ -409,8 +408,7 @@ class Switch(Machine):
|
|||
"id": self.get_management_cred.management_id,
|
||||
"pass": self.get_management_cred.management_pass,
|
||||
}
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
|
||||
@cached_property
|
||||
def rest_enabled(self):
|
||||
|
@ -427,10 +425,9 @@ class Switch(Machine):
|
|||
)
|
||||
if sw_management_ssl:
|
||||
return "ssl"
|
||||
elif sw_management:
|
||||
if sw_management:
|
||||
return "plain"
|
||||
else:
|
||||
return self.automatic_provision
|
||||
return self.automatic_provision
|
||||
|
||||
@cached_property
|
||||
def ipv4(self):
|
||||
|
@ -487,8 +484,7 @@ class Switch(Machine):
|
|||
"""Get the dormitory in which the switch is located."""
|
||||
if self.switchbay:
|
||||
return self.switchbay.building.dormitory
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def nothing_profile(cls):
|
||||
|
@ -540,7 +536,8 @@ class Switch(Machine):
|
|||
|
||||
# We want to retrieve the default behaviour given by AclMixin rather
|
||||
# than the one overwritten by Machine. If you are not familiar with
|
||||
# the behaviour of `super`, please check https://docs.python.org/3/library/functions.html#super
|
||||
# the behaviour of `super`, please check
|
||||
# https://docs.python.org/3/library/functions.html#super
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls, *args, **kwargs):
|
||||
|
@ -592,8 +589,7 @@ class ModelSwitch(AclMixin, RevMixin, models.Model):
|
|||
def __str__(self):
|
||||
if self.commercial_name:
|
||||
return str(self.constructor) + " " + str(self.commercial_name)
|
||||
else:
|
||||
return str(self.constructor) + " " + self.reference
|
||||
return str(self.constructor) + " " + self.reference
|
||||
|
||||
|
||||
class ModuleSwitch(AclMixin, RevMixin, models.Model):
|
||||
|
@ -711,8 +707,7 @@ class Dormitory(AclMixin, RevMixin, models.Model):
|
|||
multiple_dorms = cache.get("multiple_dorms")
|
||||
if multiple_dorms:
|
||||
return multiple_dorms
|
||||
else:
|
||||
return cache.get_or_set("multiple_dorms", cls.objects.count() > 1)
|
||||
return cache.get_or_set("multiple_dorms", cls.objects.count() > 1)
|
||||
|
||||
@classmethod
|
||||
def can_list(cls, user_request, *_args, **_kwargs):
|
||||
|
@ -752,8 +747,7 @@ class Building(AclMixin, RevMixin, models.Model):
|
|||
def get_name(self):
|
||||
if Dormitory.is_multiple_dorms():
|
||||
return self.dormitory.name + " : " + self.name
|
||||
else:
|
||||
return self.name
|
||||
return self.name
|
||||
|
||||
@classmethod
|
||||
def can_list(cls, user_request, *_args, **_kwargs):
|
||||
|
@ -835,12 +829,11 @@ class Port(AclMixin, RevMixin, models.Model):
|
|||
"""More elaborated name for label on switch configuration."""
|
||||
if self.related:
|
||||
return _("Uplink: ") + self.related.switch.short_name
|
||||
elif self.machine_interface:
|
||||
if self.machine_interface:
|
||||
return _("Machine: ") + str(self.machine_interface.domain)
|
||||
elif self.room:
|
||||
if self.room:
|
||||
return _("Room: ") + str(self.room)
|
||||
else:
|
||||
return _("Unknown")
|
||||
return _("Unknown")
|
||||
|
||||
@cached_property
|
||||
def get_port_profile(self):
|
||||
|
@ -853,17 +846,15 @@ class Port(AclMixin, RevMixin, models.Model):
|
|||
"""
|
||||
if self.custom_profile:
|
||||
return self.custom_profile
|
||||
elif self.related:
|
||||
if self.related:
|
||||
return self.switch.default_uplink_profile
|
||||
elif self.machine_interface:
|
||||
if self.machine_interface:
|
||||
if hasattr(self.machine_interface.machine, "accesspoint"):
|
||||
return self.switch.default_access_point_profile
|
||||
else:
|
||||
return self.switch.default_asso_machine_profile
|
||||
elif self.room:
|
||||
return self.switch.default_asso_machine_profile
|
||||
if self.room:
|
||||
return self.switch.default_room_profile
|
||||
else:
|
||||
return Switch.nothing_profile()
|
||||
return Switch.nothing_profile()
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls, port_id, *_args, **kwargs):
|
||||
|
@ -922,8 +913,7 @@ class Port(AclMixin, RevMixin, models.Model):
|
|||
" before creating the relation."
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.make_port_related()
|
||||
self.make_port_related()
|
||||
elif hasattr(self, "related_port"):
|
||||
self.clean_port_related()
|
||||
|
||||
|
@ -1117,7 +1107,7 @@ class PortProfile(AclMixin, RevMixin, models.Model):
|
|||
|
||||
def clean(self):
|
||||
"""Check that there is only one generic profile default."""
|
||||
super(PortProfile, self).clean()
|
||||
super().clean()
|
||||
if (
|
||||
self.profil_default
|
||||
and not self.on_dormitory
|
||||
|
@ -1129,7 +1119,8 @@ class PortProfile(AclMixin, RevMixin, models.Model):
|
|||
raise ValidationError(
|
||||
{
|
||||
"profil_default": _(
|
||||
"A default profile for all dormitories of that type already exists."
|
||||
"A default profile for all dormitories of that type "
|
||||
"already exists."
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1438,8 +1438,10 @@ def recursive_switchs(switch_start, switch_before, detected):
|
|||
|
||||
Args:
|
||||
switch_start: the switch to begin the visit on.
|
||||
switch_before: the switch that you come from. None if switch_start is the first one.
|
||||
detected: list of all switches already visited. None if switch_start is the first one.
|
||||
switch_before: the switch that you come from.
|
||||
None if switch_start is the first one.
|
||||
detected: list of all switches already visited.
|
||||
None if switch_start is the first one.
|
||||
|
||||
Returns:
|
||||
A list of all the links found and a list of all the switches visited.
|
||||
|
|
|
@ -42,9 +42,11 @@ from .models import Building, Dormitory, Port, PortProfile, Room, Switch, Switch
|
|||
class RoomAutocomplete(AutocompleteLoggedOutViewMixin):
|
||||
obj_type = Room
|
||||
|
||||
# Precision on search to add annotations so search behaves more like users expect it to
|
||||
# Precision on search to add annotations so search behaves more like
|
||||
# users expect it to
|
||||
def filter_results(self):
|
||||
# Suppose we have a dorm named Dorm, a building named B, and rooms from 001 - 999
|
||||
# Suppose we have a dorm named Dorm, a building named B,
|
||||
# and rooms from 001 - 999
|
||||
# Comments explain what we try to match
|
||||
self.query_set = self.query_set.annotate(
|
||||
full_name=Concat(
|
||||
|
@ -70,6 +72,7 @@ class RoomAutocomplete(AutocompleteLoggedOutViewMixin):
|
|||
), # Match "Dorm : B 001" (see Room's full_name property)
|
||||
).all()
|
||||
|
||||
# pylint: disable=unsupported-binary-operation
|
||||
if self.q:
|
||||
self.query_set = self.query_set.filter(
|
||||
Q(full_name__icontains=self.q)
|
||||
|
@ -109,7 +112,8 @@ class PortAutocomplete(AutocompleteViewMixin):
|
|||
|
||||
def filter_results(self):
|
||||
# We want to enter the switch name, not just the port number
|
||||
# Because we're concatenating a CharField and an Integer, we have to specify the output_field
|
||||
# Because we're concatenating a CharField and an Integer,
|
||||
# Zwe have to specify the output_field
|
||||
self.query_set = self.query_set.annotate(
|
||||
full_name=Concat(
|
||||
"switch__name", Value(" "), "port", output_field=CharField()
|
||||
|
@ -121,6 +125,7 @@ class PortAutocomplete(AutocompleteViewMixin):
|
|||
).all()
|
||||
|
||||
if self.q:
|
||||
# pylint: disable=unsupported-binary-operation
|
||||
self.query_set = self.query_set.filter(
|
||||
Q(full_name__icontains=self.q)
|
||||
| Q(full_name_stuck__icontains=self.q)
|
||||
|
@ -153,6 +158,7 @@ class SwitchBayAutocomplete(AutocompleteViewMixin):
|
|||
).all()
|
||||
|
||||
if self.q:
|
||||
# pylint: disable=unsupported-binary-operation
|
||||
self.query_set = self.query_set.filter(
|
||||
Q(full_name__icontains=self.q)
|
||||
| Q(dorm_name__icontains=self.q)
|
||||
|
|
|
@ -9,7 +9,7 @@ from django.db.models.functions import Concat
|
|||
from reversion.models import Revision
|
||||
|
||||
from machines.models import Domain, Machine
|
||||
from re2o.login import hashNT, makeSecret
|
||||
from re2o.login import hash_nt, make_secret
|
||||
from users.models import Adherent, Club, School, User
|
||||
|
||||
|
||||
|
@ -104,8 +104,8 @@ class Command(BaseCommand):
|
|||
)
|
||||
)
|
||||
|
||||
u.update(pwd_ntlm=hashNT(password))
|
||||
u.update(password=makeSecret(password))
|
||||
u.update(pwd_ntlm=hash_nt(password))
|
||||
u.update(password=make_secret(password))
|
||||
self.stdout.write(self.style.SUCCESS("Done..."))
|
||||
|
||||
self.stdout.write("Deletion of the history (this may take some time)...")
|
||||
|
|
|
@ -933,10 +933,10 @@ class User(
|
|||
password (string): new password (cleatext) to set.
|
||||
|
||||
"""
|
||||
from re2o.login import hashNT
|
||||
from re2o.login import hash_nt
|
||||
|
||||
super().set_password(password)
|
||||
self.pwd_ntlm = hashNT(password)
|
||||
self.pwd_ntlm = hash_nt(password)
|
||||
return
|
||||
|
||||
def confirm_mail(self):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue