diff --git a/api/locale/fr/LC_MESSAGES/django.po b/api/locale/fr/LC_MESSAGES/django.po index 8040325f..3c08e4e5 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-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+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 ef9b3fa5..aca2607b 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-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+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/forms.py b/logs/forms.py new file mode 100644 index 00000000..3182ef17 --- /dev/null +++ b/logs/forms.py @@ -0,0 +1,55 @@ +# 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. + +"""The forms used by the search app""" + +from django import forms +from django.forms import Form +from django.utils.translation import ugettext_lazy as _ +from re2o.base import get_input_formats_help_text + +CHOICES_TYPE = ( + ("ip", _("IPv4")), + ("mac", _("MAC address")), +) + + +class MachineHistoryForm(Form): + """The form for a simple search""" + + q = forms.CharField( + label=_("Search"), + max_length=100, + ) + t = forms.CharField( + label=_("Search type"), + widget=forms.Select(choices=CHOICES_TYPE) + ) + s = forms.DateField(required=False, label=_("Start date")) + e = forms.DateField(required=False, label=_("End date")) + + def __init__(self, *args, **kwargs): + super(MachineHistoryForm, self).__init__(*args, **kwargs) + self.fields["s"].help_text = get_input_formats_help_text( + self.fields["s"].input_formats + ) + self.fields["e"].help_text = get_input_formats_help_text( + self.fields["e"].input_formats + ) diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index 88f515fd..bb8dc944 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-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+0200\n" "PO-Revision-Date: 2018-06-23 16:01+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -34,6 +34,30 @@ msgstr "" msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." +#: logs/forms.py:29 logs/templates/logs/machine_history.html:35 +msgid "IPv4" +msgstr "IPv4" + +#: logs/forms.py:30 logs/templates/logs/machine_history.html:36 +msgid "MAC address" +msgstr "Adresse MAC" + +#: logs/forms.py:38 logs/templates/logs/search_machine_history.html:38 +msgid "Search" +msgstr "Rechercher" + +#: logs/forms.py:42 +msgid "Search type" +msgstr "Type de recherche" + +#: logs/forms.py:45 logs/templates/logs/machine_history.html:37 +msgid "Start date" +msgstr "Date de début" + +#: logs/forms.py:46 logs/templates/logs/machine_history.html:38 +msgid "End date" +msgstr "Date de fin" + #: logs/templates/logs/aff_stats_logs.html:36 msgid "Edited object" msgstr "Objet modifié" @@ -52,6 +76,7 @@ msgid "Date of editing" msgstr "Date de modification" #: logs/templates/logs/aff_stats_logs.html:42 +#: logs/templates/logs/machine_history.html:39 msgid "Comment" msgstr "Commentaire" @@ -159,10 +184,35 @@ msgid "Statistics" msgstr "Statistiques" #: logs/templates/logs/index.html:32 logs/templates/logs/stats_logs.html:32 -#: logs/views.py:418 +#: logs/views.py:421 msgid "Actions performed" msgstr "Actions effectuées" +#: logs/templates/logs/machine_history.html:27 +msgid "Search results" +msgstr "Résultats de la recherche" + +#: logs/templates/logs/machine_history.html:34 +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 +msgid "No result" +msgstr "Aucun résultat" + +#: logs/templates/logs/search_machine_history.html:27 +#: logs/templates/logs/search_machine_history.html:32 +msgid "Search machine history" +msgstr "Rechercher l'historique des machines" + #: logs/templates/logs/sidebar.html:33 msgid "Summary" msgstr "Résumé" @@ -187,6 +237,10 @@ msgstr "Actions de câblage" msgid "Users" msgstr "Utilisateurs" +#: logs/templates/logs/sidebar.html:57 +msgid "Machine history" +msgstr "Historique des machines" + #: logs/templates/logs/stats_general.html:32 msgid "General statistics" msgstr "Statistiques générales" @@ -199,138 +253,138 @@ msgstr "Statistiques sur la base de données" msgid "Statistics about users" msgstr "Statistiques sur les utilisateurs" -#: logs/views.py:175 +#: logs/views.py:178 msgid "Nonexistent revision." msgstr "Révision inexistante." -#: logs/views.py:178 +#: logs/views.py:181 msgid "The action was deleted." msgstr "L'action a été supprimée." -#: logs/views.py:219 +#: logs/views.py:222 msgid "Category" msgstr "Catégorie" -#: logs/views.py:220 +#: logs/views.py:223 msgid "Number of users (members and clubs)" msgstr "Nombre d'utilisateurs (adhérents et clubs)" -#: logs/views.py:221 +#: logs/views.py:224 msgid "Number of members" msgstr "Nombre d'adhérents" -#: logs/views.py:222 +#: logs/views.py:225 msgid "Number of clubs" msgstr "Nombre de clubs" -#: logs/views.py:226 +#: logs/views.py:229 msgid "Activated users" msgstr "Utilisateurs activés" -#: logs/views.py:232 +#: logs/views.py:235 msgid "Disabled users" msgstr "Utilisateurs désactivés" -#: logs/views.py:238 +#: logs/views.py:241 msgid "Archived users" msgstr "Utilisateurs archivés" -#: logs/views.py:244 +#: logs/views.py:247 msgid "Fully archived users" msgstr "Utilisateurs complètement archivés" -#: logs/views.py:254 +#: logs/views.py:257 msgid "Not yet active users" msgstr "Utilisateurs pas encore actifs" -#: logs/views.py:264 +#: logs/views.py:267 msgid "Contributing members" msgstr "Adhérents cotisants" -#: logs/views.py:270 +#: logs/views.py:273 msgid "Users benefiting from a connection" msgstr "Utilisateurs bénéficiant d'une connexion" -#: logs/views.py:276 +#: logs/views.py:279 msgid "Banned users" msgstr "Utilisateurs bannis" -#: logs/views.py:282 +#: logs/views.py:285 msgid "Users benefiting from a free connection" msgstr "Utilisateurs bénéficiant d'une connexion gratuite" -#: logs/views.py:288 +#: logs/views.py:291 msgid "Users with a confirmed email" msgstr "Utilisateurs ayant un mail confirmé" -#: logs/views.py:294 +#: logs/views.py:297 msgid "Users with an unconfirmed email" msgstr "Utilisateurs ayant un mail non confirmé" -#: logs/views.py:300 +#: logs/views.py:303 msgid "Users pending email confirmation" msgstr "Utilisateurs en attente de confirmation du mail" -#: logs/views.py:306 +#: logs/views.py:309 msgid "Active interfaces (with access to the network)" msgstr "Interfaces actives (ayant accès au réseau)" -#: logs/views.py:320 +#: logs/views.py:323 msgid "Active interfaces assigned IPv4" msgstr "Interfaces actives assignées IPv4" -#: logs/views.py:337 +#: logs/views.py:340 msgid "IP range" msgstr "Plage d'IP" -#: logs/views.py:338 +#: logs/views.py:341 msgid "VLAN" msgstr "VLAN" -#: logs/views.py:339 +#: logs/views.py:342 msgid "Total number of IP addresses" msgstr "Nombre total d'adresses IP" -#: logs/views.py:340 +#: logs/views.py:343 msgid "Number of assigned IP addresses" msgstr "Nombre d'adresses IP assignées" -#: logs/views.py:341 +#: logs/views.py:344 msgid "Number of IP address assigned to an activated machine" msgstr "Nombre d'adresses IP assignées à une machine activée" -#: logs/views.py:342 +#: logs/views.py:345 msgid "Number of unassigned IP addresses" msgstr "Nombre d'adresses IP non assignées" -#: logs/views.py:357 +#: logs/views.py:360 msgid "Users (members and clubs)" msgstr "Utilisateurs (adhérents et clubs)" -#: logs/views.py:403 +#: logs/views.py:406 msgid "Topology" msgstr "Topologie" -#: logs/views.py:419 +#: logs/views.py:422 msgid "Number of actions" msgstr "Nombre d'actions" -#: logs/views.py:444 +#: logs/views.py:447 msgid "rights" msgstr "droits" -#: logs/views.py:473 +#: logs/views.py:476 msgid "actions" msgstr "actions" -#: logs/views.py:504 +#: logs/views.py:529 msgid "No model found." msgstr "Aucun modèle trouvé." -#: logs/views.py:510 +#: logs/views.py:535 msgid "Nonexistent entry." msgstr "Entrée inexistante." -#: logs/views.py:517 +#: logs/views.py:542 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 new file mode 100644 index 00000000..343a33cb --- /dev/null +++ b/logs/models.py @@ -0,0 +1,207 @@ +# -*- mode: python; coding: utf-8 -*- +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 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. +"""logs.models +The models definitions for the logs app +""" +from reversion.models import Version + +from machines.models import IpList +from machines.models import Interface +from machines.models import Machine +from users.models import User + + +class HistoryEvent: + def __init__(self, user, machine, interface, start=None, end=None): + """ + :param user: User, The user owning the maching at the time of the event + :param machine: Version, the machine version related to the interface + :param interface: Version, the interface targeted by this event + :param start: datetime, the date at which this version was created + :param end: datetime, the date at which this version was replace by a new one + """ + self.user = user + self.machine = machine + self.interface = interface + self.ipv4 = IpList.objects.get(id=interface.field_dict["ipv4_id"]).ipv4 + self.mac = self.interface.field_dict["mac_address"] + self.start_date = start + self.end_date = end + self.comment = interface.revision.get_comment() or None + + def is_similar(self, elt2): + """ + Checks whether two events are similar enough to be merged + :return: bool + """ + return ( + elt2 is not None + and self.user.id == elt2.user.id + and self.ipv4 == elt2.ipv4 + and self.machine.field_dict["id"] == elt2.machine.field_dict["id"] + and self.interface.field_dict["id"] == elt2.interface.field_dict["id"] + ) + + def __repr__(self): + return "{} ({} - ): from {} to {} ({})".format( + self.machine, + self.mac, + self.ipv4, + self.start_date, + self.end_date, + self.comment or "No comment" + ) + + +class MachineHistory: + def __init__(self): + self.events = [] + self.__last_evt = None + + def get(self, search, params): + """ + :param search: ip or mac to lookup + :param params: dict built by the search view + :return: list or None, a list of HistoryEvent + """ + self.start = params.get("s", None) + self.end = params.get("e", None) + search_type = params.get("t", 0) + + self.events = [] + if search_type == "ip": + return self.__get_by_ip(search) + elif search_type == "mac": + return self.__get_by_mac(search) + + return None + + def __add_revision(self, user, machine, interface): + """ + Add a new revision to the chronological order + :param user: User, The user owning the maching at the time of the event + :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.start_date = interface.revision.date_created + + # Try not to recreate events if it's unnecessary + if evt.is_similar(self.__last_evt): + return + + # Mark the end of validity of the last element + if self.__last_evt and not self.__last_evt.end_date: + self.__last_evt.end_date = evt.start_date + + # If the event ends before the given date, remove it + if self.start and evt.start_date.date() < self.start: + self.__last_evt = None + self.events.pop() + + # Make sure the new event starts before the given end date + if self.end and evt.start_date.date() > self.end: + return + + # Save the new element + self.events.append(evt) + self.__last_evt = evt + + def __get_interfaces_for_ip(self, ip): + """ + :param ip: str + :return: An iterable object with the Version objects + of Interfaces with the given IP + """ + # TODO: What if ip list was deleted? + try: + ip_id = IpList.objects.get(ipv4=ip).id + except IpList.DoesNotExist: + return [] + + return filter( + lambda x: x.field_dict["ipv4_id"] == ip_id, + Version.objects.get_for_model(Interface).order_by("revision__date_created") + ) + + def __get_interfaces_for_mac(self, mac): + """ + :param mac: str + :return: An iterable object with the Version objects + of Interfaces with the given MAC address + """ + return filter( + lambda x: str(x.field_dict["mac_address"]) == mac, + Version.objects.get_for_model(Interface).order_by("revision__date_created") + ) + + def __get_machines_for_interface(self, interface): + """ + :param interface: Version, the interface for which to find the machines + :return: An iterable object with the Version objects of Machine to + which the given interface was attributed + """ + machine_id = interface.field_dict["machine_id"] + return filter( + lambda x: x.field_dict["id"] == machine_id, + Version.objects.get_for_model(Machine).order_by("revision__date_created") + ) + + def __get_user_for_machine(self, machine): + """ + :param machine: Version, the machine of which the owner must be found + :return: The user to which the given machine belongs + """ + # TODO: What if user was deleted? + user_id = machine.field_dict["user_id"] + return User.objects.get(id=user_id) + + def __get_by_ip(self, ip): + """ + :param ip: str, The IP to lookup + :returns: list, a list of HistoryEvent + """ + interfaces = self.__get_interfaces_for_ip(ip) + + for interface in interfaces: + machines = self.__get_machines_for_interface(interface) + + for machine in machines: + user = self.__get_user_for_machine(machine) + self.__add_revision(user, machine, interface) + + return self.events + + def __get_by_mac(self, mac): + """ + :param mac: str, The MAC address to lookup + :returns: list, a list of HistoryEvent + """ + interfaces = self.__get_interfaces_for_mac(mac) + + for interface in interfaces: + machines = self.__get_machines_for_interface(interface) + + for machine in machines: + user = self.__get_user_for_machine(machine) + self.__add_revision(user, machine, interface) + + return self.events diff --git a/logs/templates/logs/machine_history.html b/logs/templates/logs/machine_history.html new file mode 100644 index 00000000..5aea5e9d --- /dev/null +++ b/logs/templates/logs/machine_history.html @@ -0,0 +1,76 @@ +{% 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 "Search results" %}{% endblock %} + +{% block content %} + {% if events %} + + + + + + + + + + + + {% for event in events %} + + + + + + + + + {% endfor %} +
{% trans "User" %}{% trans "IPv4" %}{% trans "MAC address" %}{% trans "Start date" %}{% trans "End date" %}{% trans "Comment" %}
+ + {{ event.user }} + + {{ event.ipv4 }}{{ event.mac }} + {% if event.start_date %} + {{ event.start_date }} + {% else %} + {% trans "Unknown" %} + {% endif %} + + {% if event.end_date %} + {{ event.end_date }} + {% else %} + {% trans "Now" %} + {% endif %} + {{ event.comment }}
+ {% include 'pagination.html' with list=events %} + {% else %} +

