mirror of
https://gitlab.crans.org/bde/nk20
synced 2025-06-22 18:38:23 +02:00
Compare commits
1 Commits
permission
...
3096cb2966
Author | SHA1 | Date | |
---|---|---|---|
3096cb2966
|
@ -1,9 +1,10 @@
|
|||||||
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from api.viewsets import ReadProtectedModelViewSet
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework.filters import SearchFilter
|
|
||||||
|
from api.filters import RegexSafeSearchFilter
|
||||||
|
from api.viewsets import ReadProtectedModelViewSet
|
||||||
|
|
||||||
from .serializers import ActivitySerializer, ActivityTypeSerializer, EntrySerializer, GuestSerializer
|
from .serializers import ActivitySerializer, ActivityTypeSerializer, EntrySerializer, GuestSerializer
|
||||||
from ..models import Activity, ActivityType, Entry, Guest
|
from ..models import Activity, ActivityType, Entry, Guest
|
||||||
@ -29,7 +30,7 @@ class ActivityViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Activity.objects.order_by('id')
|
queryset = Activity.objects.order_by('id')
|
||||||
serializer_class = ActivitySerializer
|
serializer_class = ActivitySerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['name', 'description', 'activity_type', 'location', 'creater', 'organizer', 'attendees_club',
|
filterset_fields = ['name', 'description', 'activity_type', 'location', 'creater', 'organizer', 'attendees_club',
|
||||||
'date_start', 'date_end', 'valid', 'open', ]
|
'date_start', 'date_end', 'valid', 'open', ]
|
||||||
search_fields = ['$name', '$description', '$location', '$creater__last_name', '$creater__first_name',
|
search_fields = ['$name', '$description', '$location', '$creater__last_name', '$creater__first_name',
|
||||||
@ -47,7 +48,7 @@ class GuestViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Guest.objects.order_by('id')
|
queryset = Guest.objects.order_by('id')
|
||||||
serializer_class = GuestSerializer
|
serializer_class = GuestSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['activity', 'activity__name', 'last_name', 'first_name', 'inviter', 'inviter__alias__name',
|
filterset_fields = ['activity', 'activity__name', 'last_name', 'first_name', 'inviter', 'inviter__alias__name',
|
||||||
'inviter__alias__normalized_name', ]
|
'inviter__alias__normalized_name', ]
|
||||||
search_fields = ['$activity__name', '$last_name', '$first_name', '$inviter__user__email', '$inviter__alias__name',
|
search_fields = ['$activity__name', '$last_name', '$first_name', '$inviter__user__email', '$inviter__alias__name',
|
||||||
@ -62,7 +63,7 @@ class EntryViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Entry.objects.order_by('id')
|
queryset = Entry.objects.order_by('id')
|
||||||
serializer_class = EntrySerializer
|
serializer_class = EntrySerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['activity', 'time', 'note', 'guest', ]
|
filterset_fields = ['activity', 'time', 'note', 'guest', ]
|
||||||
search_fields = ['$activity__name', '$note__user__email', '$note__alias__name', '$note__alias__normalized_name',
|
search_fields = ['$activity__name', '$note__user__email', '$note__alias__name', '$note__alias__normalized_name',
|
||||||
'$guest__last_name', '$guest__first_name', ]
|
'$guest__last_name', '$guest__first_name', ]
|
||||||
|
42
apps/api/filters.py
Normal file
42
apps/api/filters.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import re
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
from rest_framework.filters import SearchFilter
|
||||||
|
|
||||||
|
|
||||||
|
class RegexSafeSearchFilter(SearchFilter):
|
||||||
|
@lru_cache
|
||||||
|
def validate_regex(self, search_term) -> bool:
|
||||||
|
try:
|
||||||
|
re.compile(search_term)
|
||||||
|
return True
|
||||||
|
except re.error:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_search_fields(self, view, request):
|
||||||
|
"""
|
||||||
|
Ensure that given regex are valid.
|
||||||
|
If not, we consider that the user is trying to search by substring.
|
||||||
|
"""
|
||||||
|
search_fields = super().get_search_fields(view, request)
|
||||||
|
search_terms = self.get_search_terms(request)
|
||||||
|
|
||||||
|
for search_term in search_terms:
|
||||||
|
if not self.validate_regex(search_term):
|
||||||
|
# Invalid regex. We assume we don't query by regex but by substring.
|
||||||
|
search_fields = [f.replace('$', '') for f in search_fields]
|
||||||
|
break
|
||||||
|
|
||||||
|
return search_fields
|
||||||
|
|
||||||
|
def get_search_terms(self, request):
|
||||||
|
"""
|
||||||
|
Ensure that search field is a valid regex query. If not, we remove extra characters.
|
||||||
|
"""
|
||||||
|
terms = super().get_search_terms(request)
|
||||||
|
if not all(self.validate_regex(term) for term in terms):
|
||||||
|
# Invalid regex. If a ^ is prefixed to the search term, we remove it.
|
||||||
|
terms = [term[1:] if term[0] == '^' else term for term in terms]
|
||||||
|
# Same for dollars.
|
||||||
|
terms = [term[:-1] if term[-1] == '$' else term for term in terms]
|
||||||
|
return terms
|
@ -12,11 +12,13 @@ from django.contrib.contenttypes.models import ContentType
|
|||||||
from django.db.models.fields.files import ImageFieldFile
|
from django.db.models.fields.files import ImageFieldFile
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
|
from phonenumbers import PhoneNumber
|
||||||
|
from rest_framework.filters import OrderingFilter
|
||||||
|
|
||||||
|
from api.filters import RegexSafeSearchFilter
|
||||||
from member.models import Membership, Club
|
from member.models import Membership, Club
|
||||||
from note.models import NoteClub, NoteUser, Alias, Note
|
from note.models import NoteClub, NoteUser, Alias, Note
|
||||||
from permission.models import PermissionMask, Permission, Role
|
from permission.models import PermissionMask, Permission, Role
|
||||||
from phonenumbers import PhoneNumber
|
|
||||||
from rest_framework.filters import SearchFilter, OrderingFilter
|
|
||||||
|
|
||||||
from .viewsets import ContentTypeViewSet, UserViewSet
|
from .viewsets import ContentTypeViewSet, UserViewSet
|
||||||
|
|
||||||
@ -87,7 +89,7 @@ class TestAPI(TestCase):
|
|||||||
resp = self.client.get(url + f"?ordering=-{field}")
|
resp = self.client.get(url + f"?ordering=-{field}")
|
||||||
self.assertEqual(resp.status_code, 200)
|
self.assertEqual(resp.status_code, 200)
|
||||||
|
|
||||||
if SearchFilter in backends:
|
if RegexSafeSearchFilter in backends:
|
||||||
# Basic search
|
# Basic search
|
||||||
for field in viewset.search_fields:
|
for field in viewset.search_fields:
|
||||||
obj = self.fix_note_object(obj, field)
|
obj = self.fix_note_object(obj, field)
|
||||||
|
@ -6,11 +6,11 @@ from django_filters.rest_framework import DjangoFilterBackend
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from rest_framework.filters import SearchFilter
|
|
||||||
from rest_framework.viewsets import ReadOnlyModelViewSet, ModelViewSet
|
from rest_framework.viewsets import ReadOnlyModelViewSet, ModelViewSet
|
||||||
from permission.backends import PermissionBackend
|
from permission.backends import PermissionBackend
|
||||||
from note.models import Alias
|
from note.models import Alias
|
||||||
|
|
||||||
|
from .filters import RegexSafeSearchFilter
|
||||||
from .serializers import UserSerializer, ContentTypeSerializer
|
from .serializers import UserSerializer, ContentTypeSerializer
|
||||||
|
|
||||||
|
|
||||||
@ -107,6 +107,6 @@ class ContentTypeViewSet(ReadOnlyModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = ContentType.objects.order_by('id')
|
queryset = ContentType.objects.order_by('id')
|
||||||
serializer_class = ContentTypeSerializer
|
serializer_class = ContentTypeSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['id', 'app_label', 'model', ]
|
filterset_fields = ['id', 'app_label', 'model', ]
|
||||||
search_fields = ['$app_label', '$model', ]
|
search_fields = ['$app_label', '$model', ]
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework.filters import OrderingFilter
|
from rest_framework.filters import OrderingFilter
|
||||||
|
|
||||||
from api.viewsets import ReadOnlyProtectedModelViewSet
|
from api.viewsets import ReadOnlyProtectedModelViewSet
|
||||||
|
|
||||||
from .serializers import ChangelogSerializer
|
from .serializers import ChangelogSerializer
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
from rest_framework.filters import OrderingFilter
|
||||||
|
|
||||||
|
from api.filters import RegexSafeSearchFilter
|
||||||
from api.viewsets import ReadProtectedModelViewSet
|
from api.viewsets import ReadProtectedModelViewSet
|
||||||
|
|
||||||
from .serializers import ProfileSerializer, ClubSerializer, MembershipSerializer
|
from .serializers import ProfileSerializer, ClubSerializer, MembershipSerializer
|
||||||
@ -17,7 +19,7 @@ class ProfileViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Profile.objects.order_by('id')
|
queryset = Profile.objects.order_by('id')
|
||||||
serializer_class = ProfileSerializer
|
serializer_class = ProfileSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['user', 'user__first_name', 'user__last_name', 'user__username', 'user__email',
|
filterset_fields = ['user', 'user__first_name', 'user__last_name', 'user__username', 'user__email',
|
||||||
'user__note__alias__name', 'user__note__alias__normalized_name', 'phone_number', "section",
|
'user__note__alias__name', 'user__note__alias__normalized_name', 'phone_number', "section",
|
||||||
'department', 'promotion', 'address', 'paid', 'ml_events_registration', 'ml_sport_registration',
|
'department', 'promotion', 'address', 'paid', 'ml_events_registration', 'ml_sport_registration',
|
||||||
@ -34,7 +36,7 @@ class ClubViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Club.objects.order_by('id')
|
queryset = Club.objects.order_by('id')
|
||||||
serializer_class = ClubSerializer
|
serializer_class = ClubSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['name', 'email', 'note__alias__name', 'note__alias__normalized_name', 'parent_club',
|
filterset_fields = ['name', 'email', 'note__alias__name', 'note__alias__normalized_name', 'parent_club',
|
||||||
'parent_club__name', 'require_memberships', 'membership_fee_paid', 'membership_fee_unpaid',
|
'parent_club__name', 'require_memberships', 'membership_fee_paid', 'membership_fee_unpaid',
|
||||||
'membership_duration', 'membership_start', 'membership_end', ]
|
'membership_duration', 'membership_start', 'membership_end', ]
|
||||||
@ -49,7 +51,7 @@ class MembershipViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Membership.objects.order_by('id')
|
queryset = Membership.objects.order_by('id')
|
||||||
serializer_class = MembershipSerializer
|
serializer_class = MembershipSerializer
|
||||||
filter_backends = [DjangoFilterBackend, OrderingFilter, SearchFilter]
|
filter_backends = [DjangoFilterBackend, OrderingFilter, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['club__name', 'club__email', 'club__note__alias__name', 'club__note__alias__normalized_name',
|
filterset_fields = ['club__name', 'club__email', 'club__note__alias__name', 'club__note__alias__normalized_name',
|
||||||
'user__username', 'user__last_name', 'user__first_name', 'user__email',
|
'user__username', 'user__last_name', 'user__first_name', 'user__email',
|
||||||
'user__note__alias__name', 'user__note__alias__normalized_name',
|
'user__note__alias__name', 'user__note__alias__normalized_name',
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
/**
|
|
||||||
* On form submit, create a new friendship
|
|
||||||
*/
|
|
||||||
function create_trust (e) {
|
|
||||||
// Do not submit HTML form
|
|
||||||
e.preventDefault()
|
|
||||||
|
|
||||||
// Get data and send to API
|
|
||||||
const formData = new FormData(e.target)
|
|
||||||
$.getJSON('/api/note/alias/'+formData.get('trusted') + '/',
|
|
||||||
function (trusted_alias) {
|
|
||||||
if ((trusted_alias.note == formData.get('trusting')))
|
|
||||||
{
|
|
||||||
addMsg(gettext("You can't add yourself as a friend"), "danger")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
$.post('/api/note/trust/', {
|
|
||||||
csrfmiddlewaretoken: formData.get('csrfmiddlewaretoken'),
|
|
||||||
trusting: formData.get('trusting'),
|
|
||||||
trusted: trusted_alias.note
|
|
||||||
}).done(function () {
|
|
||||||
// Reload table
|
|
||||||
$('#trust_table').load(location.pathname + ' #trust_table')
|
|
||||||
addMsg(gettext('Friendship successfully added'), 'success')
|
|
||||||
}).fail(function (xhr, _textStatus, _error) {
|
|
||||||
errMsg(xhr.responseJSON)
|
|
||||||
})
|
|
||||||
}).fail(function (xhr, _textStatus, _error) {
|
|
||||||
errMsg(xhr.responseJSON)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* On click of "delete", delete the alias
|
|
||||||
* @param button_id:Integer Alias id to remove
|
|
||||||
*/
|
|
||||||
function delete_button (button_id) {
|
|
||||||
$.ajax({
|
|
||||||
url: '/api/note/trust/' + button_id + '/',
|
|
||||||
method: 'DELETE',
|
|
||||||
headers: { 'X-CSRFTOKEN': CSRF_TOKEN }
|
|
||||||
}).done(function () {
|
|
||||||
addMsg(gettext('Friendship successfully deleted'), 'success')
|
|
||||||
$('#trust_table').load(location.pathname + ' #trust_table')
|
|
||||||
}).fail(function (xhr, _textStatus, _error) {
|
|
||||||
errMsg(xhr.responseJSON)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
|
||||||
// Attach event
|
|
||||||
document.getElementById('form_trust').addEventListener('submit', create_trust)
|
|
||||||
})
|
|
@ -25,14 +25,6 @@
|
|||||||
</a>
|
</a>
|
||||||
</dd>
|
</dd>
|
||||||
|
|
||||||
<dt class="col-xl-6">{% trans 'friendships'|capfirst %}</dt>
|
|
||||||
<dd class="col-xl-6">
|
|
||||||
<a class="badge badge-secondary" href="{% url 'member:user_trust' user_object.pk %}">
|
|
||||||
<i class="fa fa-edit"></i>
|
|
||||||
{% trans 'Manage friendships' %} ({{ user_object.note.trusting.all|length }})
|
|
||||||
</a>
|
|
||||||
</dd>
|
|
||||||
|
|
||||||
{% if "member.view_profile"|has_perm:user_object.profile %}
|
{% if "member.view_profile"|has_perm:user_object.profile %}
|
||||||
<dt class="col-xl-6">{% trans 'section'|capfirst %}</dt>
|
<dt class="col-xl-6">{% trans 'section'|capfirst %}</dt>
|
||||||
<dd class="col-xl-6">{{ user_object.profile.section }}</dd>
|
<dd class="col-xl-6">{{ user_object.profile.section }}</dd>
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
{% extends "member/base.html" %}
|
|
||||||
{% comment %}
|
|
||||||
SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
{% endcomment %}
|
|
||||||
{% load static django_tables2 i18n %}
|
|
||||||
|
|
||||||
{% block profile_content %}
|
|
||||||
<div class="card bg-light mb-3">
|
|
||||||
<h3 class="card-header text-center">
|
|
||||||
{% trans "Note friendships" %}
|
|
||||||
</h3>
|
|
||||||
<div class="card-body">
|
|
||||||
{% if can_create %}
|
|
||||||
<form class="input-group" method="POST" id="form_trust">
|
|
||||||
{% csrf_token %}
|
|
||||||
<input type="hidden" name="trusting" value="{{ object.note.pk }}">
|
|
||||||
{%include "autocomplete_model.html" %}
|
|
||||||
<div class="input-group-append">
|
|
||||||
<input type="submit" class="btn btn-success" value="{% trans "Add" %}">
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% render_table trusting %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert alert-warning card">
|
|
||||||
{% blocktrans trimmed %}
|
|
||||||
Adding someone as a friend enables them to initiate transactions coming
|
|
||||||
from your account (while keeping your balance positive). This is
|
|
||||||
designed to simplify using note kfet transfers to transfer money between
|
|
||||||
users. The intent is that one person can make all transfers for a group of
|
|
||||||
friends without needing additional rights among them.
|
|
||||||
{% endblocktrans %}
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block extrajavascript %}
|
|
||||||
<script src="{% static "member/js/trust.js" %}"></script>
|
|
||||||
<script src="{% static "js/autocomplete_model.js" %}"></script>
|
|
||||||
{% endblock%}
|
|
@ -23,6 +23,5 @@ urlpatterns = [
|
|||||||
path('user/<int:pk>/update/', views.UserUpdateView.as_view(), name="user_update_profile"),
|
path('user/<int:pk>/update/', views.UserUpdateView.as_view(), name="user_update_profile"),
|
||||||
path('user/<int:pk>/update_pic/', views.ProfilePictureUpdateView.as_view(), name="user_update_pic"),
|
path('user/<int:pk>/update_pic/', views.ProfilePictureUpdateView.as_view(), name="user_update_pic"),
|
||||||
path('user/<int:pk>/aliases/', views.ProfileAliasView.as_view(), name="user_alias"),
|
path('user/<int:pk>/aliases/', views.ProfileAliasView.as_view(), name="user_alias"),
|
||||||
path('user/<int:pk>/trust', views.ProfileTrustView.as_view(), name="user_trust"),
|
|
||||||
path('manage-auth-token/', views.ManageAuthTokens.as_view(), name='auth_token'),
|
path('manage-auth-token/', views.ManageAuthTokens.as_view(), name='auth_token'),
|
||||||
]
|
]
|
||||||
|
@ -8,7 +8,6 @@ from django.contrib.auth import logout
|
|||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.views import LoginView
|
from django.contrib.auth.views import LoginView
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import Q, F
|
from django.db.models import Q, F
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
@ -19,9 +18,9 @@ from django.views.generic import DetailView, UpdateView, TemplateView
|
|||||||
from django.views.generic.edit import FormMixin
|
from django.views.generic.edit import FormMixin
|
||||||
from django_tables2.views import SingleTableView
|
from django_tables2.views import SingleTableView
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
from note.models import Alias, NoteClub, NoteUser, Trust
|
from note.models import Alias, NoteUser, NoteClub
|
||||||
from note.models.transactions import Transaction, SpecialTransaction
|
from note.models.transactions import Transaction, SpecialTransaction
|
||||||
from note.tables import HistoryTable, AliasTable, TrustTable
|
from note.tables import HistoryTable, AliasTable
|
||||||
from note_kfet.middlewares import _set_current_request
|
from note_kfet.middlewares import _set_current_request
|
||||||
from permission.backends import PermissionBackend
|
from permission.backends import PermissionBackend
|
||||||
from permission.models import Role
|
from permission.models import Role
|
||||||
@ -244,39 +243,6 @@ class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class ProfileTrustView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
|
|
||||||
"""
|
|
||||||
View and manage user trust relationships
|
|
||||||
"""
|
|
||||||
model = User
|
|
||||||
template_name = 'member/profile_trust.html'
|
|
||||||
context_object_name = 'user_object'
|
|
||||||
extra_context = {"title": _("Note friendships")}
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
note = context['object'].note
|
|
||||||
context["trusting"] = TrustTable(
|
|
||||||
note.trusting.filter(PermissionBackend.filter_queryset(self.request, Trust, "view")).distinct().all())
|
|
||||||
context["can_create"] = PermissionBackend.check_perm(self.request, "note.add_trust", Trust(
|
|
||||||
trusting=context["object"].note,
|
|
||||||
trusted=context["object"].note
|
|
||||||
))
|
|
||||||
context["widget"] = {
|
|
||||||
"name": "trusted",
|
|
||||||
"attrs": {
|
|
||||||
"model_pk": ContentType.objects.get_for_model(Alias).pk,
|
|
||||||
"class": "autocomplete form-control",
|
|
||||||
"id": "trusted",
|
|
||||||
"resetable": True,
|
|
||||||
"api_url": "/api/note/alias/?note__polymorphic_ctype__model=noteuser",
|
|
||||||
"name_field": "name",
|
|
||||||
"placeholder": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
|
class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
View and manage user aliases.
|
View and manage user aliases.
|
||||||
|
@ -12,7 +12,7 @@ from note_kfet.middlewares import get_current_request
|
|||||||
from permission.backends import PermissionBackend
|
from permission.backends import PermissionBackend
|
||||||
from rest_framework.utils import model_meta
|
from rest_framework.utils import model_meta
|
||||||
|
|
||||||
from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias, Trust
|
from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
|
||||||
from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction, TemplateCategory, \
|
from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction, TemplateCategory, \
|
||||||
RecurrentTransaction, SpecialTransaction
|
RecurrentTransaction, SpecialTransaction
|
||||||
|
|
||||||
@ -77,22 +77,6 @@ class NoteUserSerializer(serializers.ModelSerializer):
|
|||||||
return str(obj)
|
return str(obj)
|
||||||
|
|
||||||
|
|
||||||
class TrustSerializer(serializers.ModelSerializer):
|
|
||||||
"""
|
|
||||||
REST API Serializer for Trusts.
|
|
||||||
The djangorestframework plugin will analyse the model `Trust` and parse all fields in the API.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Trust
|
|
||||||
fields = '__all__'
|
|
||||||
|
|
||||||
def validate(self, attrs):
|
|
||||||
instance = Trust(**attrs)
|
|
||||||
instance.clean()
|
|
||||||
return attrs
|
|
||||||
|
|
||||||
|
|
||||||
class AliasSerializer(serializers.ModelSerializer):
|
class AliasSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
REST API Serializer for Aliases.
|
REST API Serializer for Aliases.
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from .views import NotePolymorphicViewSet, AliasViewSet, ConsumerViewSet, \
|
from .views import NotePolymorphicViewSet, AliasViewSet, ConsumerViewSet, \
|
||||||
TemplateCategoryViewSet, TransactionViewSet, TransactionTemplateViewSet, \
|
TemplateCategoryViewSet, TransactionViewSet, TransactionTemplateViewSet
|
||||||
TrustViewSet
|
|
||||||
|
|
||||||
|
|
||||||
def register_note_urls(router, path):
|
def register_note_urls(router, path):
|
||||||
@ -12,7 +11,6 @@ def register_note_urls(router, path):
|
|||||||
"""
|
"""
|
||||||
router.register(path + '/note', NotePolymorphicViewSet)
|
router.register(path + '/note', NotePolymorphicViewSet)
|
||||||
router.register(path + '/alias', AliasViewSet)
|
router.register(path + '/alias', AliasViewSet)
|
||||||
router.register(path + '/trust', TrustViewSet)
|
|
||||||
router.register(path + '/consumer', ConsumerViewSet)
|
router.register(path + '/consumer', ConsumerViewSet)
|
||||||
|
|
||||||
router.register(path + '/transaction/category', TemplateCategoryViewSet)
|
router.register(path + '/transaction/category', TemplateCategoryViewSet)
|
||||||
|
@ -1,22 +1,24 @@
|
|||||||
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
from rest_framework.filters import OrderingFilter
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
|
||||||
|
from api.filters import RegexSafeSearchFilter
|
||||||
from api.viewsets import ReadProtectedModelViewSet, ReadOnlyProtectedModelViewSet
|
from api.viewsets import ReadProtectedModelViewSet, ReadOnlyProtectedModelViewSet
|
||||||
from permission.backends import PermissionBackend
|
from permission.backends import PermissionBackend
|
||||||
|
|
||||||
from .serializers import NotePolymorphicSerializer, AliasSerializer, ConsumerSerializer,\
|
from .serializers import NotePolymorphicSerializer, AliasSerializer, ConsumerSerializer,\
|
||||||
TemplateCategorySerializer, TransactionTemplateSerializer, TransactionPolymorphicSerializer, \
|
TemplateCategorySerializer, TransactionTemplateSerializer, TransactionPolymorphicSerializer
|
||||||
TrustSerializer
|
from ..models.notes import Note, Alias, NoteUser, NoteClub, NoteSpecial
|
||||||
from ..models.notes import Note, Alias, NoteUser, NoteClub, NoteSpecial, Trust
|
|
||||||
from ..models.transactions import TransactionTemplate, Transaction, TemplateCategory
|
from ..models.transactions import TransactionTemplate, Transaction, TemplateCategory
|
||||||
|
|
||||||
|
|
||||||
@ -29,7 +31,7 @@ class NotePolymorphicViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Note.objects.order_by('id')
|
queryset = Note.objects.order_by('id')
|
||||||
serializer_class = NotePolymorphicSerializer
|
serializer_class = NotePolymorphicSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter, OrderingFilter]
|
||||||
filterset_fields = ['alias__name', 'polymorphic_ctype', 'is_active', 'balance', 'last_negative', 'created_at', ]
|
filterset_fields = ['alias__name', 'polymorphic_ctype', 'is_active', 'balance', 'last_negative', 'created_at', ]
|
||||||
search_fields = ['$alias__normalized_name', '$alias__name', '$polymorphic_ctype__model',
|
search_fields = ['$alias__normalized_name', '$alias__name', '$polymorphic_ctype__model',
|
||||||
'$noteuser__user__last_name', '$noteuser__user__first_name', '$noteuser__user__email',
|
'$noteuser__user__last_name', '$noteuser__user__first_name', '$noteuser__user__email',
|
||||||
@ -57,45 +59,15 @@ class NotePolymorphicViewSet(ReadProtectedModelViewSet):
|
|||||||
return queryset.order_by("id")
|
return queryset.order_by("id")
|
||||||
|
|
||||||
|
|
||||||
class TrustViewSet(ReadProtectedModelViewSet):
|
|
||||||
"""
|
|
||||||
REST Trust View set.
|
|
||||||
The djangorestframework plugin will get all `Trust` objects, serialize it to JSON with the given serializer,
|
|
||||||
then render it on /api/note/trust/
|
|
||||||
"""
|
|
||||||
queryset = Trust.objects
|
|
||||||
serializer_class = TrustSerializer
|
|
||||||
filter_backends = [SearchFilter, DjangoFilterBackend, OrderingFilter]
|
|
||||||
search_fields = ['$trusting__alias__name', '$trusting__alias__normalized_name',
|
|
||||||
'$trusted__alias__name', '$trusted__alias__normalized_name']
|
|
||||||
filterset_fields = ['trusting', 'trusting__noteuser__user', 'trusted', 'trusted__noteuser__user']
|
|
||||||
ordering_fields = ['trusting', 'trusted', ]
|
|
||||||
|
|
||||||
def get_serializer_class(self):
|
|
||||||
serializer_class = self.serializer_class
|
|
||||||
if self.request.method in ['PUT', 'PATCH']:
|
|
||||||
# trust relationship can't change people involved
|
|
||||||
serializer_class.Meta.read_only_fields = ('trusting', 'trusting',)
|
|
||||||
return serializer_class
|
|
||||||
|
|
||||||
def destroy(self, request, *args, **kwargs):
|
|
||||||
instance = self.get_object()
|
|
||||||
try:
|
|
||||||
self.perform_destroy(instance)
|
|
||||||
except ValidationError as e:
|
|
||||||
return Response({e.code: str(e)}, status.HTTP_400_BAD_REQUEST)
|
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
|
||||||
|
|
||||||
|
|
||||||
class AliasViewSet(ReadProtectedModelViewSet):
|
class AliasViewSet(ReadProtectedModelViewSet):
|
||||||
"""
|
"""
|
||||||
REST API View set.
|
REST API View set.
|
||||||
The djangorestframework plugin will get all `Alias` objects, serialize it to JSON with the given serializer,
|
The djangorestframework plugin will get all `Alias` objects, serialize it to JSON with the given serializer,
|
||||||
then render it on /api/note/aliases/
|
then render it on /api/aliases/
|
||||||
"""
|
"""
|
||||||
queryset = Alias.objects
|
queryset = Alias.objects
|
||||||
serializer_class = AliasSerializer
|
serializer_class = AliasSerializer
|
||||||
filter_backends = [SearchFilter, DjangoFilterBackend, OrderingFilter]
|
filter_backends = [RegexSafeSearchFilter, DjangoFilterBackend, OrderingFilter]
|
||||||
search_fields = ['$normalized_name', '$name', '$note__polymorphic_ctype__model', ]
|
search_fields = ['$normalized_name', '$name', '$note__polymorphic_ctype__model', ]
|
||||||
filterset_fields = ['name', 'normalized_name', 'note', 'note__noteuser__user',
|
filterset_fields = ['name', 'normalized_name', 'note', 'note__noteuser__user',
|
||||||
'note__noteclub__club', 'note__polymorphic_ctype__model', ]
|
'note__noteclub__club', 'note__polymorphic_ctype__model', ]
|
||||||
@ -147,7 +119,7 @@ class AliasViewSet(ReadProtectedModelViewSet):
|
|||||||
class ConsumerViewSet(ReadOnlyProtectedModelViewSet):
|
class ConsumerViewSet(ReadOnlyProtectedModelViewSet):
|
||||||
queryset = Alias.objects
|
queryset = Alias.objects
|
||||||
serializer_class = ConsumerSerializer
|
serializer_class = ConsumerSerializer
|
||||||
filter_backends = [SearchFilter, OrderingFilter, DjangoFilterBackend]
|
filter_backends = [RegexSafeSearchFilter, OrderingFilter, DjangoFilterBackend]
|
||||||
search_fields = ['$normalized_name', '$name', '$note__polymorphic_ctype__model', ]
|
search_fields = ['$normalized_name', '$name', '$note__polymorphic_ctype__model', ]
|
||||||
filterset_fields = ['name', 'normalized_name', 'note', 'note__noteuser__user',
|
filterset_fields = ['name', 'normalized_name', 'note', 'note__noteuser__user',
|
||||||
'note__noteclub__club', 'note__polymorphic_ctype__model', ]
|
'note__noteclub__club', 'note__polymorphic_ctype__model', ]
|
||||||
@ -207,7 +179,7 @@ class TemplateCategoryViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = TemplateCategory.objects.order_by('name')
|
queryset = TemplateCategory.objects.order_by('name')
|
||||||
serializer_class = TemplateCategorySerializer
|
serializer_class = TemplateCategorySerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['name', 'templates', 'templates__name']
|
filterset_fields = ['name', 'templates', 'templates__name']
|
||||||
search_fields = ['$name', '$templates__name', ]
|
search_fields = ['$name', '$templates__name', ]
|
||||||
|
|
||||||
@ -220,7 +192,7 @@ class TransactionTemplateViewSet(viewsets.ModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = TransactionTemplate.objects.order_by('name')
|
queryset = TransactionTemplate.objects.order_by('name')
|
||||||
serializer_class = TransactionTemplateSerializer
|
serializer_class = TransactionTemplateSerializer
|
||||||
filter_backends = [SearchFilter, DjangoFilterBackend, OrderingFilter]
|
filter_backends = [RegexSafeSearchFilter, DjangoFilterBackend, OrderingFilter]
|
||||||
filterset_fields = ['name', 'amount', 'display', 'category', 'category__name', ]
|
filterset_fields = ['name', 'amount', 'display', 'category', 'category__name', ]
|
||||||
search_fields = ['$name', '$category__name', ]
|
search_fields = ['$name', '$category__name', ]
|
||||||
ordering_fields = ['amount', ]
|
ordering_fields = ['amount', ]
|
||||||
@ -234,7 +206,7 @@ class TransactionViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Transaction.objects.order_by('-created_at')
|
queryset = Transaction.objects.order_by('-created_at')
|
||||||
serializer_class = TransactionPolymorphicSerializer
|
serializer_class = TransactionPolymorphicSerializer
|
||||||
filter_backends = [SearchFilter, DjangoFilterBackend, OrderingFilter]
|
filter_backends = [RegexSafeSearchFilter, DjangoFilterBackend, OrderingFilter]
|
||||||
filterset_fields = ['source', 'source_alias', 'source__alias__name', 'source__alias__normalized_name',
|
filterset_fields = ['source', 'source_alias', 'source__alias__name', 'source__alias__normalized_name',
|
||||||
'destination', 'destination_alias', 'destination__alias__name',
|
'destination', 'destination_alias', 'destination__alias__name',
|
||||||
'destination__alias__normalized_name', 'quantity', 'polymorphic_ctype', 'amount',
|
'destination__alias__normalized_name', 'quantity', 'polymorphic_ctype', 'amount',
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
# Generated by Django 2.2.24 on 2021-09-05 19:16
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('note', '0005_auto_20210313_1235'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Trust',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('trusted', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='trusted', to='note.Note', verbose_name='trusted')),
|
|
||||||
('trusting', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='trusting', to='note.Note', verbose_name='trusting')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'frienship',
|
|
||||||
'verbose_name_plural': 'friendships',
|
|
||||||
'unique_together': {('trusting', 'trusted')},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,13 +1,13 @@
|
|||||||
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from .notes import Alias, Note, NoteClub, NoteSpecial, NoteUser, Trust
|
from .notes import Alias, Note, NoteClub, NoteSpecial, NoteUser
|
||||||
from .transactions import MembershipTransaction, Transaction, \
|
from .transactions import MembershipTransaction, Transaction, \
|
||||||
TemplateCategory, TransactionTemplate, RecurrentTransaction, SpecialTransaction
|
TemplateCategory, TransactionTemplate, RecurrentTransaction, SpecialTransaction
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# Notes
|
# Notes
|
||||||
'Alias', 'Trust', 'Note', 'NoteClub', 'NoteSpecial', 'NoteUser',
|
'Alias', 'Note', 'NoteClub', 'NoteSpecial', 'NoteUser',
|
||||||
# Transactions
|
# Transactions
|
||||||
'MembershipTransaction', 'Transaction', 'TemplateCategory', 'TransactionTemplate',
|
'MembershipTransaction', 'Transaction', 'TemplateCategory', 'TransactionTemplate',
|
||||||
'RecurrentTransaction', 'SpecialTransaction',
|
'RecurrentTransaction', 'SpecialTransaction',
|
||||||
|
@ -217,38 +217,6 @@ class NoteSpecial(Note):
|
|||||||
return self.special_type
|
return self.special_type
|
||||||
|
|
||||||
|
|
||||||
class Trust(models.Model):
|
|
||||||
"""
|
|
||||||
A one-sided trust relationship bertween two users
|
|
||||||
|
|
||||||
If another user considers you as your friend, you can transfer money from
|
|
||||||
them
|
|
||||||
"""
|
|
||||||
|
|
||||||
trusting = models.ForeignKey(
|
|
||||||
Note,
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='trusting',
|
|
||||||
verbose_name=_('trusting')
|
|
||||||
)
|
|
||||||
|
|
||||||
trusted = models.ForeignKey(
|
|
||||||
Note,
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='trusted',
|
|
||||||
verbose_name=_('trusted')
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _("frienship")
|
|
||||||
verbose_name_plural = _("friendships")
|
|
||||||
unique_together = ("trusting", "trusted")
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return _("Friendship between {trusting} and {trusted}").format(
|
|
||||||
trusting=str(self.trusting), trusted=str(self.trusted))
|
|
||||||
|
|
||||||
|
|
||||||
class Alias(models.Model):
|
class Alias(models.Model):
|
||||||
"""
|
"""
|
||||||
points toward a :model:`note.NoteUser` or :model;`note.NoteClub` instance.
|
points toward a :model:`note.NoteUser` or :model;`note.NoteClub` instance.
|
||||||
|
@ -10,7 +10,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from note_kfet.middlewares import get_current_request
|
from note_kfet.middlewares import get_current_request
|
||||||
from permission.backends import PermissionBackend
|
from permission.backends import PermissionBackend
|
||||||
|
|
||||||
from .models.notes import Alias, Trust
|
from .models.notes import Alias
|
||||||
from .models.transactions import Transaction, TransactionTemplate
|
from .models.transactions import Transaction, TransactionTemplate
|
||||||
from .templatetags.pretty_money import pretty_money
|
from .templatetags.pretty_money import pretty_money
|
||||||
|
|
||||||
@ -148,31 +148,6 @@ DELETE_TEMPLATE = """
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class TrustTable(tables.Table):
|
|
||||||
class Meta:
|
|
||||||
attrs = {
|
|
||||||
'class': 'table table condensed table-striped',
|
|
||||||
'id': "trust_table"
|
|
||||||
}
|
|
||||||
model = Trust
|
|
||||||
fields = ("trusted",)
|
|
||||||
template_name = 'django_tables2/bootstrap4.html'
|
|
||||||
|
|
||||||
show_header = False
|
|
||||||
trusted = tables.Column(attrs={'td': {'class': 'text_center'}})
|
|
||||||
|
|
||||||
delete_col = tables.TemplateColumn(
|
|
||||||
template_code=DELETE_TEMPLATE,
|
|
||||||
extra_context={"delete_trans": _('delete')},
|
|
||||||
attrs={
|
|
||||||
'td': {
|
|
||||||
'class': lambda record: 'col-sm-1'
|
|
||||||
+ (' d-none' if not PermissionBackend.check_perm(
|
|
||||||
get_current_request(), "note.delete_trust", record)
|
|
||||||
else '')}},
|
|
||||||
verbose_name=_("Delete"),)
|
|
||||||
|
|
||||||
|
|
||||||
class AliasTable(tables.Table):
|
class AliasTable(tables.Table):
|
||||||
class Meta:
|
class Meta:
|
||||||
attrs = {
|
attrs = {
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from note_kfet.admin import admin_site
|
from note_kfet.admin import admin_site
|
||||||
|
|
||||||
from .models import Permission, PermissionVar, PermissionMask, Role
|
from .models import Permission, PermissionMask, Role
|
||||||
|
|
||||||
|
|
||||||
@admin.register(PermissionMask, site=admin_site)
|
@admin.register(PermissionMask, site=admin_site)
|
||||||
@ -15,14 +15,6 @@ class PermissionMaskAdmin(admin.ModelAdmin):
|
|||||||
list_display = ('description', 'rank', )
|
list_display = ('description', 'rank', )
|
||||||
|
|
||||||
|
|
||||||
@admin.register(PermissionVar, site=admin_site)
|
|
||||||
class PermissionVarAdmin(admin.ModelAdmin):
|
|
||||||
"""
|
|
||||||
Admin customisation for PermissionVar
|
|
||||||
"""
|
|
||||||
list_display = ('name', 'description',)
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Permission, site=admin_site)
|
@admin.register(Permission, site=admin_site)
|
||||||
class PermissionAdmin(admin.ModelAdmin):
|
class PermissionAdmin(admin.ModelAdmin):
|
||||||
"""
|
"""
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from api.viewsets import ReadOnlyProtectedModelViewSet
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework.filters import SearchFilter
|
|
||||||
|
from api.filters import RegexSafeSearchFilter
|
||||||
|
from api.viewsets import ReadOnlyProtectedModelViewSet
|
||||||
|
|
||||||
from .serializers import PermissionSerializer, RoleSerializer
|
from .serializers import PermissionSerializer, RoleSerializer
|
||||||
from ..models import Permission, Role
|
from ..models import Permission, Role
|
||||||
@ -17,7 +18,7 @@ class PermissionViewSet(ReadOnlyProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Permission.objects.order_by('id')
|
queryset = Permission.objects.order_by('id')
|
||||||
serializer_class = PermissionSerializer
|
serializer_class = PermissionSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['model', 'type', 'query', 'mask', 'field', 'permanent', ]
|
filterset_fields = ['model', 'type', 'query', 'mask', 'field', 'permanent', ]
|
||||||
search_fields = ['$model__name', '$query', '$description', ]
|
search_fields = ['$model__name', '$query', '$description', ]
|
||||||
|
|
||||||
@ -30,6 +31,6 @@ class RoleViewSet(ReadOnlyProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Role.objects.order_by('id')
|
queryset = Role.objects.order_by('id')
|
||||||
serializer_class = RoleSerializer
|
serializer_class = RoleSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['name', 'permissions', 'for_club', 'memberships__user', ]
|
filterset_fields = ['name', 'permissions', 'for_club', 'memberships__user', ]
|
||||||
search_fields = ['$name', '$for_club__name', ]
|
search_fields = ['$name', '$for_club__name', ]
|
||||||
|
@ -2928,7 +2928,7 @@
|
|||||||
"application"
|
"application"
|
||||||
],
|
],
|
||||||
"query": "{\"user\": [\"user\"]}",
|
"query": "{\"user\": [\"user\"]}",
|
||||||
"type": "add",
|
"type": "create",
|
||||||
"mask": 1,
|
"mask": 1,
|
||||||
"field": "",
|
"field": "",
|
||||||
"permanent": true,
|
"permanent": true,
|
||||||
@ -2967,118 +2967,6 @@
|
|||||||
"description": "Supprimer une application OAuth2"
|
"description": "Supprimer une application OAuth2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"model": "permission.permission",
|
|
||||||
"pk": 190,
|
|
||||||
"fields": {
|
|
||||||
"model": [
|
|
||||||
"note",
|
|
||||||
"trust"
|
|
||||||
],
|
|
||||||
"query": "{\"trusting\": [\"user\", \"note\"]}",
|
|
||||||
"type": "delete",
|
|
||||||
"mask": 1,
|
|
||||||
"field": "",
|
|
||||||
"permanent": false,
|
|
||||||
"description": "Supprimer une amitié à sa note"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"model": "permission.permission",
|
|
||||||
"pk": 191,
|
|
||||||
"fields": {
|
|
||||||
"model": [
|
|
||||||
"note",
|
|
||||||
"trust"
|
|
||||||
],
|
|
||||||
"query": "{\"trusting\": [\"user\", \"note\"]}",
|
|
||||||
"type": "add",
|
|
||||||
"mask": 1,
|
|
||||||
"field": "",
|
|
||||||
"permanent": false,
|
|
||||||
"description": "Ajouter une amitié à sa note"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"model": "permission.permission",
|
|
||||||
"pk": 192,
|
|
||||||
"fields": {
|
|
||||||
"model": [
|
|
||||||
"note",
|
|
||||||
"trust"
|
|
||||||
],
|
|
||||||
"query": "{\"trusting__is_active\": true}",
|
|
||||||
"type": "add",
|
|
||||||
"mask": 1,
|
|
||||||
"field": "",
|
|
||||||
"permanent": false,
|
|
||||||
"description": "Ajouter une amitié à une note non bloquée"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"model": "permission.permission",
|
|
||||||
"pk": 193,
|
|
||||||
"fields": {
|
|
||||||
"model": [
|
|
||||||
"note",
|
|
||||||
"trust"
|
|
||||||
],
|
|
||||||
"query": "{\"trusting__is_active\": true}",
|
|
||||||
"type": "delete",
|
|
||||||
"mask": 3,
|
|
||||||
"field": "",
|
|
||||||
"permanent": false,
|
|
||||||
"description": "Supprimer une amitié à une note non bloquée"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"model": "permission.permission",
|
|
||||||
"pk": 194,
|
|
||||||
"fields": {
|
|
||||||
"model": [
|
|
||||||
"note",
|
|
||||||
"trust"
|
|
||||||
],
|
|
||||||
"query": "{}",
|
|
||||||
"type": "view",
|
|
||||||
"mask": 3,
|
|
||||||
"field": "",
|
|
||||||
"permanent": false,
|
|
||||||
"description": "Voir toutes les amitiés, y compris celles des non adhérents"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"model": "permission.permission",
|
|
||||||
"pk": 195,
|
|
||||||
"fields": {
|
|
||||||
"model": [
|
|
||||||
"note",
|
|
||||||
"trust"
|
|
||||||
],
|
|
||||||
"query": "{\"trusting__noteuser__user\": [\"user\"]}",
|
|
||||||
"type": "view",
|
|
||||||
"mask": 1,
|
|
||||||
"field": "",
|
|
||||||
"permanent": true,
|
|
||||||
"description": "Voir ses propres amitiés, pour toujours"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"model": "permission.permission",
|
|
||||||
"pk": 196,
|
|
||||||
"fields": {
|
|
||||||
"model": [
|
|
||||||
"note",
|
|
||||||
"transaction"
|
|
||||||
],
|
|
||||||
"query": "[\"AND\", {\"source__trusting__trusted\": [\"user\", \"note\"]}, [\"OR\", {\"source__balance__gte\": {\"F\": [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]]}}, {\"valid\": false}]]",
|
|
||||||
"type": "add",
|
|
||||||
"mask": 1,
|
|
||||||
"field": "",
|
|
||||||
"permanent": false,
|
|
||||||
"description": "Transférer de l'argent depuis une note amie en restant positif"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"model": "permission.role",
|
"model": "permission.role",
|
||||||
"pk": 1,
|
"pk": 1,
|
||||||
@ -3113,11 +3001,7 @@
|
|||||||
186,
|
186,
|
||||||
187,
|
187,
|
||||||
188,
|
188,
|
||||||
189,
|
189
|
||||||
190,
|
|
||||||
191,
|
|
||||||
195,
|
|
||||||
196
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3158,9 +3042,7 @@
|
|||||||
158,
|
158,
|
||||||
159,
|
159,
|
||||||
160,
|
160,
|
||||||
179,
|
179
|
||||||
189,
|
|
||||||
190
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3310,10 +3192,7 @@
|
|||||||
176,
|
176,
|
||||||
177,
|
177,
|
||||||
178,
|
178,
|
||||||
188,
|
183
|
||||||
183,
|
|
||||||
186,
|
|
||||||
187
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3507,14 +3386,7 @@
|
|||||||
186,
|
186,
|
||||||
187,
|
187,
|
||||||
188,
|
188,
|
||||||
189,
|
189
|
||||||
190,
|
|
||||||
191,
|
|
||||||
192,
|
|
||||||
193,
|
|
||||||
194,
|
|
||||||
195,
|
|
||||||
196
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
# Generated by Django 2.2.28 on 2022-10-10 17:37
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('permission', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='PermissionVar',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('name', models.SlugField(unique=True, verbose_name='name')),
|
|
||||||
('query', models.TextField(verbose_name='query')),
|
|
||||||
('description', models.CharField(blank=True, max_length=255, verbose_name='description')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
@ -118,25 +118,6 @@ class PermissionMask(models.Model):
|
|||||||
verbose_name_plural = _("permission masks")
|
verbose_name_plural = _("permission masks")
|
||||||
|
|
||||||
|
|
||||||
class PermissionVar(models.Model):
|
|
||||||
|
|
||||||
name = models.SlugField(
|
|
||||||
unique=True,
|
|
||||||
blank=False,
|
|
||||||
verbose_name=_("name"),
|
|
||||||
)
|
|
||||||
|
|
||||||
query = models.TextField(
|
|
||||||
verbose_name=_("query"),
|
|
||||||
)
|
|
||||||
|
|
||||||
description = models.CharField(
|
|
||||||
max_length=255,
|
|
||||||
blank=True,
|
|
||||||
verbose_name=_("description"),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Permission(models.Model):
|
class Permission(models.Model):
|
||||||
|
|
||||||
PERMISSION_TYPES = [
|
PERMISSION_TYPES = [
|
||||||
@ -158,7 +139,6 @@ class Permission(models.Model):
|
|||||||
# query -> ["AND", query, …] AND multiple queries
|
# query -> ["AND", query, …] AND multiple queries
|
||||||
# | ["OR", query, …] OR multiple queries
|
# | ["OR", query, …] OR multiple queries
|
||||||
# | ["NOT", query] Opposite of query
|
# | ["NOT", query] Opposite of query
|
||||||
# | ["VAR", query] A var name as defined in PermissionVar
|
|
||||||
# query -> {key: value, …} A list of fields and values of a Q object
|
# query -> {key: value, …} A list of fields and values of a Q object
|
||||||
# key -> string A field name
|
# key -> string A field name
|
||||||
# value -> int | string | bool | null Literal values
|
# value -> int | string | bool | null Literal values
|
||||||
@ -170,7 +150,6 @@ class Permission(models.Model):
|
|||||||
# | ["MUL", oper, …] Multiply F objects or literals
|
# | ["MUL", oper, …] Multiply F objects or literals
|
||||||
# | int | string | bool | null Literal values
|
# | int | string | bool | null Literal values
|
||||||
# | ["F", string] A field
|
# | ["F", string] A field
|
||||||
# | ["VAR", string] A var name as defined in PermissionVar
|
|
||||||
#
|
#
|
||||||
# Examples:
|
# Examples:
|
||||||
# Q(is_superuser=True) := {"is_superuser": true}
|
# Q(is_superuser=True) := {"is_superuser": true}
|
||||||
@ -236,8 +215,6 @@ class Permission(models.Model):
|
|||||||
return functools.reduce(operator.mul, [Permission.compute_f(oper, **kwargs) for oper in oper[1:]])
|
return functools.reduce(operator.mul, [Permission.compute_f(oper, **kwargs) for oper in oper[1:]])
|
||||||
elif oper[0] == 'F':
|
elif oper[0] == 'F':
|
||||||
return F(oper[1])
|
return F(oper[1])
|
||||||
elif oper[0] == 'VAR':
|
|
||||||
return compute_f(json.loads(PermissionVar.objects.get(name=oper[1]).query), **kwargs)
|
|
||||||
else:
|
else:
|
||||||
field = kwargs[oper[0]]
|
field = kwargs[oper[0]]
|
||||||
for i in range(1, len(oper)):
|
for i in range(1, len(oper)):
|
||||||
@ -312,8 +289,6 @@ class Permission(models.Model):
|
|||||||
return functools.reduce(operator.or_, [Permission._about(query, **kwargs) for query in query[1:]])
|
return functools.reduce(operator.or_, [Permission._about(query, **kwargs) for query in query[1:]])
|
||||||
elif query[0] == 'NOT':
|
elif query[0] == 'NOT':
|
||||||
return ~Permission._about(query[1], **kwargs)
|
return ~Permission._about(query[1], **kwargs)
|
||||||
elif query[0] == 'VAR':
|
|
||||||
return Permission._about(json.loads(PermissionVar.objects.get(name=query[1]).query), **kwargs)
|
|
||||||
else:
|
else:
|
||||||
return Q(pk=F("pk")) if Permission.compute_param(query, **kwargs) else ~Q(pk=F("pk"))
|
return Q(pk=F("pk")) if Permission.compute_param(query, **kwargs) else ~Q(pk=F("pk"))
|
||||||
elif isinstance(query, dict):
|
elif isinstance(query, dict):
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework.filters import SearchFilter
|
|
||||||
|
from api.filters import RegexSafeSearchFilter
|
||||||
from api.viewsets import ReadProtectedModelViewSet
|
from api.viewsets import ReadProtectedModelViewSet
|
||||||
|
|
||||||
from .serializers import InvoiceSerializer, ProductSerializer, RemittanceTypeSerializer, RemittanceSerializer,\
|
from .serializers import InvoiceSerializer, ProductSerializer, RemittanceTypeSerializer, RemittanceSerializer,\
|
||||||
@ -18,7 +19,7 @@ class InvoiceViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Invoice.objects.order_by('id')
|
queryset = Invoice.objects.order_by('id')
|
||||||
serializer_class = InvoiceSerializer
|
serializer_class = InvoiceSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['bde', 'object', 'description', 'name', 'address', 'date', 'acquitted', 'locked', ]
|
filterset_fields = ['bde', 'object', 'description', 'name', 'address', 'date', 'acquitted', 'locked', ]
|
||||||
search_fields = ['$object', '$description', '$name', '$address', ]
|
search_fields = ['$object', '$description', '$name', '$address', ]
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ class ProductViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Product.objects.order_by('invoice_id', 'id')
|
queryset = Product.objects.order_by('invoice_id', 'id')
|
||||||
serializer_class = ProductSerializer
|
serializer_class = ProductSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['invoice', 'designation', 'quantity', 'amount', ]
|
filterset_fields = ['invoice', 'designation', 'quantity', 'amount', ]
|
||||||
search_fields = ['$designation', '$invoice__object', ]
|
search_fields = ['$designation', '$invoice__object', ]
|
||||||
|
|
||||||
@ -44,7 +45,7 @@ class RemittanceTypeViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = RemittanceType.objects.order_by('id')
|
queryset = RemittanceType.objects.order_by('id')
|
||||||
serializer_class = RemittanceTypeSerializer
|
serializer_class = RemittanceTypeSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['note', ]
|
filterset_fields = ['note', ]
|
||||||
search_fields = ['$note__special_type', ]
|
search_fields = ['$note__special_type', ]
|
||||||
|
|
||||||
@ -57,7 +58,7 @@ class RemittanceViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Remittance.objects.order_by('id')
|
queryset = Remittance.objects.order_by('id')
|
||||||
serializer_class = RemittanceSerializer
|
serializer_class = RemittanceSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['date', 'remittance_type', 'comment', 'closed', 'transaction_proxies__transaction', ]
|
filterset_fields = ['date', 'remittance_type', 'comment', 'closed', 'transaction_proxies__transaction', ]
|
||||||
search_fields = ['$remittance_type__note__special_type', '$comment', ]
|
search_fields = ['$remittance_type__note__special_type', '$comment', ]
|
||||||
|
|
||||||
@ -70,7 +71,7 @@ class SogeCreditViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = SogeCredit.objects.order_by('id')
|
queryset = SogeCredit.objects.order_by('id')
|
||||||
serializer_class = SogeCreditSerializer
|
serializer_class = SogeCreditSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['user', 'user__last_name', 'user__first_name', 'user__email', 'user__note__alias__name',
|
filterset_fields = ['user', 'user__last_name', 'user__first_name', 'user__email', 'user__note__alias__name',
|
||||||
'user__note__alias__normalized_name', 'transactions', 'credit_transaction', ]
|
'user__note__alias__normalized_name', 'transactions', 'credit_transaction', ]
|
||||||
search_fields = ['$user__last_name', '$user__first_name', '$user__email', '$user__note__alias__name',
|
search_fields = ['$user__last_name', '$user__first_name', '$user__email', '$user__note__alias__name',
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
from rest_framework.filters import OrderingFilter
|
||||||
|
|
||||||
|
from api.filters import RegexSafeSearchFilter
|
||||||
from api.viewsets import ReadProtectedModelViewSet
|
from api.viewsets import ReadProtectedModelViewSet
|
||||||
|
|
||||||
from .serializers import WEIClubSerializer, BusSerializer, BusTeamSerializer, WEIRoleSerializer, \
|
from .serializers import WEIClubSerializer, BusSerializer, BusTeamSerializer, WEIRoleSerializer, \
|
||||||
@ -18,7 +20,7 @@ class WEIClubViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = WEIClub.objects.order_by('id')
|
queryset = WEIClub.objects.order_by('id')
|
||||||
serializer_class = WEIClubSerializer
|
serializer_class = WEIClubSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['name', 'year', 'date_start', 'date_end', 'email', 'note__alias__name',
|
filterset_fields = ['name', 'year', 'date_start', 'date_end', 'email', 'note__alias__name',
|
||||||
'note__alias__normalized_name', 'parent_club', 'parent_club__name', 'require_memberships',
|
'note__alias__normalized_name', 'parent_club', 'parent_club__name', 'require_memberships',
|
||||||
'membership_fee_paid', 'membership_fee_unpaid', 'membership_duration', 'membership_start',
|
'membership_fee_paid', 'membership_fee_unpaid', 'membership_duration', 'membership_start',
|
||||||
@ -34,7 +36,7 @@ class BusViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Bus.objects.order_by('id')
|
queryset = Bus.objects.order_by('id')
|
||||||
serializer_class = BusSerializer
|
serializer_class = BusSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['name', 'wei', 'description', ]
|
filterset_fields = ['name', 'wei', 'description', ]
|
||||||
search_fields = ['$name', '$wei__name', '$description', ]
|
search_fields = ['$name', '$wei__name', '$description', ]
|
||||||
|
|
||||||
@ -47,7 +49,7 @@ class BusTeamViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = BusTeam.objects.order_by('id')
|
queryset = BusTeam.objects.order_by('id')
|
||||||
serializer_class = BusTeamSerializer
|
serializer_class = BusTeamSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['name', 'bus', 'color', 'description', 'bus__wei', ]
|
filterset_fields = ['name', 'bus', 'color', 'description', 'bus__wei', ]
|
||||||
search_fields = ['$name', '$bus__name', '$bus__wei__name', '$description', ]
|
search_fields = ['$name', '$bus__name', '$bus__wei__name', '$description', ]
|
||||||
|
|
||||||
@ -60,7 +62,7 @@ class WEIRoleViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = WEIRole.objects.order_by('id')
|
queryset = WEIRole.objects.order_by('id')
|
||||||
serializer_class = WEIRoleSerializer
|
serializer_class = WEIRoleSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['name', 'permissions', 'memberships', ]
|
filterset_fields = ['name', 'permissions', 'memberships', ]
|
||||||
search_fields = ['$name', ]
|
search_fields = ['$name', ]
|
||||||
|
|
||||||
@ -73,7 +75,7 @@ class WEIRegistrationViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = WEIRegistration.objects.order_by('id')
|
queryset = WEIRegistration.objects.order_by('id')
|
||||||
serializer_class = WEIRegistrationSerializer
|
serializer_class = WEIRegistrationSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['user', 'user__username', 'user__first_name', 'user__last_name', 'user__email',
|
filterset_fields = ['user', 'user__username', 'user__first_name', 'user__last_name', 'user__email',
|
||||||
'user__note__alias__name', 'user__note__alias__normalized_name', 'wei', 'wei__name',
|
'user__note__alias__name', 'user__note__alias__normalized_name', 'wei', 'wei__name',
|
||||||
'wei__email', 'wei__year', 'soge_credit', 'caution_check', 'birth_date', 'gender',
|
'wei__email', 'wei__year', 'soge_credit', 'caution_check', 'birth_date', 'gender',
|
||||||
@ -92,7 +94,7 @@ class WEIMembershipViewSet(ReadProtectedModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = WEIMembership.objects.order_by('id')
|
queryset = WEIMembership.objects.order_by('id')
|
||||||
serializer_class = WEIMembershipSerializer
|
serializer_class = WEIMembershipSerializer
|
||||||
filter_backends = [DjangoFilterBackend, OrderingFilter, SearchFilter]
|
filter_backends = [DjangoFilterBackend, OrderingFilter, RegexSafeSearchFilter]
|
||||||
filterset_fields = ['club__name', 'club__email', 'club__note__alias__name',
|
filterset_fields = ['club__name', 'club__email', 'club__note__alias__name',
|
||||||
'club__note__alias__normalized_name', 'user__username', 'user__last_name',
|
'club__note__alias__normalized_name', 'user__username', 'user__last_name',
|
||||||
'user__first_name', 'user__email', 'user__note__alias__name',
|
'user__first_name', 'user__email', 'user__note__alias__name',
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -7,16 +7,16 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2022-04-10 22:34+0200\n"
|
"POT-Creation-Date: 2021-10-07 22:55+0200\n"
|
||||||
"PO-Revision-Date: 2022-04-11 22:05+0200\n"
|
"PO-Revision-Date: 2020-11-16 20:02+0000\n"
|
||||||
"Last-Translator: elkmaennchen <elkmaennchen@crans.org>\n"
|
"Last-Translator: Yohann D'ANELLO <ynerant@crans.org>\n"
|
||||||
"Language-Team: French <http://translate.ynerant.fr/projects/nk20/nk20/fr/>\n"
|
"Language-Team: French <http://translate.ynerant.fr/projects/nk20/nk20/fr/>\n"
|
||||||
"Language: fr\n"
|
"Language: fr\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=n > 1;\n"
|
"Plural-Forms: nplurals=2; plural=n > 1;\n"
|
||||||
"X-Generator: Poedit 3.0\n"
|
"X-Generator: Weblate 4.3.2\n"
|
||||||
|
|
||||||
#: apps/activity/apps.py:10 apps/activity/models.py:151
|
#: apps/activity/apps.py:10 apps/activity/models.py:151
|
||||||
#: apps/activity/models.py:167
|
#: apps/activity/models.py:167
|
||||||
@ -56,7 +56,7 @@ msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité."
|
|||||||
#: apps/member/models.py:199
|
#: apps/member/models.py:199
|
||||||
#: apps/member/templates/member/includes/club_info.html:4
|
#: apps/member/templates/member/includes/club_info.html:4
|
||||||
#: apps/member/templates/member/includes/profile_info.html:4
|
#: apps/member/templates/member/includes/profile_info.html:4
|
||||||
#: apps/note/models/notes.py:263 apps/note/models/transactions.py:26
|
#: apps/note/models/notes.py:231 apps/note/models/transactions.py:26
|
||||||
#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:301
|
#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:301
|
||||||
#: apps/permission/models.py:330
|
#: apps/permission/models.py:330
|
||||||
#: apps/registration/templates/registration/future_profile_detail.html:16
|
#: apps/registration/templates/registration/future_profile_detail.html:16
|
||||||
@ -114,7 +114,7 @@ msgstr "Lieu où l'activité est organisée, par exemple la Kfet."
|
|||||||
msgid "type"
|
msgid "type"
|
||||||
msgstr "type"
|
msgstr "type"
|
||||||
|
|
||||||
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:307
|
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:305
|
||||||
#: apps/note/models/notes.py:148 apps/treasury/models.py:285
|
#: apps/note/models/notes.py:148 apps/treasury/models.py:285
|
||||||
#: apps/wei/models.py:173 apps/wei/templates/wei/attribute_bus_1A.html:13
|
#: apps/wei/models.py:173 apps/wei/templates/wei/attribute_bus_1A.html:13
|
||||||
#: apps/wei/templates/wei/survey.html:15
|
#: apps/wei/templates/wei/survey.html:15
|
||||||
@ -295,7 +295,7 @@ msgstr "Invité supprimé"
|
|||||||
#: apps/note/models/transactions.py:257
|
#: apps/note/models/transactions.py:257
|
||||||
#: apps/note/templates/note/transaction_form.html:17
|
#: apps/note/templates/note/transaction_form.html:17
|
||||||
#: apps/note/templates/note/transaction_form.html:152
|
#: apps/note/templates/note/transaction_form.html:152
|
||||||
#: note_kfet/templates/base.html:72
|
#: note_kfet/templates/base.html:73
|
||||||
msgid "Transfer"
|
msgid "Transfer"
|
||||||
msgstr "Virement"
|
msgstr "Virement"
|
||||||
|
|
||||||
@ -388,7 +388,7 @@ msgid "validate"
|
|||||||
msgstr "valider"
|
msgstr "valider"
|
||||||
|
|
||||||
#: apps/activity/templates/activity/includes/activity_info.html:71
|
#: apps/activity/templates/activity/includes/activity_info.html:71
|
||||||
#: apps/logs/models.py:64 apps/note/tables.py:220
|
#: apps/logs/models.py:64 apps/note/tables.py:195
|
||||||
msgid "edit"
|
msgid "edit"
|
||||||
msgstr "modifier"
|
msgstr "modifier"
|
||||||
|
|
||||||
@ -400,7 +400,7 @@ msgstr "Inviter"
|
|||||||
msgid "Create new activity"
|
msgid "Create new activity"
|
||||||
msgstr "Créer une nouvelle activité"
|
msgstr "Créer une nouvelle activité"
|
||||||
|
|
||||||
#: apps/activity/views.py:67 note_kfet/templates/base.html:90
|
#: apps/activity/views.py:67 note_kfet/templates/base.html:91
|
||||||
msgid "Activities"
|
msgid "Activities"
|
||||||
msgstr "Activités"
|
msgstr "Activités"
|
||||||
|
|
||||||
@ -466,9 +466,9 @@ msgstr "nouvelles données"
|
|||||||
msgid "create"
|
msgid "create"
|
||||||
msgstr "créer"
|
msgstr "créer"
|
||||||
|
|
||||||
#: apps/logs/models.py:65 apps/note/tables.py:166 apps/note/tables.py:190
|
#: apps/logs/models.py:65 apps/note/tables.py:165 apps/note/tables.py:211
|
||||||
#: apps/note/tables.py:237 apps/permission/models.py:127
|
#: apps/permission/models.py:127 apps/treasury/tables.py:38
|
||||||
#: apps/treasury/tables.py:38 apps/wei/tables.py:74
|
#: apps/wei/tables.py:74
|
||||||
msgid "delete"
|
msgid "delete"
|
||||||
msgstr "supprimer"
|
msgstr "supprimer"
|
||||||
|
|
||||||
@ -507,11 +507,11 @@ msgstr "cotisation pour adhérer (normalien élève)"
|
|||||||
msgid "membership fee (unpaid students)"
|
msgid "membership fee (unpaid students)"
|
||||||
msgstr "cotisation pour adhérer (normalien étudiant)"
|
msgstr "cotisation pour adhérer (normalien étudiant)"
|
||||||
|
|
||||||
#: apps/member/admin.py:65 apps/member/models.py:319
|
#: apps/member/admin.py:65 apps/member/models.py:317
|
||||||
msgid "roles"
|
msgid "roles"
|
||||||
msgstr "rôles"
|
msgstr "rôles"
|
||||||
|
|
||||||
#: apps/member/admin.py:66 apps/member/models.py:333
|
#: apps/member/admin.py:66 apps/member/models.py:331
|
||||||
msgid "fee"
|
msgid "fee"
|
||||||
msgstr "cotisation"
|
msgstr "cotisation"
|
||||||
|
|
||||||
@ -547,7 +547,7 @@ msgstr "Taille maximale : 2 Mo"
|
|||||||
msgid "This image cannot be loaded."
|
msgid "This image cannot be loaded."
|
||||||
msgstr "Cette image ne peut pas être chargée."
|
msgstr "Cette image ne peut pas être chargée."
|
||||||
|
|
||||||
#: apps/member/forms.py:141 apps/member/views.py:103
|
#: apps/member/forms.py:141 apps/member/views.py:102
|
||||||
#: apps/registration/forms.py:33 apps/registration/views.py:262
|
#: apps/registration/forms.py:33 apps/registration/views.py:262
|
||||||
msgid "An alias with a similar name already exists."
|
msgid "An alias with a similar name already exists."
|
||||||
msgstr "Un alias avec un nom similaire existe déjà."
|
msgstr "Un alias avec un nom similaire existe déjà."
|
||||||
@ -610,14 +610,14 @@ msgid "hash"
|
|||||||
msgstr "haché"
|
msgstr "haché"
|
||||||
|
|
||||||
#: apps/member/models.py:38
|
#: apps/member/models.py:38
|
||||||
#: apps/member/templates/member/includes/profile_info.html:43
|
#: apps/member/templates/member/includes/profile_info.html:35
|
||||||
#: apps/registration/templates/registration/future_profile_detail.html:40
|
#: apps/registration/templates/registration/future_profile_detail.html:40
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:44
|
#: apps/wei/templates/wei/weimembership_form.html:44
|
||||||
msgid "phone number"
|
msgid "phone number"
|
||||||
msgstr "numéro de téléphone"
|
msgstr "numéro de téléphone"
|
||||||
|
|
||||||
#: apps/member/models.py:45
|
#: apps/member/models.py:45
|
||||||
#: apps/member/templates/member/includes/profile_info.html:37
|
#: apps/member/templates/member/includes/profile_info.html:29
|
||||||
#: apps/registration/templates/registration/future_profile_detail.html:34
|
#: apps/registration/templates/registration/future_profile_detail.html:34
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:38
|
#: apps/wei/templates/wei/weimembership_form.html:38
|
||||||
msgid "section"
|
msgid "section"
|
||||||
@ -705,14 +705,14 @@ msgid "Year of entry to the school (None if not ENS student)"
|
|||||||
msgstr "Année d'entrée dans l'école (None si non-étudiant·e de l'ENS)"
|
msgstr "Année d'entrée dans l'école (None si non-étudiant·e de l'ENS)"
|
||||||
|
|
||||||
#: apps/member/models.py:83
|
#: apps/member/models.py:83
|
||||||
#: apps/member/templates/member/includes/profile_info.html:47
|
#: apps/member/templates/member/includes/profile_info.html:39
|
||||||
#: apps/registration/templates/registration/future_profile_detail.html:37
|
#: apps/registration/templates/registration/future_profile_detail.html:37
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:41
|
#: apps/wei/templates/wei/weimembership_form.html:41
|
||||||
msgid "address"
|
msgid "address"
|
||||||
msgstr "adresse"
|
msgstr "adresse"
|
||||||
|
|
||||||
#: apps/member/models.py:90
|
#: apps/member/models.py:90
|
||||||
#: apps/member/templates/member/includes/profile_info.html:50
|
#: apps/member/templates/member/includes/profile_info.html:42
|
||||||
#: apps/registration/templates/registration/future_profile_detail.html:43
|
#: apps/registration/templates/registration/future_profile_detail.html:43
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:47
|
#: apps/wei/templates/wei/weimembership_form.html:47
|
||||||
msgid "paid"
|
msgid "paid"
|
||||||
@ -784,7 +784,7 @@ msgstr "Activez votre compte Note Kfet"
|
|||||||
|
|
||||||
#: apps/member/models.py:204
|
#: apps/member/models.py:204
|
||||||
#: apps/member/templates/member/includes/club_info.html:55
|
#: apps/member/templates/member/includes/club_info.html:55
|
||||||
#: apps/member/templates/member/includes/profile_info.html:40
|
#: apps/member/templates/member/includes/profile_info.html:32
|
||||||
#: apps/registration/templates/registration/future_profile_detail.html:22
|
#: apps/registration/templates/registration/future_profile_detail.html:22
|
||||||
#: apps/wei/templates/wei/base.html:70
|
#: apps/wei/templates/wei/base.html:70
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:20
|
#: apps/wei/templates/wei/weimembership_form.html:20
|
||||||
@ -833,46 +833,46 @@ msgstr ""
|
|||||||
"Date maximale d'une fin d'adhésion, après laquelle les adhérents doivent la "
|
"Date maximale d'une fin d'adhésion, après laquelle les adhérents doivent la "
|
||||||
"renouveler."
|
"renouveler."
|
||||||
|
|
||||||
#: apps/member/models.py:288 apps/member/models.py:313
|
#: apps/member/models.py:286 apps/member/models.py:311
|
||||||
#: apps/note/models/notes.py:176
|
#: apps/note/models/notes.py:176
|
||||||
msgid "club"
|
msgid "club"
|
||||||
msgstr "club"
|
msgstr "club"
|
||||||
|
|
||||||
#: apps/member/models.py:289
|
#: apps/member/models.py:287
|
||||||
msgid "clubs"
|
msgid "clubs"
|
||||||
msgstr "clubs"
|
msgstr "clubs"
|
||||||
|
|
||||||
#: apps/member/models.py:324
|
#: apps/member/models.py:322
|
||||||
msgid "membership starts on"
|
msgid "membership starts on"
|
||||||
msgstr "l'adhésion commence le"
|
msgstr "l'adhésion commence le"
|
||||||
|
|
||||||
#: apps/member/models.py:328
|
#: apps/member/models.py:326
|
||||||
msgid "membership ends on"
|
msgid "membership ends on"
|
||||||
msgstr "l'adhésion finit le"
|
msgstr "l'adhésion finit le"
|
||||||
|
|
||||||
#: apps/member/models.py:430
|
#: apps/member/models.py:428
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "The role {role} does not apply to the club {club}."
|
msgid "The role {role} does not apply to the club {club}."
|
||||||
msgstr "Le rôle {role} ne s'applique pas au club {club}."
|
msgstr "Le rôle {role} ne s'applique pas au club {club}."
|
||||||
|
|
||||||
#: apps/member/models.py:439 apps/member/views.py:712
|
#: apps/member/models.py:437 apps/member/views.py:651
|
||||||
msgid "User is already a member of the club"
|
msgid "User is already a member of the club"
|
||||||
msgstr "L'utilisateur est déjà membre du club"
|
msgstr "L'utilisateur est déjà membre du club"
|
||||||
|
|
||||||
#: apps/member/models.py:451 apps/member/views.py:721
|
#: apps/member/models.py:449 apps/member/views.py:660
|
||||||
msgid "User is not a member of the parent club"
|
msgid "User is not a member of the parent club"
|
||||||
msgstr "L'utilisateur n'est pas membre du club parent"
|
msgstr "L'utilisateur n'est pas membre du club parent"
|
||||||
|
|
||||||
#: apps/member/models.py:504
|
#: apps/member/models.py:502
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Membership of {user} for the club {club}"
|
msgid "Membership of {user} for the club {club}"
|
||||||
msgstr "Adhésion de {user} pour le club {club}"
|
msgstr "Adhésion de {user} pour le club {club}"
|
||||||
|
|
||||||
#: apps/member/models.py:507 apps/note/models/transactions.py:389
|
#: apps/member/models.py:505 apps/note/models/transactions.py:389
|
||||||
msgid "membership"
|
msgid "membership"
|
||||||
msgstr "adhésion"
|
msgstr "adhésion"
|
||||||
|
|
||||||
#: apps/member/models.py:508
|
#: apps/member/models.py:506
|
||||||
msgid "memberships"
|
msgid "memberships"
|
||||||
msgstr "adhésions"
|
msgstr "adhésions"
|
||||||
|
|
||||||
@ -924,7 +924,7 @@ msgid "Account #"
|
|||||||
msgstr "Compte n°"
|
msgstr "Compte n°"
|
||||||
|
|
||||||
#: apps/member/templates/member/base.html:48
|
#: apps/member/templates/member/base.html:48
|
||||||
#: apps/member/templates/member/base.html:62 apps/member/views.py:60
|
#: apps/member/templates/member/base.html:62 apps/member/views.py:59
|
||||||
#: apps/registration/templates/registration/future_profile_detail.html:48
|
#: apps/registration/templates/registration/future_profile_detail.html:48
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:117
|
#: apps/wei/templates/wei/weimembership_form.html:117
|
||||||
msgid "Update Profile"
|
msgid "Update Profile"
|
||||||
@ -985,14 +985,13 @@ msgstr ""
|
|||||||
"seront à nouveau possible."
|
"seront à nouveau possible."
|
||||||
|
|
||||||
#: apps/member/templates/member/club_alias.html:10
|
#: apps/member/templates/member/club_alias.html:10
|
||||||
#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:287
|
#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:253
|
||||||
#: apps/member/views.py:517
|
#: apps/member/views.py:456
|
||||||
msgid "Note aliases"
|
msgid "Note aliases"
|
||||||
msgstr "Alias de la note"
|
msgstr "Alias de la note"
|
||||||
|
|
||||||
#: apps/member/templates/member/club_alias.html:20
|
#: apps/member/templates/member/club_alias.html:20
|
||||||
#: apps/member/templates/member/profile_alias.html:19
|
#: apps/member/templates/member/profile_alias.html:19
|
||||||
#: apps/member/templates/member/profile_trust.html:19
|
|
||||||
#: apps/treasury/tables.py:99
|
#: apps/treasury/tables.py:99
|
||||||
#: apps/treasury/templates/treasury/sogecredit_list.html:34
|
#: apps/treasury/templates/treasury/sogecredit_list.html:34
|
||||||
#: apps/treasury/templates/treasury/sogecredit_list.html:73
|
#: apps/treasury/templates/treasury/sogecredit_list.html:73
|
||||||
@ -1045,7 +1044,7 @@ msgid "membership fee"
|
|||||||
msgstr "cotisation pour adhérer"
|
msgstr "cotisation pour adhérer"
|
||||||
|
|
||||||
#: apps/member/templates/member/includes/club_info.html:43
|
#: apps/member/templates/member/includes/club_info.html:43
|
||||||
#: apps/member/templates/member/includes/profile_info.html:55
|
#: apps/member/templates/member/includes/profile_info.html:47
|
||||||
#: apps/treasury/templates/treasury/sogecredit_detail.html:24
|
#: apps/treasury/templates/treasury/sogecredit_detail.html:24
|
||||||
#: apps/wei/templates/wei/base.html:60
|
#: apps/wei/templates/wei/base.html:60
|
||||||
msgid "balance"
|
msgid "balance"
|
||||||
@ -1053,7 +1052,7 @@ msgstr "solde du compte"
|
|||||||
|
|
||||||
#: apps/member/templates/member/includes/club_info.html:47
|
#: apps/member/templates/member/includes/club_info.html:47
|
||||||
#: apps/member/templates/member/includes/profile_info.html:20
|
#: apps/member/templates/member/includes/profile_info.html:20
|
||||||
#: apps/note/models/notes.py:287 apps/wei/templates/wei/base.html:66
|
#: apps/note/models/notes.py:255 apps/wei/templates/wei/base.html:66
|
||||||
msgid "aliases"
|
msgid "aliases"
|
||||||
msgstr "alias"
|
msgstr "alias"
|
||||||
|
|
||||||
@ -1077,16 +1076,7 @@ msgstr "mot de passe"
|
|||||||
msgid "Change password"
|
msgid "Change password"
|
||||||
msgstr "Changer le mot de passe"
|
msgstr "Changer le mot de passe"
|
||||||
|
|
||||||
#: apps/member/templates/member/includes/profile_info.html:28
|
#: apps/member/templates/member/includes/profile_info.html:55
|
||||||
#: apps/note/models/notes.py:244
|
|
||||||
msgid "friendships"
|
|
||||||
msgstr "amitiés"
|
|
||||||
|
|
||||||
#: apps/member/templates/member/includes/profile_info.html:32
|
|
||||||
msgid "Manage friendships"
|
|
||||||
msgstr "Gérer les amitiés"
|
|
||||||
|
|
||||||
#: apps/member/templates/member/includes/profile_info.html:63
|
|
||||||
msgid "API token"
|
msgid "API token"
|
||||||
msgstr "Accès API"
|
msgstr "Accès API"
|
||||||
|
|
||||||
@ -1158,23 +1148,6 @@ msgstr "Cliquez ici pour renvoyer un lien de validation."
|
|||||||
msgid "View my memberships"
|
msgid "View my memberships"
|
||||||
msgstr "Voir mes adhésions"
|
msgstr "Voir mes adhésions"
|
||||||
|
|
||||||
#: apps/member/templates/member/profile_trust.html:10 apps/member/views.py:254
|
|
||||||
msgid "Note friendships"
|
|
||||||
msgstr "Amitiés note"
|
|
||||||
|
|
||||||
#: apps/member/templates/member/profile_trust.html:28
|
|
||||||
msgid ""
|
|
||||||
"Adding someone as a friend enables them to initiate transactions coming from "
|
|
||||||
"your account (while keeping your balance positive). This is designed to "
|
|
||||||
"simplify using note kfet transfers to transfer money between users. The "
|
|
||||||
"intent is that one person can make all transfers for a group of friends "
|
|
||||||
"without needing additional rights among them."
|
|
||||||
msgstr ""
|
|
||||||
"Ajouter quelqu'un⋅e en ami⋅e lui permet de me prélever de l'argent (tant que "
|
|
||||||
"ma note reste positive). Ceci sert à simplifier les remboursements entre "
|
|
||||||
"ami⋅es via note. En effet, une personne peut effectuer tous les transferts "
|
|
||||||
"sans posséder de droits supplémentaires."
|
|
||||||
|
|
||||||
#: apps/member/templates/member/profile_update.html:18
|
#: apps/member/templates/member/profile_update.html:18
|
||||||
msgid "Save Changes"
|
msgid "Save Changes"
|
||||||
msgstr "Sauvegarder les changements"
|
msgstr "Sauvegarder les changements"
|
||||||
@ -1183,47 +1156,47 @@ msgstr "Sauvegarder les changements"
|
|||||||
msgid "Registrations"
|
msgid "Registrations"
|
||||||
msgstr "Inscriptions"
|
msgstr "Inscriptions"
|
||||||
|
|
||||||
#: apps/member/views.py:73 apps/registration/forms.py:23
|
#: apps/member/views.py:72 apps/registration/forms.py:23
|
||||||
msgid "This address must be valid."
|
msgid "This address must be valid."
|
||||||
msgstr "Cette adresse doit être valide."
|
msgstr "Cette adresse doit être valide."
|
||||||
|
|
||||||
#: apps/member/views.py:140
|
#: apps/member/views.py:139
|
||||||
msgid "Profile detail"
|
msgid "Profile detail"
|
||||||
msgstr "Détails de l'utilisateur"
|
msgstr "Détails de l'utilisateur"
|
||||||
|
|
||||||
#: apps/member/views.py:206
|
#: apps/member/views.py:205
|
||||||
msgid "Search user"
|
msgid "Search user"
|
||||||
msgstr "Chercher un utilisateur"
|
msgstr "Chercher un utilisateur"
|
||||||
|
|
||||||
#: apps/member/views.py:308
|
#: apps/member/views.py:273
|
||||||
msgid "Update note picture"
|
msgid "Update note picture"
|
||||||
msgstr "Modifier la photo de la note"
|
msgstr "Modifier la photo de la note"
|
||||||
|
|
||||||
#: apps/member/views.py:354
|
#: apps/member/views.py:319
|
||||||
msgid "Manage auth token"
|
msgid "Manage auth token"
|
||||||
msgstr "Gérer les jetons d'authentification"
|
msgstr "Gérer les jetons d'authentification"
|
||||||
|
|
||||||
#: apps/member/views.py:381
|
#: apps/member/views.py:346
|
||||||
msgid "Create new club"
|
msgid "Create new club"
|
||||||
msgstr "Créer un nouveau club"
|
msgstr "Créer un nouveau club"
|
||||||
|
|
||||||
#: apps/member/views.py:400
|
#: apps/member/views.py:365
|
||||||
msgid "Search club"
|
msgid "Search club"
|
||||||
msgstr "Chercher un club"
|
msgstr "Chercher un club"
|
||||||
|
|
||||||
#: apps/member/views.py:433
|
#: apps/member/views.py:398
|
||||||
msgid "Club detail"
|
msgid "Club detail"
|
||||||
msgstr "Détails du club"
|
msgstr "Détails du club"
|
||||||
|
|
||||||
#: apps/member/views.py:540
|
#: apps/member/views.py:479
|
||||||
msgid "Update club"
|
msgid "Update club"
|
||||||
msgstr "Modifier le club"
|
msgstr "Modifier le club"
|
||||||
|
|
||||||
#: apps/member/views.py:574
|
#: apps/member/views.py:513
|
||||||
msgid "Add new member to the club"
|
msgid "Add new member to the club"
|
||||||
msgstr "Ajouter un nouveau membre au club"
|
msgstr "Ajouter un nouveau membre au club"
|
||||||
|
|
||||||
#: apps/member/views.py:703 apps/wei/views.py:973
|
#: apps/member/views.py:642 apps/wei/views.py:973
|
||||||
msgid ""
|
msgid ""
|
||||||
"This user don't have enough money to join this club, and can't have a "
|
"This user don't have enough money to join this club, and can't have a "
|
||||||
"negative balance."
|
"negative balance."
|
||||||
@ -1231,19 +1204,19 @@ msgstr ""
|
|||||||
"Cet utilisateur n'a pas assez d'argent pour rejoindre ce club et ne peut pas "
|
"Cet utilisateur n'a pas assez d'argent pour rejoindre ce club et ne peut pas "
|
||||||
"avoir un solde négatif."
|
"avoir un solde négatif."
|
||||||
|
|
||||||
#: apps/member/views.py:725
|
#: apps/member/views.py:664
|
||||||
msgid "The membership must start after {:%m-%d-%Y}."
|
msgid "The membership must start after {:%m-%d-%Y}."
|
||||||
msgstr "L'adhésion doit commencer après le {:%d/%m/%Y}."
|
msgstr "L'adhésion doit commencer après le {:%d/%m/%Y}."
|
||||||
|
|
||||||
#: apps/member/views.py:730
|
#: apps/member/views.py:669
|
||||||
msgid "The membership must begin before {:%m-%d-%Y}."
|
msgid "The membership must begin before {:%m-%d-%Y}."
|
||||||
msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}."
|
msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}."
|
||||||
|
|
||||||
#: apps/member/views.py:876
|
#: apps/member/views.py:815
|
||||||
msgid "Manage roles of an user in the club"
|
msgid "Manage roles of an user in the club"
|
||||||
msgstr "Gérer les rôles d'un utilisateur dans le club"
|
msgstr "Gérer les rôles d'un utilisateur dans le club"
|
||||||
|
|
||||||
#: apps/member/views.py:901
|
#: apps/member/views.py:840
|
||||||
msgid "Members of the club"
|
msgid "Members of the club"
|
||||||
msgstr "Membres du club"
|
msgstr "Membres du club"
|
||||||
|
|
||||||
@ -1261,7 +1234,7 @@ msgstr "destination"
|
|||||||
msgid "amount"
|
msgid "amount"
|
||||||
msgstr "montant"
|
msgstr "montant"
|
||||||
|
|
||||||
#: apps/note/api/serializers.py:199 apps/note/api/serializers.py:205
|
#: apps/note/api/serializers.py:183 apps/note/api/serializers.py:189
|
||||||
#: apps/note/models/transactions.py:228
|
#: apps/note/models/transactions.py:228
|
||||||
msgid ""
|
msgid ""
|
||||||
"The transaction can't be saved since the source note or the destination note "
|
"The transaction can't be saved since the source note or the destination note "
|
||||||
@ -1393,47 +1366,30 @@ msgstr "note spéciale"
|
|||||||
msgid "special notes"
|
msgid "special notes"
|
||||||
msgstr "notes spéciales"
|
msgstr "notes spéciales"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:232
|
#: apps/note/models/notes.py:237
|
||||||
msgid "trusting"
|
|
||||||
msgstr "note"
|
|
||||||
|
|
||||||
#: apps/note/models/notes.py:239
|
|
||||||
msgid "trusted"
|
|
||||||
msgstr "ami"
|
|
||||||
|
|
||||||
#: apps/note/models/notes.py:243
|
|
||||||
msgid "frienship"
|
|
||||||
msgstr "amitié"
|
|
||||||
|
|
||||||
#: apps/note/models/notes.py:248
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Friendship between {trusting} and {trusted}"
|
|
||||||
msgstr "Amitié entre {trusting} et {trusted}"
|
|
||||||
|
|
||||||
#: apps/note/models/notes.py:269
|
|
||||||
msgid "Invalid alias"
|
msgid "Invalid alias"
|
||||||
msgstr "Alias invalide"
|
msgstr "Alias invalide"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:286
|
#: apps/note/models/notes.py:254
|
||||||
msgid "alias"
|
msgid "alias"
|
||||||
msgstr "alias"
|
msgstr "alias"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:310
|
#: apps/note/models/notes.py:278
|
||||||
msgid "Alias is too long."
|
msgid "Alias is too long."
|
||||||
msgstr "L'alias est trop long."
|
msgstr "L'alias est trop long."
|
||||||
|
|
||||||
#: apps/note/models/notes.py:313
|
#: apps/note/models/notes.py:281
|
||||||
msgid ""
|
msgid ""
|
||||||
"This alias contains only complex character. Please use a more simple alias."
|
"This alias contains only complex character. Please use a more simple alias."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Cet alias ne contient que des caractères complexes. Merci d'utiliser un "
|
"Cet alias ne contient que des caractères complexes. Merci d'utiliser un "
|
||||||
"alias plus simple."
|
"alias plus simple."
|
||||||
|
|
||||||
#: apps/note/models/notes.py:317
|
#: apps/note/models/notes.py:285
|
||||||
msgid "An alias with a similar name already exists: {} "
|
msgid "An alias with a similar name already exists: {} "
|
||||||
msgstr "Un alias avec un nom similaire existe déjà : {} "
|
msgstr "Un alias avec un nom similaire existe déjà : {} "
|
||||||
|
|
||||||
#: apps/note/models/notes.py:331
|
#: apps/note/models/notes.py:299
|
||||||
msgid "You can't delete your main alias."
|
msgid "You can't delete your main alias."
|
||||||
msgstr "Vous ne pouvez pas supprimer votre alias principal."
|
msgstr "Vous ne pouvez pas supprimer votre alias principal."
|
||||||
|
|
||||||
@ -1579,8 +1535,7 @@ msgstr "Cliquez pour valider"
|
|||||||
msgid "No reason specified"
|
msgid "No reason specified"
|
||||||
msgstr "Pas de motif spécifié"
|
msgstr "Pas de motif spécifié"
|
||||||
|
|
||||||
#: apps/note/tables.py:173 apps/note/tables.py:194 apps/note/tables.py:239
|
#: apps/note/tables.py:169 apps/note/tables.py:213 apps/treasury/tables.py:39
|
||||||
#: apps/treasury/tables.py:39
|
|
||||||
#: apps/treasury/templates/treasury/invoice_confirm_delete.html:30
|
#: apps/treasury/templates/treasury/invoice_confirm_delete.html:30
|
||||||
#: apps/treasury/templates/treasury/sogecredit_detail.html:65
|
#: apps/treasury/templates/treasury/sogecredit_detail.html:65
|
||||||
#: apps/wei/tables.py:75 apps/wei/tables.py:118
|
#: apps/wei/tables.py:75 apps/wei/tables.py:118
|
||||||
@ -1591,7 +1546,7 @@ msgstr "Pas de motif spécifié"
|
|||||||
msgid "Delete"
|
msgid "Delete"
|
||||||
msgstr "Supprimer"
|
msgstr "Supprimer"
|
||||||
|
|
||||||
#: apps/note/tables.py:222 apps/note/templates/note/conso_form.html:132
|
#: apps/note/tables.py:197 apps/note/templates/note/conso_form.html:132
|
||||||
#: apps/wei/tables.py:49 apps/wei/tables.py:50
|
#: apps/wei/tables.py:49 apps/wei/tables.py:50
|
||||||
#: apps/wei/templates/wei/base.html:89
|
#: apps/wei/templates/wei/base.html:89
|
||||||
#: apps/wei/templates/wei/bus_detail.html:20
|
#: apps/wei/templates/wei/bus_detail.html:20
|
||||||
@ -1601,7 +1556,7 @@ msgstr "Supprimer"
|
|||||||
msgid "Edit"
|
msgid "Edit"
|
||||||
msgstr "Éditer"
|
msgstr "Éditer"
|
||||||
|
|
||||||
#: apps/note/tables.py:226 apps/note/tables.py:253
|
#: apps/note/tables.py:201 apps/note/tables.py:224
|
||||||
msgid "Hide/Show"
|
msgid "Hide/Show"
|
||||||
msgstr "Afficher/Masquer"
|
msgstr "Afficher/Masquer"
|
||||||
|
|
||||||
@ -1762,7 +1717,7 @@ msgstr "Chercher un bouton"
|
|||||||
msgid "Update button"
|
msgid "Update button"
|
||||||
msgstr "Modifier le bouton"
|
msgstr "Modifier le bouton"
|
||||||
|
|
||||||
#: apps/note/views.py:151 note_kfet/templates/base.html:66
|
#: apps/note/views.py:151 note_kfet/templates/base.html:67
|
||||||
msgid "Consumptions"
|
msgid "Consumptions"
|
||||||
msgstr "Consommations"
|
msgstr "Consommations"
|
||||||
|
|
||||||
@ -1960,7 +1915,7 @@ msgstr ""
|
|||||||
"Vous n'avez pas la permission d'ajouter une instance du modèle « {model} » "
|
"Vous n'avez pas la permission d'ajouter une instance du modèle « {model} » "
|
||||||
"avec ces paramètres. Merci de les corriger et de réessayer."
|
"avec ces paramètres. Merci de les corriger et de réessayer."
|
||||||
|
|
||||||
#: apps/permission/views.py:112 note_kfet/templates/base.html:108
|
#: apps/permission/views.py:112 note_kfet/templates/base.html:109
|
||||||
msgid "Rights"
|
msgid "Rights"
|
||||||
msgstr "Droits"
|
msgstr "Droits"
|
||||||
|
|
||||||
@ -2167,7 +2122,7 @@ msgstr ""
|
|||||||
msgid "Invalidate pre-registration"
|
msgid "Invalidate pre-registration"
|
||||||
msgstr "Invalider l'inscription"
|
msgstr "Invalider l'inscription"
|
||||||
|
|
||||||
#: apps/treasury/apps.py:12 note_kfet/templates/base.html:96
|
#: apps/treasury/apps.py:12 note_kfet/templates/base.html:97
|
||||||
msgid "Treasury"
|
msgid "Treasury"
|
||||||
msgstr "Trésorerie"
|
msgstr "Trésorerie"
|
||||||
|
|
||||||
@ -2575,7 +2530,7 @@ msgstr "Gérer les crédits de la Société générale"
|
|||||||
|
|
||||||
#: apps/wei/apps.py:10 apps/wei/models.py:50 apps/wei/models.py:51
|
#: apps/wei/apps.py:10 apps/wei/models.py:50 apps/wei/models.py:51
|
||||||
#: apps/wei/models.py:62 apps/wei/models.py:180
|
#: apps/wei/models.py:62 apps/wei/models.py:180
|
||||||
#: note_kfet/templates/base.html:102
|
#: note_kfet/templates/base.html:103
|
||||||
msgid "WEI"
|
msgid "WEI"
|
||||||
msgstr "WEI"
|
msgstr "WEI"
|
||||||
|
|
||||||
@ -2583,7 +2538,7 @@ msgstr "WEI"
|
|||||||
msgid "The selected user is not validated. Please validate its account first"
|
msgid "The selected user is not validated. Please validate its account first"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"L'utilisateur sélectionné n'est pas validé. Merci de d'abord valider son "
|
"L'utilisateur sélectionné n'est pas validé. Merci de d'abord valider son "
|
||||||
"compte"
|
"compte."
|
||||||
|
|
||||||
#: apps/wei/forms/registration.py:59 apps/wei/models.py:126
|
#: apps/wei/forms/registration.py:59 apps/wei/models.py:126
|
||||||
#: apps/wei/models.py:323
|
#: apps/wei/models.py:323
|
||||||
@ -2624,7 +2579,7 @@ msgstr "Sélectionnez les rôles qui vous intéressent."
|
|||||||
msgid "This team doesn't belong to the given bus."
|
msgid "This team doesn't belong to the given bus."
|
||||||
msgstr "Cette équipe n'appartient pas à ce bus."
|
msgstr "Cette équipe n'appartient pas à ce bus."
|
||||||
|
|
||||||
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:35
|
#: apps/wei/forms/surveys/wei2021.py:35
|
||||||
msgid "Choose a word:"
|
msgid "Choose a word:"
|
||||||
msgstr "Choisissez un mot :"
|
msgstr "Choisissez un mot :"
|
||||||
|
|
||||||
@ -3185,19 +3140,19 @@ msgstr "Répartir les 1A dans les bus"
|
|||||||
msgid "Attribute bus"
|
msgid "Attribute bus"
|
||||||
msgstr "Attribuer un bus"
|
msgstr "Attribuer un bus"
|
||||||
|
|
||||||
#: note_kfet/settings/base.py:172
|
#: note_kfet/settings/base.py:161
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr "Allemand"
|
msgstr "Allemand"
|
||||||
|
|
||||||
#: note_kfet/settings/base.py:173
|
#: note_kfet/settings/base.py:162
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr "Anglais"
|
msgstr "Anglais"
|
||||||
|
|
||||||
#: note_kfet/settings/base.py:174
|
#: note_kfet/settings/base.py:163
|
||||||
msgid "Spanish"
|
msgid "Spanish"
|
||||||
msgstr "Espagnol"
|
msgstr "Espagnol"
|
||||||
|
|
||||||
#: note_kfet/settings/base.py:175
|
#: note_kfet/settings/base.py:164
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr "Français"
|
msgstr "Français"
|
||||||
|
|
||||||
@ -3254,7 +3209,7 @@ msgstr ""
|
|||||||
"erreur, qui sera corrigée rapidement. Vous pouvez désormais aller boire une "
|
"erreur, qui sera corrigée rapidement. Vous pouvez désormais aller boire une "
|
||||||
"bière."
|
"bière."
|
||||||
|
|
||||||
#: note_kfet/templates/autocomplete_model.html:15
|
#: note_kfet/templates/autocomplete_model.html:14
|
||||||
msgid "Reset"
|
msgid "Reset"
|
||||||
msgstr "Réinitialiser"
|
msgstr "Réinitialiser"
|
||||||
|
|
||||||
@ -3262,34 +3217,34 @@ msgstr "Réinitialiser"
|
|||||||
msgid "The ENS Paris-Saclay BDE note."
|
msgid "The ENS Paris-Saclay BDE note."
|
||||||
msgstr "La note du BDE de l'ENS Paris-Saclay."
|
msgstr "La note du BDE de l'ENS Paris-Saclay."
|
||||||
|
|
||||||
#: note_kfet/templates/base.html:78
|
#: note_kfet/templates/base.html:79
|
||||||
msgid "Users"
|
msgid "Users"
|
||||||
msgstr "Utilisateurs"
|
msgstr "Utilisateurs"
|
||||||
|
|
||||||
#: note_kfet/templates/base.html:84
|
#: note_kfet/templates/base.html:85
|
||||||
msgid "Clubs"
|
msgid "Clubs"
|
||||||
msgstr "Clubs"
|
msgstr "Clubs"
|
||||||
|
|
||||||
#: note_kfet/templates/base.html:113
|
#: note_kfet/templates/base.html:114
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Admin"
|
msgstr "Admin"
|
||||||
|
|
||||||
#: note_kfet/templates/base.html:127
|
#: note_kfet/templates/base.html:128
|
||||||
msgid "My account"
|
msgid "My account"
|
||||||
msgstr "Mon compte"
|
msgstr "Mon compte"
|
||||||
|
|
||||||
#: note_kfet/templates/base.html:130
|
#: note_kfet/templates/base.html:131
|
||||||
msgid "Log out"
|
msgid "Log out"
|
||||||
msgstr "Se déconnecter"
|
msgstr "Se déconnecter"
|
||||||
|
|
||||||
#: note_kfet/templates/base.html:138
|
#: note_kfet/templates/base.html:139
|
||||||
#: note_kfet/templates/registration/signup.html:6
|
#: note_kfet/templates/registration/signup.html:6
|
||||||
#: note_kfet/templates/registration/signup.html:11
|
#: note_kfet/templates/registration/signup.html:11
|
||||||
#: note_kfet/templates/registration/signup.html:28
|
#: note_kfet/templates/registration/signup.html:28
|
||||||
msgid "Sign up"
|
msgid "Sign up"
|
||||||
msgstr "Inscription"
|
msgstr "Inscription"
|
||||||
|
|
||||||
#: note_kfet/templates/base.html:145
|
#: note_kfet/templates/base.html:146
|
||||||
#: note_kfet/templates/registration/login.html:6
|
#: note_kfet/templates/registration/login.html:6
|
||||||
#: note_kfet/templates/registration/login.html:15
|
#: note_kfet/templates/registration/login.html:15
|
||||||
#: note_kfet/templates/registration/login.html:38
|
#: note_kfet/templates/registration/login.html:38
|
||||||
@ -3297,7 +3252,7 @@ msgstr "Inscription"
|
|||||||
msgid "Log in"
|
msgid "Log in"
|
||||||
msgstr "Se connecter"
|
msgstr "Se connecter"
|
||||||
|
|
||||||
#: note_kfet/templates/base.html:159
|
#: note_kfet/templates/base.html:160
|
||||||
msgid ""
|
msgid ""
|
||||||
"You are not a BDE member anymore. Please renew your membership if you want "
|
"You are not a BDE member anymore. Please renew your membership if you want "
|
||||||
"to use the note."
|
"to use the note."
|
||||||
@ -3305,7 +3260,7 @@ msgstr ""
|
|||||||
"Vous n'êtes plus adhérent BDE. Merci de réadhérer si vous voulez profiter de "
|
"Vous n'êtes plus adhérent BDE. Merci de réadhérer si vous voulez profiter de "
|
||||||
"la note."
|
"la note."
|
||||||
|
|
||||||
#: note_kfet/templates/base.html:165
|
#: note_kfet/templates/base.html:166
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your e-mail address is not validated. Please check your mail inbox and click "
|
"Your e-mail address is not validated. Please check your mail inbox and click "
|
||||||
"on the validation link."
|
"on the validation link."
|
||||||
@ -3313,7 +3268,7 @@ msgstr ""
|
|||||||
"Votre adresse e-mail n'est pas validée. Merci de vérifier votre boîte mail "
|
"Votre adresse e-mail n'est pas validée. Merci de vérifier votre boîte mail "
|
||||||
"et de cliquer sur le lien de validation."
|
"et de cliquer sur le lien de validation."
|
||||||
|
|
||||||
#: note_kfet/templates/base.html:171
|
#: note_kfet/templates/base.html:172
|
||||||
msgid ""
|
msgid ""
|
||||||
"You declared that you opened a bank account in the Société générale. The "
|
"You declared that you opened a bank account in the Société générale. The "
|
||||||
"bank did not validate the creation of the account to the BDE, so the "
|
"bank did not validate the creation of the account to the BDE, so the "
|
||||||
@ -3327,11 +3282,11 @@ msgstr ""
|
|||||||
"vérification peut durer quelques jours. Merci de vous assurer de bien aller "
|
"vérification peut durer quelques jours. Merci de vous assurer de bien aller "
|
||||||
"au bout de vos démarches."
|
"au bout de vos démarches."
|
||||||
|
|
||||||
#: note_kfet/templates/base.html:194
|
#: note_kfet/templates/base.html:195
|
||||||
msgid "Contact us"
|
msgid "Contact us"
|
||||||
msgstr "Nous contacter"
|
msgstr "Nous contacter"
|
||||||
|
|
||||||
#: note_kfet/templates/base.html:196
|
#: note_kfet/templates/base.html:197
|
||||||
msgid "Technical Support"
|
msgid "Technical Support"
|
||||||
msgstr "Support technique"
|
msgstr "Support technique"
|
||||||
|
|
||||||
|
@ -24,15 +24,6 @@ ALLOWED_HOSTS = [
|
|||||||
os.getenv('NOTE_URL', 'localhost'),
|
os.getenv('NOTE_URL', 'localhost'),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Use secure cookies in production
|
|
||||||
SESSION_COOKIE_SECURE = not DEBUG
|
|
||||||
CSRF_COOKIE_SECURE = not DEBUG
|
|
||||||
|
|
||||||
# Remember HTTPS for 1 year
|
|
||||||
SECURE_HSTS_SECONDS = 31536000
|
|
||||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
|
|
||||||
SECURE_HSTS_PRELOAD = True
|
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ mark {
|
|||||||
|
|
||||||
/* Last BDE colors */
|
/* Last BDE colors */
|
||||||
.bg-primary {
|
.bg-primary {
|
||||||
background-color: rgb(102, 83, 105) !important;
|
background-color: rgb(18, 67, 4) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
@ -81,14 +81,14 @@ body {
|
|||||||
.btn-outline-primary:not(:disabled):not(.disabled).active,
|
.btn-outline-primary:not(:disabled):not(.disabled).active,
|
||||||
.btn-outline-primary:not(:disabled):not(.disabled):active {
|
.btn-outline-primary:not(:disabled):not(.disabled):active {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: rgb(102, 83, 105);
|
background-color: rgb(18, 67, 46);
|
||||||
border-color: rgb(102, 83, 105);
|
border-color: rgb(18, 67, 46);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-outline-primary {
|
.btn-outline-primary {
|
||||||
color: rgb(102, 83, 105);
|
color: rgb(18, 67, 46);
|
||||||
background-color: rgba(248, 249, 250, 0.9);
|
background-color: rgba(248, 249, 250, 0.9);
|
||||||
border-color: rgb(102, 83, 105);
|
border-color: rgb(18, 67, 46);
|
||||||
}
|
}
|
||||||
|
|
||||||
.turbolinks-progress-bar {
|
.turbolinks-progress-bar {
|
||||||
@ -99,35 +99,35 @@ body {
|
|||||||
.btn-primary:not(:disabled):not(.disabled).active,
|
.btn-primary:not(:disabled):not(.disabled).active,
|
||||||
.btn-primary:not(:disabled):not(.disabled):active {
|
.btn-primary:not(:disabled):not(.disabled):active {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: rgb(102, 83, 105);
|
background-color: rgb(18, 67, 46);
|
||||||
border-color: rgb(102, 83, 105);
|
border-color: rgb(18, 67, 46);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
color: rgba(248, 249, 250, 0.9);
|
color: rgba(248, 249, 250, 0.9);
|
||||||
background-color: rgb(102, 83, 105);
|
background-color: rgb(28, 114, 10);
|
||||||
border-color: rgb(102, 83, 105);
|
border-color: rgb(18, 67, 46);
|
||||||
}
|
}
|
||||||
|
|
||||||
.border-primary {
|
.border-primary {
|
||||||
border-color: rgb(115, 15, 115) !important;
|
border-color: rgb(28, 114, 10) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: rgb(102, 83, 105);
|
color: rgb(28, 114, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
color: rgb(200, 30, 200);
|
color: rgb(122, 163, 75);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control:focus {
|
.form-control:focus {
|
||||||
box-shadow: 0 0 0 0.25rem rgba(200, 30, 200, 0.25);
|
box-shadow: 0 0 0 0.25rem rgba(122, 163, 75, 0.25);
|
||||||
border-color: rgb(200, 30, 200);
|
border-color: rgb(122, 163, 75);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-outline-primary.focus {
|
.btn-outline-primary.focus {
|
||||||
box-shadow: 0 0 0 0.25rem rgba(200, 30, 200, 0.5);
|
box-shadow: 0 0 0 0.25rem rgba(122, 163, 75, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,29 +13,21 @@ $(document).ready(function () {
|
|||||||
$('#' + prefix + '_reset').removeClass('d-none')
|
$('#' + prefix + '_reset').removeClass('d-none')
|
||||||
|
|
||||||
$.getJSON(api_url + (api_url.includes('?') ? '&' : '?') + 'format=json&search=^' + input + api_url_suffix, function (objects) {
|
$.getJSON(api_url + (api_url.includes('?') ? '&' : '?') + 'format=json&search=^' + input + api_url_suffix, function (objects) {
|
||||||
let html = '<ul class="list-group list-group-flush" id="' + prefix + '_list">'
|
let html = ''
|
||||||
|
|
||||||
objects.results.forEach(function (obj) {
|
objects.results.forEach(function (obj) {
|
||||||
html += li(prefix + '_' + obj.id, obj[name_field])
|
html += li(prefix + '_' + obj.id, obj[name_field])
|
||||||
})
|
})
|
||||||
html += '</ul>'
|
|
||||||
|
|
||||||
target.tooltip({
|
const results_list = $('#' + prefix + '_list')
|
||||||
html: true,
|
results_list.html(html)
|
||||||
placement: 'bottom',
|
|
||||||
trigger: 'manual',
|
|
||||||
container: target.parent(),
|
|
||||||
fallbackPlacement: 'clockwise'
|
|
||||||
})
|
|
||||||
|
|
||||||
target.attr("data-original-title", html).tooltip("show")
|
|
||||||
|
|
||||||
objects.results.forEach(function (obj) {
|
objects.results.forEach(function (obj) {
|
||||||
$('#' + prefix + '_' + obj.id).click(function () {
|
$('#' + prefix + '_' + obj.id).click(function () {
|
||||||
target.val(obj[name_field])
|
target.val(obj[name_field])
|
||||||
$('#' + prefix + '_pk').val(obj.id)
|
$('#' + prefix + '_pk').val(obj.id)
|
||||||
|
|
||||||
target.tooltip("hide")
|
results_list.html('')
|
||||||
target.removeClass('is-invalid')
|
target.removeClass('is-invalid')
|
||||||
target.addClass('is-valid')
|
target.addClass('is-valid')
|
||||||
|
|
||||||
@ -45,8 +37,8 @@ $(document).ready(function () {
|
|||||||
if (input === obj[name_field]) { $('#' + prefix + '_pk').val(obj.id) }
|
if (input === obj[name_field]) { $('#' + prefix + '_pk').val(obj.id) }
|
||||||
})
|
})
|
||||||
|
|
||||||
if (objects.results.length === 1 && e.originalEvent.keyCode >= 32) {
|
if (results_list.children().length === 1 && e.originalEvent.keyCode >= 32) {
|
||||||
$('#' + prefix + '_' + objects.results[0].id).trigger('click')
|
results_list.children().first().trigger('click')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -9,9 +9,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
name="{{ widget.name }}_name" autocomplete="off"
|
name="{{ widget.name }}_name" autocomplete="off"
|
||||||
{% for name, value in widget.attrs.items %}
|
{% for name, value in widget.attrs.items %}
|
||||||
{% ifnotequal value False %}{{ name }}{% ifnotequal value True %}="{{ value|stringformat:'s' }}"{% endifnotequal %}{% endifnotequal %}
|
{% ifnotequal value False %}{{ name }}{% ifnotequal value True %}="{{ value|stringformat:'s' }}"{% endifnotequal %}{% endifnotequal %}
|
||||||
{% endfor %}
|
{% endfor %}>
|
||||||
aria-describedby="{{widget.attrs.id}}_tooltip">
|
|
||||||
{% if widget.resetable %}
|
{% if widget.resetable %}
|
||||||
<a id="{{ widget.attrs.id }}_reset" class="btn btn-light autocomplete-reset{% if not widget.value %} d-none{% endif %}">{% trans "Reset" %}</a>
|
<a id="{{ widget.attrs.id }}_reset" class="btn btn-light autocomplete-reset{% if not widget.value %} d-none{% endif %}">{% trans "Reset" %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<ul class="list-group list-group-flush" id="{{ widget.attrs.id }}_list">
|
||||||
|
</ul>
|
||||||
|
Reference in New Issue
Block a user