mirror of
https://github.com/ae-utbm/sith.git
synced 2024-11-22 22:23:23 +00:00
core: add family graphs
Signed-off-by: Skia <skia@libskia.so>
This commit is contained in:
parent
47bace2057
commit
7879b6dd6b
@ -6,6 +6,8 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<p><a href="{{ url("core:user_godfathers_tree_pict", user_id=profile.id) }}?family">
|
||||||
|
{% trans %}Show family picture{% endtrans %}</a></p>
|
||||||
{% if profile.godfathers.exists() %}
|
{% if profile.godfathers.exists() %}
|
||||||
<h4>{% trans %}Godfathers{% endtrans %}</h4>
|
<h4>{% trans %}Godfathers{% endtrans %}</h4>
|
||||||
<ul>
|
<ul>
|
||||||
@ -14,6 +16,8 @@
|
|||||||
{{ u.get_mini_item()|safe }} </a>{{ delete_godfather(user, profile, u, True) }}</li>
|
{{ u.get_mini_item()|safe }} </a>{{ delete_godfather(user, profile, u, True) }}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
<p><a href="{{ url("core:user_godfathers_tree", user_id=profile.id) }}">
|
||||||
|
{% trans %}Show ancestors tree{% endtrans %}</a></p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>{% trans %}No godfathers{% endtrans %}
|
<p>{% trans %}No godfathers{% endtrans %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -25,6 +29,8 @@
|
|||||||
{{ u.get_mini_item()|safe }} </a>{{ delete_godfather(user, profile, u, False) }}</li>
|
{{ u.get_mini_item()|safe }} </a>{{ delete_godfather(user, profile, u, False) }}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
<p><a href="{{ url("core:user_godfathers_tree", user_id=profile.id) }}?descent">
|
||||||
|
{% trans %}Show descent tree{% endtrans %}</a></p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>{% trans %}No godchildren{% endtrans %}
|
<p>{% trans %}No godchildren{% endtrans %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
54
core/templates/core/user_godfathers_tree.jinja
Normal file
54
core/templates/core/user_godfathers_tree.jinja
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
{% extends "core/base.jinja" %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
{% if param == "godchildren" %}
|
||||||
|
{% trans user_name=profile.get_display_name() %}{{ user_name }}'s godchildren{% endtrans %}
|
||||||
|
{% else %}
|
||||||
|
{% trans user_name=profile.get_display_name() %}{{ user_name }}'s godfathers{% endtrans %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% macro display_members_list(user) %}
|
||||||
|
{% if user.__getattribute__(param).exists() %}
|
||||||
|
<ul>
|
||||||
|
{% for u in user.__getattribute__(param).all() %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ url("core:user_godfathers", user_id=u.id) }}">
|
||||||
|
{{ u.get_short_name() }}
|
||||||
|
</a>
|
||||||
|
{% if u in members_set %}
|
||||||
|
{% trans %}Already seen (check above){% endtrans %}
|
||||||
|
{% else %}
|
||||||
|
{{ members_set.add(u) or "" }}
|
||||||
|
{{ display_members_list(u) }}
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<p><a href="{{ url("core:user_godfathers", user_id=profile.id) }}">
|
||||||
|
{% trans %}Back to family{% endtrans %}</a></p>
|
||||||
|
{% if profile.__getattribute__(param).exists() %}
|
||||||
|
{% if param == "godchildren" %}
|
||||||
|
<p><a href="{{ url("core:user_godfathers_tree_pict", user_id=profile.id) }}?descent">
|
||||||
|
{% trans %}Show a picture of the tree{% endtrans %}</a></p>
|
||||||
|
<h4>{% trans u=profile.get_short_name() %}Descent tree of {{ u }}{% endtrans %}</h4>
|
||||||
|
{% else %}
|
||||||
|
<p><a href="{{ url("core:user_godfathers_tree_pict", user_id=profile.id) }}?ancestors">
|
||||||
|
{% trans %}Show a picture of the tree{% endtrans %}</a></p>
|
||||||
|
<h4>{% trans u=profile.get_short_name() %}Ancestors tree of {{ u }}{% endtrans %}</h4>
|
||||||
|
{% endif %}
|
||||||
|
{{ members_set.add(profile) or "" }}
|
||||||
|
{{ display_members_list(profile) }}
|
||||||
|
{% else %}
|
||||||
|
{% if param == "godchildren" %}
|
||||||
|
<p>{% trans %}No godchildren{% endtrans %}
|
||||||
|
{% else %}
|
||||||
|
<p>{% trans %}No godfathers{% endtrans %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -61,6 +61,8 @@ urlpatterns = [
|
|||||||
url(r'^user/(?P<user_id>[0-9]+)/$', UserView.as_view(), name='user_profile'),
|
url(r'^user/(?P<user_id>[0-9]+)/$', UserView.as_view(), name='user_profile'),
|
||||||
url(r'^user/(?P<user_id>[0-9]+)/pictures$', UserPicturesView.as_view(), name='user_pictures'),
|
url(r'^user/(?P<user_id>[0-9]+)/pictures$', UserPicturesView.as_view(), name='user_pictures'),
|
||||||
url(r'^user/(?P<user_id>[0-9]+)/godfathers$', UserGodfathersView.as_view(), name='user_godfathers'),
|
url(r'^user/(?P<user_id>[0-9]+)/godfathers$', UserGodfathersView.as_view(), name='user_godfathers'),
|
||||||
|
url(r'^user/(?P<user_id>[0-9]+)/godfathers/tree$', UserGodfathersTreeView.as_view(), name='user_godfathers_tree'),
|
||||||
|
url(r'^user/(?P<user_id>[0-9]+)/godfathers/tree/pict$', UserGodfathersTreePictureView.as_view(), name='user_godfathers_tree_pict'),
|
||||||
url(r'^user/(?P<user_id>[0-9]+)/godfathers/(?P<godfather_id>[0-9]+)/(?P<is_father>(True)|(False))/delete$', DeleteUserGodfathers, name='user_godfathers_delete'),
|
url(r'^user/(?P<user_id>[0-9]+)/godfathers/(?P<godfather_id>[0-9]+)/(?P<is_father>(True)|(False))/delete$', DeleteUserGodfathers, name='user_godfathers_delete'),
|
||||||
url(r'^user/(?P<user_id>[0-9]+)/edit$', UserUpdateProfileView.as_view(), name='user_edit'),
|
url(r'^user/(?P<user_id>[0-9]+)/edit$', UserUpdateProfileView.as_view(), name='user_edit'),
|
||||||
url(r'^user/(?P<user_id>[0-9]+)/profile_upload$', UserUploadProfilePictView.as_view(), name='user_profile_upload'),
|
url(r'^user/(?P<user_id>[0-9]+)/profile_upload$', UserUploadProfilePictView.as_view(), name='user_profile_upload'),
|
||||||
|
@ -28,7 +28,7 @@ from django.contrib.auth import views
|
|||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.core.exceptions import PermissionDenied, ValidationError
|
from django.core.exceptions import PermissionDenied, ValidationError
|
||||||
from django.http import Http404
|
from django.http import Http404, HttpResponse
|
||||||
from django.views.generic.edit import UpdateView
|
from django.views.generic.edit import UpdateView
|
||||||
from django.views.generic import ListView, DetailView, TemplateView
|
from django.views.generic import ListView, DetailView, TemplateView
|
||||||
from django.forms.models import modelform_factory
|
from django.forms.models import modelform_factory
|
||||||
@ -233,20 +233,6 @@ class UserView(UserTabsMixin, CanViewMixin, DetailView):
|
|||||||
current_tab = 'infos'
|
current_tab = 'infos'
|
||||||
|
|
||||||
|
|
||||||
def DeleteUserGodfathers(request, user_id, godfather_id, is_father):
|
|
||||||
user = User.objects.get(id=user_id)
|
|
||||||
if ((user == request.user) or
|
|
||||||
request.user.is_root or
|
|
||||||
request.user.is_board_member):
|
|
||||||
ud = get_object_or_404(User, id=godfather_id)
|
|
||||||
if is_father == "True":
|
|
||||||
user.godfathers.remove(ud)
|
|
||||||
else:
|
|
||||||
user.godchildren.remove(ud)
|
|
||||||
else:
|
|
||||||
raise PermissionDenied
|
|
||||||
return redirect('core:user_godfathers', user_id=user_id)
|
|
||||||
|
|
||||||
|
|
||||||
class UserPicturesView(UserTabsMixin, CanViewMixin, DetailView):
|
class UserPicturesView(UserTabsMixin, CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
@ -274,6 +260,20 @@ class UserPicturesView(UserTabsMixin, CanViewMixin, DetailView):
|
|||||||
kwargs['pictures'][album.id].append(pict_relation.picture)
|
kwargs['pictures'][album.id].append(pict_relation.picture)
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
def DeleteUserGodfathers(request, user_id, godfather_id, is_father):
|
||||||
|
user = User.objects.get(id=user_id)
|
||||||
|
if ((user == request.user) or
|
||||||
|
request.user.is_root or
|
||||||
|
request.user.is_board_member):
|
||||||
|
ud = get_object_or_404(User, id=godfather_id)
|
||||||
|
if is_father == "True":
|
||||||
|
user.godfathers.remove(ud)
|
||||||
|
else:
|
||||||
|
user.godchildren.remove(ud)
|
||||||
|
else:
|
||||||
|
raise PermissionDenied
|
||||||
|
return redirect('core:user_godfathers', user_id=user_id)
|
||||||
|
|
||||||
class UserGodfathersView(UserTabsMixin, CanViewMixin, DetailView):
|
class UserGodfathersView(UserTabsMixin, CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Display a user's godfathers
|
Display a user's godfathers
|
||||||
@ -305,6 +305,91 @@ class UserGodfathersView(UserTabsMixin, CanViewMixin, DetailView):
|
|||||||
kwargs['form'] = UserGodfathersForm()
|
kwargs['form'] = UserGodfathersForm()
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
class UserGodfathersTreeView(UserTabsMixin, CanViewMixin, DetailView):
|
||||||
|
"""
|
||||||
|
Display a user's family tree
|
||||||
|
"""
|
||||||
|
model = User
|
||||||
|
pk_url_kwarg = "user_id"
|
||||||
|
context_object_name = "profile"
|
||||||
|
template_name = "core/user_godfathers_tree.jinja"
|
||||||
|
current_tab = 'godfathers'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
kwargs = super(UserGodfathersTreeView, self).get_context_data(**kwargs)
|
||||||
|
if "descent" in self.request.GET:
|
||||||
|
kwargs['param'] = "godchildren"
|
||||||
|
else:
|
||||||
|
kwargs['param'] = "godfathers"
|
||||||
|
kwargs['members_set'] = set()
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
class UserGodfathersTreePictureView(CanViewMixin, DetailView):
|
||||||
|
"""
|
||||||
|
Display a user's tree as a picture
|
||||||
|
"""
|
||||||
|
model = User
|
||||||
|
pk_url_kwarg = "user_id"
|
||||||
|
|
||||||
|
def build_complex_graph(self):
|
||||||
|
import pygraphviz as pgv
|
||||||
|
self.depth = int(self.request.GET.get("depth", 4))
|
||||||
|
if self.param == "godfathers":
|
||||||
|
self.graph = pgv.AGraph(strict=False, directed=True, rankdir="BT")
|
||||||
|
else:
|
||||||
|
self.graph = pgv.AGraph(strict=False, directed=True)
|
||||||
|
family = set()
|
||||||
|
self.level = 1
|
||||||
|
# Since the tree isn't very deep, we can build it recursively
|
||||||
|
def crawl_family(user):
|
||||||
|
if self.level > self.depth: return
|
||||||
|
self.level += 1
|
||||||
|
for u in user.__getattribute__(self.param).all():
|
||||||
|
self.graph.add_edge(user.get_short_name(), u.get_short_name())
|
||||||
|
if u not in family:
|
||||||
|
family.add(u)
|
||||||
|
crawl_family(u)
|
||||||
|
self.level -= 1
|
||||||
|
self.graph.add_node(self.object.get_short_name())
|
||||||
|
family.add(self.object)
|
||||||
|
crawl_family(self.object)
|
||||||
|
|
||||||
|
def build_family_graph(self):
|
||||||
|
import pygraphviz as pgv
|
||||||
|
self.graph = pgv.AGraph(strict=False, directed=True)
|
||||||
|
self.graph.add_node(self.object.get_short_name())
|
||||||
|
for u in self.object.godfathers.all():
|
||||||
|
self.graph.add_edge(u.get_short_name(), self.object.get_short_name())
|
||||||
|
for u in self.object.godchildren.all():
|
||||||
|
self.graph.add_edge(self.object.get_short_name(), u.get_short_name())
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
self.object = self.get_object()
|
||||||
|
if "descent" in self.request.GET:
|
||||||
|
self.param = "godchildren"
|
||||||
|
elif "ancestors" in self.request.GET:
|
||||||
|
self.param = "godfathers"
|
||||||
|
else:
|
||||||
|
self.param = "family"
|
||||||
|
|
||||||
|
if self.param == "family":
|
||||||
|
self.build_family_graph()
|
||||||
|
else:
|
||||||
|
self.build_complex_graph()
|
||||||
|
# Pimp the graph before display
|
||||||
|
self.graph.node_attr['color'] = "lightblue"
|
||||||
|
self.graph.node_attr['style'] = "filled"
|
||||||
|
main_node = self.graph.get_node(self.object.get_short_name())
|
||||||
|
main_node.attr['color'] = "sandybrown"
|
||||||
|
main_node.attr['shape'] = "rect"
|
||||||
|
if self.param == "godchildren":
|
||||||
|
self.graph.graph_attr['label'] = _("Godchildren")
|
||||||
|
elif self.param == "godfathers":
|
||||||
|
self.graph.graph_attr['label'] = _("Godfathers")
|
||||||
|
else:
|
||||||
|
self.graph.graph_attr['label'] = _("Family")
|
||||||
|
img = self.graph.draw(format="png", prog="dot")
|
||||||
|
return HttpResponse(img, content_type="image/png")
|
||||||
|
|
||||||
class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
|
class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2017-10-09 16:19+0200\n"
|
"POT-Creation-Date: 2017-10-11 12:22+0200\n"
|
||||||
"PO-Revision-Date: 2016-07-18\n"
|
"PO-Revision-Date: 2016-07-18\n"
|
||||||
"Last-Translator: Skia <skia@libskia.so>\n"
|
"Last-Translator: Skia <skia@libskia.so>\n"
|
||||||
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
||||||
@ -745,9 +745,10 @@ msgid ""
|
|||||||
"Warning: if you select <em>Account</em>, the opposite operation will be "
|
"Warning: if you select <em>Account</em>, the opposite operation will be "
|
||||||
"created in the target account. If you don't want that, select <em>Club</em> "
|
"created in the target account. If you don't want that, select <em>Club</em> "
|
||||||
"instead of <em>Account</em>."
|
"instead of <em>Account</em>."
|
||||||
msgstr "Attention : si vous sélectionnez <em>Compte</em>, l'opération inverse sera "
|
msgstr ""
|
||||||
"créée dans le compte cible. Si vous ne le voulez pas, sélectionnez <em>Club</em> "
|
"Attention : si vous sélectionnez <em>Compte</em>, l'opération inverse sera "
|
||||||
"à la place de <em>Compte</em>."
|
"créée dans le compte cible. Si vous ne le voulez pas, sélectionnez <em>Club</"
|
||||||
|
"em> à la place de <em>Compte</em>."
|
||||||
|
|
||||||
#: accounting/templates/accounting/operation_edit.jinja:46
|
#: accounting/templates/accounting/operation_edit.jinja:46
|
||||||
msgid "Linked operation:"
|
msgid "Linked operation:"
|
||||||
@ -760,7 +761,7 @@ msgstr "Opération liée : "
|
|||||||
#: core/templates/core/file_edit.jinja:8
|
#: core/templates/core/file_edit.jinja:8
|
||||||
#: core/templates/core/macros_pages.jinja:26
|
#: core/templates/core/macros_pages.jinja:26
|
||||||
#: core/templates/core/page_prop.jinja:11
|
#: core/templates/core/page_prop.jinja:11
|
||||||
#: core/templates/core/user_godfathers.jinja:35
|
#: core/templates/core/user_godfathers.jinja:41
|
||||||
#: core/templates/core/user_preferences.jinja:12
|
#: core/templates/core/user_preferences.jinja:12
|
||||||
#: core/templates/core/user_preferences.jinja:19
|
#: core/templates/core/user_preferences.jinja:19
|
||||||
#: counter/templates/counter/cash_register_summary.jinja:22
|
#: counter/templates/counter/cash_register_summary.jinja:22
|
||||||
@ -896,16 +897,12 @@ msgid "logo"
|
|||||||
msgstr "logo"
|
msgstr "logo"
|
||||||
|
|
||||||
#: club/models.py:62
|
#: club/models.py:62
|
||||||
#, fuzzy
|
|
||||||
#| msgid "active"
|
|
||||||
msgid "is active"
|
msgid "is active"
|
||||||
msgstr "actif"
|
msgstr "actif"
|
||||||
|
|
||||||
#: club/models.py:63
|
#: club/models.py:63
|
||||||
#, fuzzy
|
|
||||||
#| msgid "description"
|
|
||||||
msgid "short description"
|
msgid "short description"
|
||||||
msgstr "description"
|
msgstr "description courte"
|
||||||
|
|
||||||
#: club/models.py:64 core/models.py:199
|
#: club/models.py:64 core/models.py:199
|
||||||
msgid "address"
|
msgid "address"
|
||||||
@ -2895,26 +2892,69 @@ msgid "Change user password"
|
|||||||
msgstr "Changer le mot de passe"
|
msgstr "Changer le mot de passe"
|
||||||
|
|
||||||
#: core/templates/core/user_godfathers.jinja:5
|
#: core/templates/core/user_godfathers.jinja:5
|
||||||
|
#: core/templates/core/user_godfathers_tree.jinja:7
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(user_name)s's godfathers"
|
msgid "%(user_name)s's godfathers"
|
||||||
msgstr "Parrains de %(user_name)s"
|
msgstr "Parrains de %(user_name)s"
|
||||||
|
|
||||||
#: core/templates/core/user_godfathers.jinja:10 core/views/user.py:169
|
#: core/templates/core/user_godfathers.jinja:10
|
||||||
|
msgid "Show family picture"
|
||||||
|
msgstr "Voir une image de la famille"
|
||||||
|
|
||||||
|
#: core/templates/core/user_godfathers.jinja:12 core/views/user.py:169
|
||||||
|
#: core/views/user.py:388
|
||||||
msgid "Godfathers"
|
msgid "Godfathers"
|
||||||
msgstr "Parrains"
|
msgstr "Parrains"
|
||||||
|
|
||||||
#: core/templates/core/user_godfathers.jinja:18
|
#: core/templates/core/user_godfathers.jinja:20
|
||||||
|
msgid "Show ancestors tree"
|
||||||
|
msgstr "Voir l'arbre des ancêtres"
|
||||||
|
|
||||||
|
#: core/templates/core/user_godfathers.jinja:22
|
||||||
|
#: core/templates/core/user_godfathers_tree.jinja:50
|
||||||
msgid "No godfathers"
|
msgid "No godfathers"
|
||||||
msgstr "Pas de parrains"
|
msgstr "Pas de parrains"
|
||||||
|
|
||||||
#: core/templates/core/user_godfathers.jinja:21
|
#: core/templates/core/user_godfathers.jinja:25 core/views/user.py:386
|
||||||
msgid "Godchildren"
|
msgid "Godchildren"
|
||||||
msgstr "Fillots"
|
msgstr "Fillots"
|
||||||
|
|
||||||
#: core/templates/core/user_godfathers.jinja:29
|
#: core/templates/core/user_godfathers.jinja:33
|
||||||
|
msgid "Show descent tree"
|
||||||
|
msgstr "Voir l'arbre de la descendance"
|
||||||
|
|
||||||
|
#: core/templates/core/user_godfathers.jinja:35
|
||||||
|
#: core/templates/core/user_godfathers_tree.jinja:48
|
||||||
msgid "No godchildren"
|
msgid "No godchildren"
|
||||||
msgstr "Pas de fillots"
|
msgstr "Pas de fillots"
|
||||||
|
|
||||||
|
#: core/templates/core/user_godfathers_tree.jinja:5
|
||||||
|
msgid "%(user_name)s's godchildren"
|
||||||
|
msgstr "Fillots de %(user_name)s"
|
||||||
|
|
||||||
|
#: core/templates/core/user_godfathers_tree.jinja:20
|
||||||
|
msgid "Already seen (check above)"
|
||||||
|
msgstr "Déjà vu (voir plus haut)"
|
||||||
|
|
||||||
|
#: core/templates/core/user_godfathers_tree.jinja:33
|
||||||
|
msgid "Back to family"
|
||||||
|
msgstr "Retour à la famille"
|
||||||
|
|
||||||
|
#: core/templates/core/user_godfathers_tree.jinja:37
|
||||||
|
#: core/templates/core/user_godfathers_tree.jinja:41
|
||||||
|
msgid "Show a picture of the tree"
|
||||||
|
msgstr "Voir une image de l'arbre"
|
||||||
|
|
||||||
|
#: core/templates/core/user_godfathers_tree.jinja:38
|
||||||
|
#, python-format
|
||||||
|
msgid "Descent tree of %(u)s"
|
||||||
|
msgstr "Descendants de %(u)s"
|
||||||
|
|
||||||
|
#: core/templates/core/user_godfathers_tree.jinja:42
|
||||||
|
#, python-format
|
||||||
|
msgid "Ancestors tree of %(u)s"
|
||||||
|
msgstr "Ancêtres de %(u)s"
|
||||||
|
|
||||||
#: core/templates/core/user_group.jinja:4
|
#: core/templates/core/user_group.jinja:4
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Edit user groups for %(user_name)s"
|
msgid "Edit user groups for %(user_name)s"
|
||||||
@ -3181,7 +3221,11 @@ msgstr "Fillot"
|
|||||||
msgid "Pictures"
|
msgid "Pictures"
|
||||||
msgstr "Photos"
|
msgstr "Photos"
|
||||||
|
|
||||||
#: core/views/user.py:388
|
#: core/views/user.py:390
|
||||||
|
msgid "Family"
|
||||||
|
msgstr "Famille"
|
||||||
|
|
||||||
|
#: core/views/user.py:473
|
||||||
msgid "User already has a profile picture"
|
msgid "User already has a profile picture"
|
||||||
msgstr "L'utilisateur a déjà une photo de profil"
|
msgstr "L'utilisateur a déjà une photo de profil"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user