diff --git a/api/locale/fr/LC_MESSAGES/django.po b/api/locale/fr/LC_MESSAGES/django.po index 3c08e4e5..f5f9153f 100644 --- a/api/locale/fr/LC_MESSAGES/django.po +++ b/api/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2019-01-07 01:37+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po index aca2607b..ac3900ea 100644 --- a/cotisations/locale/fr/LC_MESSAGES/django.po +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language: fr_FR\n" diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index bb8dc944..2c5265fc 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2018-06-23 16:01+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -58,6 +58,20 @@ msgstr "Date de début" msgid "End date" msgstr "Date de fin" +#: logs/models.py:238 logs/models.py:263 +msgid "None" +msgstr "Aucun(e)" + +#: logs/models.py:248 +msgid "Deleted" +msgstr "Supprimé(e)" + +#: logs/models.py:255 logs/models.py:260 +#: logs/templates/logs/machine_history.html:55 +#: logs/templates/logs/user_history.html:51 +msgid "Unknown" +msgstr "Inconnu(e)" + #: logs/templates/logs/aff_stats_logs.html:36 msgid "Edited object" msgstr "Objet modifié" @@ -77,6 +91,7 @@ msgstr "Date de modification" #: logs/templates/logs/aff_stats_logs.html:42 #: logs/templates/logs/machine_history.html:39 +#: logs/templates/logs/user_history.html:39 msgid "Comment" msgstr "Commentaire" @@ -113,6 +128,7 @@ msgid "Rank" msgstr "Rang" #: logs/templates/logs/aff_summary.html:37 +#: logs/templates/logs/user_history.html:36 msgid "Date" msgstr "Date" @@ -196,15 +212,11 @@ msgstr "Résultats de la recherche" msgid "User" msgstr "Utilisateur" -#: logs/templates/logs/machine_history.html:55 -msgid "Unknown" -msgstr "Inconnu(e)" - #: logs/templates/logs/machine_history.html:62 msgid "Now" msgstr "Maintenant" -#: logs/templates/logs/machine_history.html:70 +#: logs/templates/logs/machine_history.html:71 msgid "No result" msgstr "Aucun résultat" @@ -253,6 +265,27 @@ msgstr "Statistiques sur la base de données" msgid "Statistics about users" msgstr "Statistiques sur les utilisateurs" +#: logs/templates/logs/user_history.html:27 +msgid "History" +msgstr "Historique" + +#: logs/templates/logs/user_history.html:30 +#, python-format +msgid "History of %(user)s" +msgstr "Historique de %(user)s" + +#: logs/templates/logs/user_history.html:37 +msgid "Performed by" +msgstr "Effectué(e) par" + +#: logs/templates/logs/user_history.html:38 +msgid "Edited" +msgstr "Modifié" + +#: logs/templates/logs/user_history.html:74 +msgid "No event" +msgstr "Aucun évènement" + #: logs/views.py:178 msgid "Nonexistent revision." msgstr "Révision inexistante." @@ -377,14 +410,14 @@ msgstr "droits" msgid "actions" msgstr "actions" -#: logs/views.py:529 +#: logs/views.py:554 msgid "No model found." msgstr "Aucun modèle trouvé." -#: logs/views.py:535 +#: logs/views.py:560 msgid "Nonexistent entry." msgstr "Entrée inexistante." -#: logs/views.py:542 +#: logs/views.py:567 msgid "You don't have the right to access this menu." msgstr "Vous n'avez pas le droit d'accéder à ce menu." diff --git a/logs/models.py b/logs/models.py index 343a33cb..2066c77e 100644 --- a/logs/models.py +++ b/logs/models.py @@ -22,14 +22,19 @@ The models definitions for the logs app """ from reversion.models import Version +from django.utils.translation import ugettext_lazy as _ +from django.contrib.auth.models import Group from machines.models import IpList from machines.models import Interface from machines.models import Machine from users.models import User +from users.models import Adherent +from users.models import Club +from topologie.models import Room -class HistoryEvent: +class MachineHistoryEvent: def __init__(self, user, machine, interface, start=None, end=None): """ :param user: User, The user owning the maching at the time of the event @@ -80,7 +85,7 @@ class MachineHistory: """ :param search: ip or mac to lookup :param params: dict built by the search view - :return: list or None, a list of HistoryEvent + :return: list or None, a list of MachineHistoryEvent """ self.start = params.get("s", None) self.end = params.get("e", None) @@ -101,7 +106,7 @@ class MachineHistory: :param machine: Version, the machine version related to the interface :param interface: Version, the interface targeted by this event """ - evt = HistoryEvent(user, machine, interface) + evt = MachineHistoryEvent(user, machine, interface) evt.start_date = interface.revision.date_created # Try not to recreate events if it's unnecessary @@ -177,7 +182,7 @@ class MachineHistory: def __get_by_ip(self, ip): """ :param ip: str, The IP to lookup - :returns: list, a list of HistoryEvent + :returns: list, a list of MachineHistoryEvent """ interfaces = self.__get_interfaces_for_ip(ip) @@ -193,7 +198,7 @@ class MachineHistory: def __get_by_mac(self, mac): """ :param mac: str, The MAC address to lookup - :returns: list, a list of HistoryEvent + :returns: list, a list of MachineHistoryEvent """ interfaces = self.__get_interfaces_for_mac(mac) @@ -205,3 +210,205 @@ class MachineHistory: self.__add_revision(user, machine, interface) return self.events + + +class UserHistoryEvent: + def __init__(self, user, version, previous_version=None, edited_fields=None): + """ + :param user: User, The user who's history is being built + :param version: Version, the version of the user for this event + :param previous_version: Version, the version of the user before this event + :param edited_fields: list, The list of modified fields by this event + """ + self.user = user + self.version = version + self.previous_version = previous_version + self.edited_fields = edited_fields + self.date = version.revision.date_created + self.performed_by = version.revision.user + self.comment = version.revision.get_comment() or None + + def __repr(self, name, value): + """ + Returns the best representation of the given field + :param name: the name of the field + :param value: the value of the field + :return: object + """ + if name == "groups": + if len(value) == 0: + # Removed all the user's groups + return _("None") + + # value is a list of ints + groups = [] + for gid in value: + # Try to get the group name, if it's not deleted + try: + groups.append(Group.objects.get(id=gid).name) + except Group.DoesNotExist: + # TODO: Find the group name in the versions? + groups.append("{} ({})".format(_("Deleted"), gid)) + + return ", ".join(groups) + elif name == "state": + if value is not None: + return User.STATES[value][1] + else: + return _("Unknown") + elif 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: + # 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": + if len(value) == 0: + # Removed all the club's members + return _("None") + + # value is a list of ints + users = [] + for uid in value: + # Try to get the user's name, if theyr're not deleted + try: + 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)) + + return ", ".join(users) + + if value is None: + return _("None") + + return value + + def edits(self, hide=["password", "pwd_ntlm", "gpg_fingerprint"]): + """ + Build a list of the changes performed during this event + :param hide: list, the list of fields for which not to show details + :return: str + """ + edits = [] + + for field in self.edited_fields: + if field in hide: + # Don't show sensitive information + edits.append((field, None, None)) + else: + edits.append(( + field, + self.__repr(field, self.previous_version.field_dict[field]), + self.__repr(field, self.version.field_dict[field]) + )) + + return edits + + def __eq__(self, other): + return ( + self.user.id == other.user.id + and self.edited_fields == other.edited_fields + and self.date == other.date + and self.performed_by == other.performed_by + and self.comment == other.comment + ) + + def __hash__(self): + return hash((self.user.id, frozenset(self.edited_fields), self.date, self.performed_by, self.comment)) + + def __repr__(self): + return "{} edited fields {} of {} ({})".format( + self.performed_by, + self.edited_fields or "nothing", + self.user, + self.comment or "No comment" + ) + + +class UserHistory: + def __init__(self): + self.events = [] + self.__last_version = None + + def get(self, user): + """ + :param user: User, the user to lookup + :return: list or None, a list of UserHistoryEvent, in reverse chronological order + """ + self.events = [] + + # Find whether this is a Club or an Adherent + try: + obj = Adherent.objects.get(user_ptr_id=user.id) + except Adherent.DoesNotExist: + obj = Club.objects.get(user_ptr_id=user.id) + + # Get all the versions for this user, with the oldest first + self.__last_version = None + user_versions = filter( + lambda x: x.field_dict["id"] == user.id, + Version.objects.get_for_model(User).order_by("revision__date_created") + ) + + for version in user_versions: + self.__add_revision(user, version) + + # Do the same thing for the Adherent of Club + self.__last_version = None + obj_versions = filter( + lambda x: x.field_dict["id"] == obj.id, + Version.objects.get_for_model(type(obj)).order_by("revision__date_created") + ) + + for version in obj_versions: + self.__add_revision(user, version) + + # Remove duplicates and sort + self.events = list(dict.fromkeys(self.events)) + return sorted( + self.events, + key=lambda e: e.date, + reverse=True + ) + + def __compute_diff(self, v1, v2, ignoring=["last_login", "pwd_ntlm", "email_change_date"]): + """ + Find the edited field between two versions + :param v1: Version + :param v2: Version + :param ignoring: List, a list of fields to ignore + :return: List of field names + """ + fields = [] + + for key in v1.field_dict.keys(): + if key not in ignoring and v1.field_dict[key] != v2.field_dict[key]: + fields.append(key) + + return fields + + def __add_revision(self, user, version): + """ + Add a new revision to the chronological order + :param user: User, The user displayed in this history + :param version: Version, The version of the user for this event + """ + diff = None + if self.__last_version is not None: + diff = self.__compute_diff(version, self.__last_version) + + # Ignore "empty" events like login + if not diff: + self.__last_version = version + return + + evt = UserHistoryEvent(user, version, self.__last_version, diff) + self.events.append(evt) + self.__last_version = version diff --git a/logs/templates/logs/user_history.html b/logs/templates/logs/user_history.html new file mode 100644 index 00000000..ce492281 --- /dev/null +++ b/logs/templates/logs/user_history.html @@ -0,0 +1,80 @@ +{% extends 'logs/sidebar.html' %} +{% comment %} +Re2o est un logiciel d'administration développé initiallement au rezometz. Il +se veut agnostique au réseau considéré, de manière à être installable en +quelques clics. + +Copyright © 2020 Jean-Romain Garnier + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +{% endcomment %} + +{% load bootstrap3 %} +{% load i18n %} + +{% block title %}{% trans "History" %}{% endblock %} + +{% block content %} +