{% trans "No result" %}

+ {% endif %} +
+
+
+{% endblock %} diff --git a/logs/templates/logs/search_machine_history.html b/logs/templates/logs/search_machine_history.html new file mode 100644 index 00000000..07c9f68c --- /dev/null +++ b/logs/templates/logs/search_machine_history.html @@ -0,0 +1,46 @@ +{% 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 "Search machine history" %}{% endblock %} + +{% block content %} + +
+

{% trans "Search machine history" %}

+ + {% bootstrap_field history_form.q %} + {% bootstrap_field history_form.t %} + {% bootstrap_field history_form.s %} + {% bootstrap_field history_form.e %} + {% trans "Search" as tr_search %} + {% bootstrap_button tr_search button_type="submit" icon="search" %} +
+
+
+
+
+
+{% endblock %} diff --git a/logs/templates/logs/sidebar.html b/logs/templates/logs/sidebar.html index 7f7d6cbf..a44da9e3 100644 --- a/logs/templates/logs/sidebar.html +++ b/logs/templates/logs/sidebar.html @@ -52,6 +52,10 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Users" %} + + + {% trans "Machine history" %} + {% acl_end %} {% endblock %} diff --git a/logs/urls.py b/logs/urls.py index 8fa0f469..eced2a83 100644 --- a/logs/urls.py +++ b/logs/urls.py @@ -46,4 +46,5 @@ urlpatterns = [ views.history, name="history", ), + url(r"^stats_search_machine/$", views.stats_search_machine_history, name="stats-search-machine"), ] diff --git a/logs/views.py b/logs/views.py index 78971d18..85ba35cf 100644 --- a/logs/views.py +++ b/logs/views.py @@ -101,6 +101,9 @@ from re2o.utils import ( from re2o.base import re2o_paginator, SortTable from re2o.acl import can_view_all, can_view_app, can_edit_history +from .models import MachineHistory +from .forms import MachineHistoryForm + @login_required @can_view_app("logs") @@ -478,6 +481,33 @@ def stats_actions(request): return render(request, "logs/stats_users.html", {"stats_list": stats}) +@login_required +@can_view_app("users") +def stats_search_machine_history(request): + """View which displays the history of machines with the given + une IP or MAC adresse""" + history_form = MachineHistoryForm(request.GET or None) + if history_form.is_valid(): + history = MachineHistory() + events = history.get( + history_form.cleaned_data.get("q", ""), + history_form.cleaned_data + ) + max_result = GeneralOption.get_cached_value("pagination_number") + events = re2o_paginator( + request, + events, + max_result + ) + + return render( + request, + "logs/machine_history.html", + { "events": events }, + ) + return render(request, "logs/search_machine_history.html", {"history_form": history_form}) + + 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 e5f8dfc9..f1726d25 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-21 22:29+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+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 e60cb464..14c22e92 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-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+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 e90c1e2a..c655e7a3 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-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+0200\n" "PO-Revision-Date: 2018-06-24 15:54+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index 84b4bd34..92498917 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-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+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 2e18a0a2..0c644ed6 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-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+0200\n" "PO-Revision-Date: 2018-06-24 20:10+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index ad8e9968..89d40143 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-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+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 df651ec7..fbb7cbc2 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-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+0200\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/topologie/locale/fr/LC_MESSAGES/django.po b/topologie/locale/fr/LC_MESSAGES/django.po index b5f45bff..a637304c 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-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+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 edae2c02..6ce34b36 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-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+0200\n" "PO-Revision-Date: 2018-06-27 23:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -492,8 +492,7 @@ msgstr "Le champ mail ne peut pas ^êêtre vide" #: users/models.py:1344 msgid "You can't use a {} address as an external contact address." -msgstr "" -"Vous ne pouvez pas utiliser une adresse {} pour votre adresse externe." +msgstr "Vous ne pouvez pas utiliser une adresse {} pour votre adresse externe." #: users/models.py:1371 msgid "member"