{% blocktrans %}History of {{ user }}{% endblocktrans %}

+ +{% if events %} + + + + + + + + + + {% for event in events %} + + + + + + + {% endfor %} +
{% trans "Date" %}{% trans "Performed by" %}{% trans "Edited" %}{% trans "Comment" %}
{{ event.date }} + {% if event.performed_by %} + + {{ event.performed_by }} + + {% else %} + {% trans "Unknown" %} + {% endif %} + + {% for edit in event.edits %} + {% if edit.1 is None and edit.2 is None %} + {{ edit.0 }}
+ {% elif edit.1 is None %} + {{ edit.0 }}: + {{ edit.2 }}
+ {% else %} + {{ edit.0 }}: + {{ edit.1 }} + ➔ {{ edit.2 }}
+ {% endif %} + {% endfor %} +
{{ event.comment }}
+ {% include 'pagination.html' with list=events %} +{% else %} +

{% trans "No event" %}

+{% endif %} +
+
+
+ +{% endblock %} diff --git a/logs/urls.py b/logs/urls.py index eced2a83..562bd93c 100644 --- a/logs/urls.py +++ b/logs/urls.py @@ -47,4 +47,5 @@ urlpatterns = [ name="history", ), url(r"^stats_search_machine/$", views.stats_search_machine_history, name="stats-search-machine"), + url(r"^user/(?P[0-9]+)$", views.user_history, name="user-history"), ] diff --git a/logs/views.py b/logs/views.py index 85ba35cf..25ee7403 100644 --- a/logs/views.py +++ b/logs/views.py @@ -99,9 +99,9 @@ from re2o.utils import ( all_active_interfaces_count, ) from re2o.base import re2o_paginator, SortTable -from re2o.acl import can_view_all, can_view_app, can_edit_history +from re2o.acl import can_view_all, can_view_app, can_edit_history, can_view -from .models import MachineHistory +from .models import MachineHistory, UserHistory from .forms import MachineHistoryForm @@ -508,6 +508,26 @@ def stats_search_machine_history(request): return render(request, "logs/search_machine_history.html", {"history_form": history_form}) +@login_required +@can_view(User) +def user_history(request, users, **_kwargs): + history = UserHistory() + events = history.get(users) + + max_result = GeneralOption.get_cached_value("pagination_number") + events = re2o_paginator( + request, + events, + max_result + ) + + return render( + request, + "logs/user_history.html", + { "user": users, "events": events }, + ) + + def history(request, application, object_name, object_id): """Render history for a model. diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index f1726d25..5013ea49 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/multi_op/locale/fr/LC_MESSAGES/django.po b/multi_op/locale/fr/LC_MESSAGES/django.po index 14c22e92..f4daa664 100644 --- a/multi_op/locale/fr/LC_MESSAGES/django.po +++ b/multi_op/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2019-11-16 00:22+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index dc41163f..2f109979 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 11:55+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2018-06-24 15:54+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -342,13 +342,13 @@ msgid "Maximum number of local email addresses for a standard user." msgstr "" "Nombre maximum d'adresses mail locales autorisé pour un utilisateur standard." -#: preferences/models.py:125 +#: preferences/models.py:122 msgid "Not yet active users will be deleted after this number of days." msgstr "" "Les utilisateurs n'ayant jamais adhéré seront supprimés après ce nombre de " "jours." -#: preferences/models.py:130 +#: preferences/models.py:127 msgid "" "Users with an email address not yet confirmed will be disabled after this " "number of days." @@ -356,11 +356,11 @@ msgstr "" "Les utilisateurs n'ayant pas confirmé leur addresse mail seront désactivés " "après ce nombre de jours" -#: preferences/models.py:134 +#: preferences/models.py:131 msgid "A new user can create their account on Re2o." msgstr "Un nouvel utilisateur peut créer son compte sur Re2o." -#: preferences/models.py:139 +#: preferences/models.py:136 msgid "" "If True, all new created and connected users are active. If False, only when " "a valid registration has been paid." @@ -368,7 +368,7 @@ msgstr "" "Si True, tous les nouveaux utilisations créés et connectés sont actifs. Si " "False, seulement quand une inscription validée a été payée." -#: preferences/models.py:146 +#: preferences/models.py:143 msgid "" "If True, users have the choice to receive an email containing a link to " "reset their password during creation, or to directly set their password in " @@ -379,172 +379,172 @@ msgstr "" "de choisir leur mot de passe immédiatement. Si False, un mail est toujours " "envoyé." -#: preferences/models.py:153 +#: preferences/models.py:150 msgid "If True, archived users are allowed to connect." msgstr "Si True, les utilisateurs archivés sont autorisés à se connecter." -#: preferences/models.py:157 +#: preferences/models.py:154 msgid "Can view the user preferences" msgstr "Peut voir les préférences d'utilisateur" -#: preferences/models.py:158 +#: preferences/models.py:155 msgid "user preferences" msgstr "Préférences d'utilisateur" -#: preferences/models.py:165 +#: preferences/models.py:162 msgid "Email domain must begin with @." msgstr "Un domaine mail doit commencer par @." -#: preferences/models.py:183 +#: preferences/models.py:180 msgid "Automatic configuration by RA" msgstr "Configuration automatique par RA" -#: preferences/models.py:184 +#: preferences/models.py:181 msgid "IP addresses assignment by DHCPv6" msgstr "Attribution d'adresses IP par DHCPv6" -#: preferences/models.py:185 +#: preferences/models.py:182 msgid "Disabled" msgstr "Désactivé" -#: preferences/models.py:194 +#: preferences/models.py:191 msgid "default Time To Live (TTL) for CNAME, A and AAAA records" msgstr "" "Temps de vie (TTL) par défault pour des enregistrements CNAME, A et AAAA" -#: preferences/models.py:204 +#: preferences/models.py:201 msgid "Can view the machine preferences" msgstr "Peut voir les préférences de machine" -#: preferences/models.py:205 +#: preferences/models.py:202 msgid "machine preferences" msgstr "Préférences de machine" -#: preferences/models.py:225 preferences/models.py:687 +#: preferences/models.py:222 preferences/models.py:684 msgid "On the IP range's VLAN of the machine" msgstr "Sur le VLAN de la plage d'IP de la machine" -#: preferences/models.py:226 preferences/models.py:688 +#: preferences/models.py:223 preferences/models.py:685 msgid "Preset in \"VLAN for machines accepted by RADIUS\"" msgstr "Prédéfinie dans « VLAN pour les machines acceptées par RADIUS »" -#: preferences/models.py:232 +#: preferences/models.py:229 msgid "Web management, activated in case of automatic provision." msgstr "Gestion web, activée en cas de provision automatique." -#: preferences/models.py:237 +#: preferences/models.py:234 msgid "" "SSL web management, make sure that a certificate is installed on the switch." msgstr "" "Gestion web SSL, vérifiez qu'un certificat est installé sur le commutateur " "réseau." -#: preferences/models.py:243 +#: preferences/models.py:240 msgid "REST management, activated in case of automatic provision." msgstr "Gestion REST, activée en cas de provision automatique." -#: preferences/models.py:250 +#: preferences/models.py:247 msgid "IP range for the management of switches." msgstr "Plage d'IP pour la gestion des commutateurs réseau." -#: preferences/models.py:256 +#: preferences/models.py:253 msgid "Provision of configuration mode for switches." msgstr "Mode de provision de configuration pour les commutateurs réseau." -#: preferences/models.py:259 +#: preferences/models.py:256 msgid "SFTP login for switches." msgstr "Identifiant SFTP pour les commutateurs réseau." -#: preferences/models.py:262 +#: preferences/models.py:259 msgid "SFTP password." msgstr "Mot de passe SFTP." -#: preferences/models.py:367 +#: preferences/models.py:364 msgid "Can view the topology preferences" msgstr "Peut voir les préférences de topologie" -#: preferences/models.py:369 +#: preferences/models.py:366 msgid "topology preferences" msgstr "préférences de topologie" -#: preferences/models.py:382 +#: preferences/models.py:379 msgid "RADIUS key." msgstr "Clé RADIUS." -#: preferences/models.py:384 +#: preferences/models.py:381 msgid "Comment for this key." msgstr "Commentaire pour cette clé." -#: preferences/models.py:387 +#: preferences/models.py:384 msgid "Default key for switches." msgstr "Clé par défaut pour les commutateurs réseau." -#: preferences/models.py:391 +#: preferences/models.py:388 msgid "Can view a RADIUS key object" msgstr "Peut voir un objet clé RADIUS" -#: preferences/models.py:392 preferences/views.py:335 +#: preferences/models.py:389 preferences/views.py:335 msgid "RADIUS key" msgstr "Clé RADIUS" -#: preferences/models.py:393 -#: preferences/templates/preferences/display_preferences.html:223 +#: preferences/models.py:390 +#: preferences/templates/preferences/display_preferences.html:221 msgid "RADIUS keys" msgstr "clés RADIUS" -#: preferences/models.py:400 +#: preferences/models.py:397 msgid "Default RADIUS key for switches already exists." msgstr "Clé par défaut pour les commutateurs réseau." -#: preferences/models.py:403 +#: preferences/models.py:400 msgid "RADIUS key " msgstr "clé RADIUS " -#: preferences/models.py:409 +#: preferences/models.py:406 msgid "Switch login." msgstr "Identifiant du commutateur réseau." -#: preferences/models.py:410 +#: preferences/models.py:407 msgid "Password." msgstr "Mot de passe." -#: preferences/models.py:412 +#: preferences/models.py:409 msgid "Default credentials for switches." msgstr "Identifiants par défaut pour les commutateurs réseau." -#: preferences/models.py:419 +#: preferences/models.py:416 msgid "Can view a switch management credentials object" msgstr "Peut voir un objet identifiants de gestion de commutateur réseau" -#: preferences/models.py:422 preferences/views.py:397 +#: preferences/models.py:419 preferences/views.py:397 msgid "switch management credentials" msgstr "identifiants de gestion de commutateur réseau" -#: preferences/models.py:425 +#: preferences/models.py:422 msgid "Switch login " msgstr "Identifiant du commutateur réseau " -#: preferences/models.py:437 +#: preferences/models.py:434 msgid "Delay between the email and the membership's end." msgstr "Délai entre le mail et la fin d'adhésion." -#: preferences/models.py:443 +#: preferences/models.py:440 msgid "Message displayed specifically for this reminder." msgstr "Message affiché spécifiquement pour ce rappel." -#: preferences/models.py:447 +#: preferences/models.py:444 msgid "Can view a reminder object" msgstr "Peut voir un objet rappel" -#: preferences/models.py:448 preferences/views.py:280 +#: preferences/models.py:445 preferences/views.py:280 msgid "reminder" msgstr "rappel" -#: preferences/models.py:449 +#: preferences/models.py:446 msgid "reminders" msgstr "rappels" -#: preferences/models.py:470 +#: preferences/models.py:467 msgid "" "General message displayed on the French version of the website (e.g. in case " "of maintenance)." @@ -552,7 +552,7 @@ msgstr "" "Message général affiché sur la version française du site (ex : en cas de " "maintenance)." -#: preferences/models.py:478 +#: preferences/models.py:475 msgid "" "General message displayed on the English version of the website (e.g. in " "case of maintenance)." @@ -560,75 +560,75 @@ msgstr "" "Message général affiché sur la version anglaise du site (ex : en cas de " "maintenance)." -#: preferences/models.py:493 +#: preferences/models.py:490 msgid "Can view the general preferences" msgstr "Peut voir les préférences générales" -#: preferences/models.py:494 +#: preferences/models.py:491 msgid "general preferences" msgstr "préférences générales" -#: preferences/models.py:514 +#: preferences/models.py:511 msgid "Can view the service preferences" msgstr "Peut voir les préférences de service" -#: preferences/models.py:515 preferences/views.py:231 +#: preferences/models.py:512 preferences/views.py:231 msgid "service" msgstr "service" -#: preferences/models.py:516 +#: preferences/models.py:513 msgid "services" msgstr "services" -#: preferences/models.py:526 +#: preferences/models.py:523 msgid "Contact email address." msgstr "Adresse mail de contact." -#: preferences/models.py:532 +#: preferences/models.py:529 msgid "Description of the associated email address." msgstr "Description de l'adresse mail associée." -#: preferences/models.py:542 +#: preferences/models.py:539 msgid "Can view a contact email address object" msgstr "Peut voir un objet adresse mail de contact" -#: preferences/models.py:544 +#: preferences/models.py:541 msgid "contact email address" msgstr "adresse mail de contact" -#: preferences/models.py:545 +#: preferences/models.py:542 msgid "contact email addresses" msgstr "adresses mail de contact" -#: preferences/models.py:553 preferences/views.py:635 +#: preferences/models.py:550 preferences/views.py:635 msgid "mandate" msgstr "mandat" -#: preferences/models.py:554 +#: preferences/models.py:551 msgid "mandates" msgstr "mandats" -#: preferences/models.py:555 +#: preferences/models.py:552 msgid "Can view a mandate object" msgstr "Peut voir un objet mandat" -#: preferences/models.py:562 +#: preferences/models.py:559 msgid "president of the association" msgstr "président de l'association" -#: preferences/models.py:563 +#: preferences/models.py:560 msgid "Displayed on subscription vouchers." msgstr "Affiché sur les reçus de cotisation." -#: preferences/models.py:565 +#: preferences/models.py:562 msgid "start date" msgstr "date de début" -#: preferences/models.py:566 +#: preferences/models.py:563 msgid "end date" msgstr "date de fin" -#: preferences/models.py:580 +#: preferences/models.py:577 msgid "" "No mandates have been created. Please go to the preferences page to create " "one." @@ -636,140 +636,140 @@ msgstr "" "Aucun mandat n'a été créé. Veuillez vous rendre sur la page de préférences " "pour en créer un." -#: preferences/models.py:596 +#: preferences/models.py:593 msgid "Networking organisation school Something" msgstr "Association de réseau de l'école Machin" -#: preferences/models.py:599 +#: preferences/models.py:596 msgid "Threadneedle Street" msgstr "1 rue de la Vrillière" -#: preferences/models.py:600 +#: preferences/models.py:597 msgid "London EC2R 8AH" msgstr "75001 Paris" -#: preferences/models.py:603 +#: preferences/models.py:600 msgid "Organisation" msgstr "Association" -#: preferences/models.py:610 +#: preferences/models.py:607 msgid "Can view the organisation preferences" msgstr "Peut voir les préférences d'association" -#: preferences/models.py:611 +#: preferences/models.py:608 msgid "organisation preferences" msgstr "préférences d'association" -#: preferences/models.py:629 +#: preferences/models.py:626 msgid "Can view the homepage preferences" msgstr "Peut voir les préférences de page d'accueil" -#: preferences/models.py:630 +#: preferences/models.py:627 msgid "homepage preferences" msgstr "Préférences de page d'accueil" -#: preferences/models.py:644 +#: preferences/models.py:641 msgid "Welcome email in French." msgstr "Mail de bienvenue en français." -#: preferences/models.py:647 +#: preferences/models.py:644 msgid "Welcome email in English." msgstr "Mail de bienvenue en anglais." -#: preferences/models.py:652 +#: preferences/models.py:649 msgid "Can view the email message preferences" msgstr "Peut voir les préférences de message pour les mails" -#: preferences/models.py:654 +#: preferences/models.py:651 msgid "email message preferences" msgstr "préférences de messages pour les mails" -#: preferences/models.py:659 +#: preferences/models.py:656 msgid "RADIUS attribute" msgstr "attribut RADIUS" -#: preferences/models.py:660 +#: preferences/models.py:657 msgid "RADIUS attributes" msgstr "attributs RADIUS" -#: preferences/models.py:664 preferences/views.py:588 +#: preferences/models.py:661 preferences/views.py:588 msgid "attribute" msgstr "attribut" -#: preferences/models.py:665 +#: preferences/models.py:662 msgid "See https://freeradius.org/rfc/attributes.html." msgstr "Voir https://freeradius.org/rfc/attributes.html." -#: preferences/models.py:667 +#: preferences/models.py:664 msgid "value" msgstr "valeur" -#: preferences/models.py:669 +#: preferences/models.py:666 msgid "comment" msgstr "commentaire" -#: preferences/models.py:670 +#: preferences/models.py:667 msgid "Use this field to document this attribute." msgstr "Utilisez ce champ pour documenter cet attribut." -#: preferences/models.py:681 +#: preferences/models.py:678 msgid "RADIUS policy" msgstr "politique de RADIUS" -#: preferences/models.py:682 -#: preferences/templates/preferences/display_preferences.html:301 +#: preferences/models.py:679 +#: preferences/templates/preferences/display_preferences.html:299 msgid "RADIUS policies" msgstr "politiques de RADIUS" -#: preferences/models.py:693 +#: preferences/models.py:690 msgid "Reject the machine" msgstr "Rejeter la machine" -#: preferences/models.py:694 +#: preferences/models.py:691 msgid "Place the machine on the VLAN" msgstr "Placer la machine sur le VLAN" -#: preferences/models.py:703 +#: preferences/models.py:700 msgid "policy for unknown machines" msgstr "politique pour les machines inconnues" -#: preferences/models.py:711 +#: preferences/models.py:708 msgid "unknown machines VLAN" msgstr "VLAN pour les machines inconnues" -#: preferences/models.py:712 +#: preferences/models.py:709 msgid "VLAN for unknown machines if not rejected." msgstr "VLAN pour les machines inconnues si non rejeté." -#: preferences/models.py:718 +#: preferences/models.py:715 msgid "unknown machines attributes" msgstr "attributs pour les machines inconnues" -#: preferences/models.py:719 +#: preferences/models.py:716 msgid "Answer attributes for unknown machines." msgstr "Attributs de réponse pour les machines inconnues." -#: preferences/models.py:725 +#: preferences/models.py:722 msgid "policy for unknown ports" msgstr "politique pour les ports inconnus" -#: preferences/models.py:733 +#: preferences/models.py:730 msgid "unknown ports VLAN" msgstr "VLAN pour les ports inconnus" -#: preferences/models.py:734 +#: preferences/models.py:731 msgid "VLAN for unknown ports if not rejected." msgstr "VLAN pour les ports inconnus si non rejeté." -#: preferences/models.py:740 +#: preferences/models.py:737 msgid "unknown ports attributes" msgstr "attributs pour les ports inconnus" -#: preferences/models.py:741 +#: preferences/models.py:738 msgid "Answer attributes for unknown ports." msgstr "Attributs de réponse pour les ports inconnus." -#: preferences/models.py:748 +#: preferences/models.py:745 msgid "" "Policy for machines connecting from unregistered rooms (relevant on ports " "with STRICT RADIUS mode)" @@ -777,87 +777,87 @@ msgstr "" "Politique pour les machines se connectant depuis des chambre non " "enregistrées (pertinent pour les ports avec le mode de RADIUS STRICT)" -#: preferences/models.py:758 +#: preferences/models.py:755 msgid "unknown rooms VLAN" msgstr "VLAN pour les chambres inconnues" -#: preferences/models.py:759 +#: preferences/models.py:756 msgid "VLAN for unknown rooms if not rejected." msgstr "VLAN pour les chambres inconnues si non rejeté." -#: preferences/models.py:765 +#: preferences/models.py:762 msgid "unknown rooms attributes" msgstr "attributs pour les chambres inconnues" -#: preferences/models.py:766 +#: preferences/models.py:763 msgid "Answer attributes for unknown rooms." msgstr "Attributs de réponse pour les chambres inconnues." -#: preferences/models.py:772 +#: preferences/models.py:769 msgid "policy for non members" msgstr "politique pour les non adhérents" -#: preferences/models.py:780 +#: preferences/models.py:777 msgid "non members VLAN" msgstr "VLAN pour les non adhérents" -#: preferences/models.py:781 +#: preferences/models.py:778 msgid "VLAN for non members if not rejected." msgstr "VLAN pour les non adhérents si non rejeté." -#: preferences/models.py:787 +#: preferences/models.py:784 msgid "non members attributes" msgstr "attributs pour les non adhérents" -#: preferences/models.py:788 +#: preferences/models.py:785 msgid "Answer attributes for non members." msgstr "Attributs de réponse pour les non adhérents." -#: preferences/models.py:794 +#: preferences/models.py:791 msgid "policy for banned users" msgstr "politique pour les utilisateurs bannis" -#: preferences/models.py:802 +#: preferences/models.py:799 msgid "banned users VLAN" msgstr "VLAN pour les utilisateurs bannis" -#: preferences/models.py:803 +#: preferences/models.py:800 msgid "VLAN for banned users if not rejected." msgstr "VLAN pour les utilisateurs bannis si non rejeté." -#: preferences/models.py:809 +#: preferences/models.py:806 msgid "banned users attributes" msgstr "attributs pour les utilisateurs bannis" -#: preferences/models.py:810 +#: preferences/models.py:807 msgid "Answer attributes for banned users." msgstr "Attributs de réponse pour les utilisateurs bannis." -#: preferences/models.py:823 +#: preferences/models.py:820 msgid "accepted users attributes" msgstr "attributs pour les utilisateurs acceptés" -#: preferences/models.py:824 +#: preferences/models.py:821 msgid "Answer attributes for accepted users." msgstr "Attributs de réponse pour les utilisateurs acceptés." -#: preferences/models.py:851 +#: preferences/models.py:848 msgid "subscription preferences" msgstr "préférences de cotisation" -#: preferences/models.py:855 +#: preferences/models.py:852 msgid "template for invoices" msgstr "modèle pour les factures" -#: preferences/models.py:862 +#: preferences/models.py:859 msgid "template for subscription vouchers" msgstr "modèle pour les reçus de cotisation" -#: preferences/models.py:868 +#: preferences/models.py:865 msgid "send voucher by email when the invoice is controlled" msgstr "envoyer le reçu par mail quand la facture est contrôlée" -#: preferences/models.py:870 +#: preferences/models.py:867 msgid "" "Be careful, if no mandate is defined on the preferences page, errors will be " "triggered when generating vouchers." @@ -865,19 +865,19 @@ msgstr "" "Faites attention, si aucun mandat n'est défini sur la page de préférences, " "des erreurs seront déclenchées en générant des reçus." -#: preferences/models.py:882 +#: preferences/models.py:879 msgid "template" msgstr "modèle" -#: preferences/models.py:883 +#: preferences/models.py:880 msgid "name" msgstr "nom" -#: preferences/models.py:886 +#: preferences/models.py:883 msgid "document template" msgstr "modèle de document" -#: preferences/models.py:887 +#: preferences/models.py:884 msgid "document templates" msgstr "modèles de document" diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index 92498917..942bab4a 100644 --- a/re2o/locale/fr/LC_MESSAGES/django.po +++ b/re2o/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index 0c644ed6..d9828af5 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2018-06-24 20:10+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/templates/buttons/history.html b/templates/buttons/history.html index 730023e8..f8eed813 100644 --- a/templates/buttons/history.html +++ b/templates/buttons/history.html @@ -23,7 +23,14 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load i18n %} + +{% if name == "user" %} + + {% if text %}{% trans "History" %}{% endif %} + +{% else %} {% if text %}{% trans "History" %}{% endif %} +{% endif %} diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index 89d40143..5dae6c1d 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index 415c618f..4ac73acc 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 03:10+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -30,65 +30,65 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: tickets/forms.py:76 +#: tickets/forms.py:78 msgid "comment" msgstr "commentaire" -#: tickets/models.py:57 +#: tickets/models.py:58 msgid "Title of the ticket." msgstr "Titre du ticket." -#: tickets/models.py:66 +#: tickets/models.py:67 msgid "An email address to get back to you." msgstr "Une adresse mail pour vous recontacter." -#: tickets/models.py:70 +#: tickets/models.py:71 msgid "Language of the ticket." msgstr "Langue des tickets" -#: tickets/models.py:74 tickets/models.py:170 +#: tickets/models.py:76 tickets/models.py:173 msgid "Can view a ticket object" msgstr "Peut voir un objet ticket" -#: tickets/models.py:75 tickets/models.py:171 +#: tickets/models.py:77 tickets/models.py:174 msgid "ticket" msgstr "ticket" -#: tickets/models.py:76 tickets/models.py:172 +#: tickets/models.py:78 tickets/models.py:175 msgid "tickets" msgstr "tickets" -#: tickets/models.py:80 +#: tickets/models.py:82 #, python-format msgid "Ticket from %(name)s. Date: %(date)s." msgstr "Ticket de %(name)s. Date : %(date)s." -#: tickets/models.py:82 +#: tickets/models.py:84 #, python-format msgid "Anonymous ticket. Date: %s." msgstr "Ticket anonyme. Date : %s." -#: tickets/models.py:90 tickets/templates/tickets/aff_ticket.html:52 +#: tickets/models.py:92 tickets/templates/tickets/aff_ticket.html:52 msgid "Anonymous user" msgstr "Utilisateur anonyme" -#: tickets/models.py:128 +#: tickets/models.py:130 msgid "You don't have the right to view other tickets than yours." msgstr "Vous n'avez pas le droit de voir d'autres tickets que les vôtres." -#: tickets/models.py:140 tickets/models.py:214 +#: tickets/models.py:142 tickets/models.py:217 msgid "You don't have the right to view the list of tickets." msgstr "Vous n'avez pas le droit de voir la liste des tickets." -#: tickets/models.py:187 +#: tickets/models.py:190 msgid "You don't have the right to view other tickets comments than yours." msgstr "Vous n'avez pas le droit de voir d'autres tickets que les vôtres." -#: tickets/models.py:202 +#: tickets/models.py:205 msgid "You don't have the right to edit other tickets comments than yours." msgstr "Vous n'avez pas le droit d'éditer d'autres tickets que les vôtres." -#: tickets/models.py:232 +#: tickets/models.py:236 msgid "Update of your ticket" msgstr "Mise à jour de votre ticket" diff --git a/topologie/locale/fr/LC_MESSAGES/django.po b/topologie/locale/fr/LC_MESSAGES/django.po index a637304c..53a55c1a 100644 --- a/topologie/locale/fr/LC_MESSAGES/django.po +++ b/topologie/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2018-06-25 14:53+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 6ce34b36..fce3163a 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2018-06-27 23:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n"