From d8949a993a30640ac8799a9eb9928f2b907ec6a8 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Sat, 28 Mar 2020 17:42:29 +0100 Subject: [PATCH 01/87] get alias and note infos together --- apps/note/api/serializers.py | 14 ++++++++++++++ apps/note/api/urls.py | 5 +++-- apps/note/api/views.py | 27 +++++++++++++++++++++++++-- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/apps/note/api/serializers.py b/apps/note/api/serializers.py index a51b4263..27ff6d65 100644 --- a/apps/note/api/serializers.py +++ b/apps/note/api/serializers.py @@ -92,6 +92,20 @@ class NotePolymorphicSerializer(PolymorphicSerializer): class Meta: model = Note +class ConsumerSerializer(serializers.ModelSerializer): + """ + REST API Nested Serializer for Consumers. + return Alias, and the note Associated to it in + """ + note = NotePolymorphicSerializer() + class Meta: + model = Alias + fields = '__all__' + + @staticmethod + def setup_eager_loading(queryset): + queryset = queryset.select_related('note') + class TemplateCategorySerializer(serializers.ModelSerializer): """ diff --git a/apps/note/api/urls.py b/apps/note/api/urls.py index 796a397f..57909080 100644 --- a/apps/note/api/urls.py +++ b/apps/note/api/urls.py @@ -1,7 +1,7 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later -from .views import NotePolymorphicViewSet, AliasViewSet, \ +from .views import NotePolymorphicViewSet, AliasViewSet, ConsumerViewSet, \ TemplateCategoryViewSet, TransactionViewSet, TransactionTemplateViewSet @@ -11,7 +11,8 @@ def register_note_urls(router, path): """ router.register(path + '/note', NotePolymorphicViewSet) router.register(path + '/alias', AliasViewSet) - + router.register(path + '/consumer', ConsumerViewSet) + router.register(path + '/transaction/category', TemplateCategoryViewSet) router.register(path + '/transaction/transaction', TransactionViewSet) router.register(path + '/transaction/template', TransactionTemplateViewSet) diff --git a/apps/note/api/views.py b/apps/note/api/views.py index f230a646..af02dc8b 100644 --- a/apps/note/api/views.py +++ b/apps/note/api/views.py @@ -6,8 +6,8 @@ from django_filters.rest_framework import DjangoFilterBackend from rest_framework.filters import OrderingFilter, SearchFilter from api.viewsets import ReadProtectedModelViewSet, ReadOnlyProtectedModelViewSet -from .serializers import NotePolymorphicSerializer, AliasSerializer, TemplateCategorySerializer, \ - TransactionTemplateSerializer, TransactionPolymorphicSerializer +from .serializers import NotePolymorphicSerializer, AliasSerializer, ConsumerSerializer,\ + TemplateCategorySerializer, TransactionTemplateSerializer, TransactionPolymorphicSerializer from ..models.notes import Note, Alias from ..models.transactions import TransactionTemplate, Transaction, TemplateCategory @@ -68,6 +68,29 @@ class AliasViewSet(ReadProtectedModelViewSet): return queryset +class ConsumerViewSet(ReadOnlyProtectedModelViewSet): + queryset = Alias.objects.all() + serializer_class = ConsumerSerializer + filter_backends = [SearchFilter, OrderingFilter] + search_fields = ['$normalized_name', '$name', '$note__polymorphic_ctype__model', ] + ordering_fields = ['name', 'normalized_name'] + + def get_queryset(self): + """ + Parse query and apply filters. + :return: The filtered set of requested aliases + """ + + queryset = super().get_queryset() + + alias = self.request.query_params.get("alias", ".*") + queryset = queryset.filter( + Q(name__regex="^" + alias) + | Q(normalized_name__regex="^" + Alias.normalize(alias)) + | Q(normalized_name__regex="^" + alias.lower())) + + return queryset + class TemplateCategoryViewSet(ReadProtectedModelViewSet): """ From f3656f367c2fc8ec8a17983099af7423e8a00033 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Sat, 28 Mar 2020 17:44:03 +0100 Subject: [PATCH 02/87] default picture is 200x200 --- media/pic/default.png | Bin 9259 -> 4054 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/media/pic/default.png b/media/pic/default.png index f933bc341619178775520150d514effb2339b10a..41a31a1cf5eddf31e98a7e6cd06b4c45f2faee92 100644 GIT binary patch literal 4054 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 IM6N<$f;wYbJOBUy literal 9259 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 Date: Sat, 28 Mar 2020 17:44:22 +0100 Subject: [PATCH 03/87] color codes from note balances --- static/js/base.js | 131 +++++++++++++++++++-------------- templates/note/conso_form.html | 16 ++-- 2 files changed, 87 insertions(+), 60 deletions(-) diff --git a/static/js/base.js b/static/js/base.js index f7085850..61372c76 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -48,10 +48,27 @@ function getMatchedNotes(pattern, fun) { /** * Generate a
  • entry with a given id and text */ -function li(id, text) { - return "
  • " + text + "
  • \n"; } +/** + * Return style to apply according to the balance of the note + * @param balance The balance of the note. + */ +function displayStyle(balance){ + if (balance < -5000){ + return " text-danger bg-dark" + } + else if(balance <-1000){ + return " text-danger" + } + else if(balance < 0){ + return "text-warning" + } + return "" +} + /** * Render note name and picture @@ -62,24 +79,28 @@ function li(id, text) { */ function displayNote(note, alias, user_note_field=null, profile_pic_field=null) { if (!note.display_image) { - note.display_image = 'https://nk20.ynerant.fr/media/pic/default.png'; - $.getJSON("/api/note/note/" + note.id + "/?format=json", function(new_note) { - note.display_image = new_note.display_image.replace("http:", "https:"); - note.name = new_note.name; - note.balance = new_note.balance; - - displayNote(note, alias, user_note_field, profile_pic_field); - }); - return; + note.display_image = '/media/pic/default.png'; } let img = note.display_image; if (alias !== note.name) alias += " (aka. " + note.name + ")"; if (user_note_field !== null) + + $("#" + user_note_field).addClass(displayStyle(note.balance)); $("#" + user_note_field).text(alias + (note.balance == null ? "" : (" : " + pretty_money(note.balance)))); - if (profile_pic_field != null) + if (profile_pic_field != null){ $("#" + profile_pic_field).attr('src', img); + $("#" + profile_pic_field).click(function(){ + console.log(note); + if(note.resourcetype == "NoteUser"){ + document.location.href = "/accounts/user/" + note.user; + } + else if(note.resourcetype == "NoteClub"){ + document.location.href = "/accounts/club/" + note.club; + } + }); + } } /** @@ -153,8 +174,11 @@ function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes // When the user type "Enter", the first alias is clicked field.keypress(function(event) { - if (event.originalEvent.charCode === 13) - $("#" + alias_matched_id + " li").first().trigger("click"); + if (event.originalEvent.charCode === 13 && notes.length > 0) { + let li_obj = $("#" + alias_matched_id + " li").first(); + displayNote(notes[0], li_obj.text(), user_note_field, profile_pic_field); + li_obj.trigger("click"); + } }); // When the user type something, the matched aliases are refreshed @@ -164,51 +188,46 @@ function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes let pattern = field.val(); // If the pattern is not modified, we don't query the API - if (pattern === old_pattern || pattern === "") + if (pattern === old_pattern) return; - old_pattern = pattern; - - // Clear old matched notes notes.length = 0; let aliases_matched_obj = $("#" + alias_matched_id); let aliases_matched_html = ""; - - // Get matched notes with the given pattern - getMatchedNotes(pattern, function(aliases) { + // get matched Alias with note associated + if(pattern == ""){ + aliases_matched_obj = $("#" + alias_matched_id); + aliases_matched_html = ""; + aliases_matched_obj.html("") + notes.length = 0; + return; + } + + $.getJSON("/api/note/consumer/?format=json&alias=" + + pattern + + "&search=user|club&ordering=normalized_name", + function(consumers){ // The response arrived too late, we stop the request if (pattern !== $("#" + field_id).val()) return; - - aliases.results.forEach(function (alias) { - let note = alias.note; - note = { - id: note, - name: alias.name, - alias: alias, - balance: null - }; - aliases_matched_html += li(alias_prefix + "_" + alias.id, alias.name); + consumers.results.forEach(function (consumer){ + let note = consumer.note; + extra_css = displayStyle(note.balance); + aliases_matched_html += li(alias_prefix + '_' + consumer.id, + consumer.name, + extra_css); notes.push(note); }); - - // Display the list of matched aliases aliases_matched_obj.html(aliases_matched_html); - - notes.forEach(function (note) { - let alias = note.alias; - let alias_obj = $("#" + alias_prefix + "_" + alias.id); - // When an alias is hovered, the profile picture and the balance are displayed at the right place - alias_obj.hover(function () { - displayNote(note, alias.name, user_note_field, profile_pic_field); + consumers.results.forEach(function(consumer){ + let note = consumer.note; + let consumer_obj = $("#" + alias_prefix+ "_" + consumer.id); + consumer_obj.hover(function(){ + displayNote(consumer.note, consumer.name, user_note_field,profile_pic_field) }); - - // When the user click on an alias, the associated note is added to the emitters - alias_obj.click(function () { - field.val(""); - old_pattern = ""; - // If the note is already an emitter, we increase the quantity + consumer_obj.click(function(){ + field.val(""); old_pattern = ""; // reset input field var disp = null; notes_display.forEach(function (d) { // We compare the note ids @@ -220,8 +239,8 @@ function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes // In the other case, we add a new emitter if (disp == null) { disp = { - name: alias.name, - id: note.id, + name: consumer.name, + id: consumer.id, note: note, quantity: 1 }; @@ -236,8 +255,11 @@ function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes let note_list = $("#" + note_list_id); let html = ""; notes_display.forEach(function (disp) { - html += li(note_prefix + "_" + disp.id, disp.name - + "" + disp.quantity + ""); + html += li(note_prefix + "_" + disp.id, + disp.name + + "" + + disp.quantity + "", + displayStyle(disp.note.balance)); }); // Emitters are displayed @@ -254,11 +276,12 @@ function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes line_obj.click(removeNote(disp, note_prefix, notes_display, note_list_id, user_note_field, profile_pic_field)); }); - }); + }) }); - }); - }); -} + + });// end getJSON alias + }); +}// end function autocomplete // When a validate button is clicked, we switch the validation status function de_validate(id, validated) { diff --git a/templates/note/conso_form.html b/templates/note/conso_form.html index b108a96f..36659849 100644 --- a/templates/note/conso_form.html +++ b/templates/note/conso_form.html @@ -11,9 +11,10 @@
    {# User details column #}
    -
    +
    + id="profile_pic" alt="" + class="img-thumbnail">
    @@ -25,14 +26,17 @@

    - {% trans "Select emitters" %} + {% trans "Consum" %}

    +
    -
    - -
      +
    +
    From aaf49c9b99a9275127873de5fb76ac56f9d4928e Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Sat, 28 Mar 2020 21:30:55 +0100 Subject: [PATCH 04/87] balance on a second line --- static/js/base.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/static/js/base.js b/static/js/base.js index 61372c76..86e090f9 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -81,14 +81,13 @@ function displayNote(note, alias, user_note_field=null, profile_pic_field=null) if (!note.display_image) { note.display_image = '/media/pic/default.png'; } - let img = note.display_image; if (alias !== note.name) alias += " (aka. " + note.name + ")"; if (user_note_field !== null) $("#" + user_note_field).addClass(displayStyle(note.balance)); - $("#" + user_note_field).text(alias + (note.balance == null ? "" : (" : " + pretty_money(note.balance)))); + $("#" + user_note_field).text(alias + (note.balance == null ? "" : (":\n" + pretty_money(note.balance)))); if (profile_pic_field != null){ $("#" + profile_pic_field).attr('src', img); $("#" + profile_pic_field).click(function(){ From 935ebf291afd54e31dbc325c699c354c846c7c5a Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Sat, 28 Mar 2020 21:31:22 +0100 Subject: [PATCH 05/87] use note id , not alias id --- static/js/consos.js | 2 +- static/js/transfer.js | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/static/js/consos.js b/static/js/consos.js index 20859933..9450febc 100644 --- a/static/js/consos.js +++ b/static/js/consos.js @@ -167,7 +167,7 @@ function reset() { function consumeAll() { notes_display.forEach(function(note_display) { buttons.forEach(function(button) { - consume(note_display.id, note_display.name, button.dest, button.quantity * note_display.quantity, button.amount, + consume(note_display.note.id, note_display.name, button.dest, button.quantity * note_display.quantity, button.amount, button.name + " (" + button.category_name + ")", button.type, button.category_id, button.id); }); }); diff --git a/static/js/transfer.js b/static/js/transfer.js index cf62e453..5b673acb 100644 --- a/static/js/transfer.js +++ b/static/js/transfer.js @@ -83,7 +83,7 @@ $("#transfer").click(function() { "polymorphic_ctype": TRANSFER_POLYMORPHIC_CTYPE, "resourcetype": "Transaction", "source": user_id, - "destination": dest.id, + "destination": dest.note.id, "destination_alias": dest.name }).done(function () { addMsg("Le transfert de " @@ -91,7 +91,7 @@ $("#transfer").click(function() { + " vers la note " + dest.name + " a été fait avec succès !", "success"); reset(); - }).fail(function () { + }).fail(function () { // do it again but valid = false $.post("/api/note/transaction/transaction/", { "csrfmiddlewaretoken": CSRF_TOKEN, @@ -103,7 +103,7 @@ $("#transfer").click(function() { "polymorphic_ctype": TRANSFER_POLYMORPHIC_CTYPE, "resourcetype": "Transaction", "source": user_id, - "destination": dest.id, + "destination": dest.note.id, "destination_alias": dest.name }).done(function () { addMsg("Le transfert de " @@ -133,9 +133,9 @@ $("#transfer").click(function() { "valid": true, "polymorphic_ctype": TRANSFER_POLYMORPHIC_CTYPE, "resourcetype": "Transaction", - "source": source.id, + "source": source.note.id, "source_alias": source.name, - "destination": dest.id, + "destination": dest.note.id, "destination_alias": dest.name }).done(function () { addMsg("Le transfert de " @@ -143,7 +143,7 @@ $("#transfer").click(function() { + " vers la note " + dest.name + " a été fait avec succès !", "success"); reset(); - }).fail(function (err) { + }).fail(function (err) { // do it again but valid = false $.post("/api/note/transaction/transaction/", { "csrfmiddlewaretoken": CSRF_TOKEN, @@ -154,9 +154,9 @@ $("#transfer").click(function() { "invalidity_reason": "Solde insuffisant", "polymorphic_ctype": TRANSFER_POLYMORPHIC_CTYPE, "resourcetype": "Transaction", - "source": source.id, + "source": source.note.id, "source_alias": source.name, - "destination": dest.id, + "destination": dest.note.id, "destination_alias": dest.name }).done(function () { addMsg("Le transfert de " @@ -176,7 +176,7 @@ $("#transfer").click(function() { }); } else if ($("#type_credit").is(':checked') || $("#type_debit").is(':checked')) { let special_note = $("#credit_type").val(); - let user_note = dests_notes_display[0].id; + let user_note = dests_notes_display[0].note.id; let given_reason = $("#reason").val(); let source, dest, reason; if ($("#type_credit").is(':checked')) { @@ -217,4 +217,4 @@ $("#transfer").click(function() { reset(); }); } -}); \ No newline at end of file +}); From 751147f254e07c54d9a9e32da001d1ecb74f61fa Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 10 Apr 2020 00:02:22 +0200 Subject: [PATCH 06/87] Don't display a note that we can't see, fix CI, fix distinct fields on PostgresSQL DB --- apps/activity/models.py | 4 ++-- apps/activity/views.py | 19 +++++++++++++------ apps/note/api/serializers.py | 18 ++++++++++++------ apps/note/api/urls.py | 2 +- apps/note/api/views.py | 3 ++- apps/permission/backends.py | 20 ++++++++++++++------ apps/treasury/views.py | 6 +++--- static/js/base.js | 2 +- 8 files changed, 48 insertions(+), 26 deletions(-) diff --git a/apps/activity/models.py b/apps/activity/models.py index 29f04b39..cab229c4 100644 --- a/apps/activity/models.py +++ b/apps/activity/models.py @@ -139,7 +139,7 @@ class Entry(models.Model): verbose_name = _("entry") verbose_name_plural = _("entries") - def save(self, *args,**kwargs): + def save(self, *args, **kwargs): qs = Entry.objects.filter(~Q(pk=self.pk), activity=self.activity, note=self.note, guest=self.guest) if qs.exists(): @@ -153,7 +153,7 @@ class Entry(models.Model): if self.note.balance < 0: raise ValidationError(_("The balance is negative.")) - ret = super().save(*args,**kwargs) + ret = super().save(*args, **kwargs) if insert and self.guest: GuestTransaction.objects.create( diff --git a/apps/activity/views.py b/apps/activity/views.py index 14746929..c0ae6a4e 100644 --- a/apps/activity/views.py +++ b/apps/activity/views.py @@ -3,6 +3,7 @@ from datetime import datetime, timezone +from django.conf import settings from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.contenttypes.models import ContentType from django.db.models import F, Q @@ -45,8 +46,8 @@ class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView context['title'] = _("Activities") upcoming_activities = Activity.objects.filter(date_end__gt=datetime.now()) - context['upcoming'] = ActivityTable(data=upcoming_activities - .filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))) + context['upcoming'] = ActivityTable(data=upcoming_activities.filter( + PermissionBackend.filter_queryset(self.request.user, Activity, "view"))) return context @@ -138,8 +139,14 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView): | Q(note__noteuser__user__last_name__regex=pattern) | Q(name__regex=pattern) | Q(normalized_name__regex=Alias.normalize(pattern)))) \ - .filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view"))\ - .distinct()[:20] + .filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view")) + if settings.DATABASES[note_qs.db]["ENGINE"] == 'django.db.backends.postgresql_psycopg2': + note_qs = note_qs.distinct('note__pk')[:20] + else: + # SQLite doesn't support distinct fields. For compatibility reason (in dev mode), the note list will only + # have distinct aliases rather than distinct notes with a SQLite DB, but it can fill the result page. + # In production mode, please use PostgreSQL. + note_qs = note_qs.distinct()[:20] for note in note_qs: note.type = "Adhérent" note.activity = activity @@ -153,9 +160,9 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView): context["title"] = _('Entry for activity "{}"').format(activity.name) context["noteuser_ctype"] = ContentType.objects.get_for_model(NoteUser).pk context["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk - + context["activities_open"] = Activity.objects.filter(open=True).filter( PermissionBackend.filter_queryset(self.request.user, Activity, "view")).filter( PermissionBackend.filter_queryset(self.request.user, Activity, "change")).all() - return context \ No newline at end of file + return context diff --git a/apps/note/api/serializers.py b/apps/note/api/serializers.py index 7005ce16..ce2ed97e 100644 --- a/apps/note/api/serializers.py +++ b/apps/note/api/serializers.py @@ -3,6 +3,8 @@ from rest_framework import serializers from rest_polymorphic.serializers import PolymorphicSerializer +from note_kfet.middlewares import get_current_authenticated_user +from permission.backends import PermissionBackend from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction, TemplateCategory, \ @@ -96,20 +98,24 @@ class NotePolymorphicSerializer(PolymorphicSerializer): class Meta: model = Note + class ConsumerSerializer(serializers.ModelSerializer): """ REST API Nested Serializer for Consumers. - return Alias, and the note Associated to it in + return Alias, and the note Associated to it in """ - note = NotePolymorphicSerializer() + note = serializers.SerializerMethodField() + class Meta: model = Alias fields = '__all__' - @staticmethod - def setup_eager_loading(queryset): - queryset = queryset.select_related('note') - + def get_note(self, obj): + if PermissionBackend.check_perm(get_current_authenticated_user(), "note.view_note", obj.note): + print(obj.pk) + return NotePolymorphicSerializer().to_representation(obj.note) + return dict(id=obj.id) + class TemplateCategorySerializer(serializers.ModelSerializer): """ diff --git a/apps/note/api/urls.py b/apps/note/api/urls.py index 57909080..2f3fd1a9 100644 --- a/apps/note/api/urls.py +++ b/apps/note/api/urls.py @@ -12,7 +12,7 @@ def register_note_urls(router, path): router.register(path + '/note', NotePolymorphicViewSet) router.register(path + '/alias', AliasViewSet) router.register(path + '/consumer', ConsumerViewSet) - + router.register(path + '/transaction/category', TemplateCategoryViewSet) router.register(path + '/transaction/transaction', TransactionViewSet) router.register(path + '/transaction/template', TransactionTemplateViewSet) diff --git a/apps/note/api/views.py b/apps/note/api/views.py index 61ef19eb..d5ad8129 100644 --- a/apps/note/api/views.py +++ b/apps/note/api/views.py @@ -89,6 +89,7 @@ class AliasViewSet(ReadProtectedModelViewSet): return queryset + class ConsumerViewSet(ReadOnlyProtectedModelViewSet): queryset = Alias.objects.all() serializer_class = ConsumerSerializer @@ -111,7 +112,7 @@ class ConsumerViewSet(ReadOnlyProtectedModelViewSet): | Q(normalized_name__regex="^" + alias.lower())) return queryset - + class TemplateCategoryViewSet(ReadProtectedModelViewSet): """ diff --git a/apps/permission/backends.py b/apps/permission/backends.py index 04d93528..2407ea1b 100644 --- a/apps/permission/backends.py +++ b/apps/permission/backends.py @@ -8,6 +8,7 @@ from django.contrib.auth.models import User, AnonymousUser from django.contrib.contenttypes.models import ContentType from django.db.models import Q, F from note.models import Note, NoteUser, NoteClub, NoteSpecial +from note_kfet import settings from note_kfet.middlewares import get_current_session from member.models import Membership, Club @@ -36,14 +37,21 @@ class PermissionBackend(ModelBackend): # Unauthenticated users have no permissions return Permission.objects.none() - return Permission.objects.annotate(club=F("rolepermissions__role__membership__club")) \ + perms = Permission.objects.annotate(club=F("rolepermissions__role__membership__club")) \ .filter( rolepermissions__role__membership__user=user, rolepermissions__role__membership__date_start__lte=datetime.date.today(), rolepermissions__role__membership__date_end__gte=datetime.date.today(), type=t, - mask__rank__lte=get_current_session().get("permission_mask", 0), - ).distinct() + mask__rank__lte=get_current_session().get("permission_mask", 42), + ) + if settings.DATABASES[perms.db]["ENGINE"] == 'django.db.backends.postgresql_psycopg2': + # We want one permission per club, and per permission type. + # SQLite does not support this kind of filter, that's why we don't filter the permissions with this + # kind of DB. This only increases performances (we can check multiple times the same permission) + # but don't have any semantic influence. + perms = perms.distinct('club', 'pk') + return perms @staticmethod def permissions(user, model, type): @@ -95,7 +103,7 @@ class PermissionBackend(ModelBackend): # Anonymous users can't do anything return Q(pk=-1) - if user.is_superuser and get_current_session().get("permission_mask", 0) >= 42: + if user.is_superuser and get_current_session().get("permission_mask", 42) >= 42: # Superusers have all rights return Q() @@ -129,9 +137,9 @@ class PermissionBackend(ModelBackend): sess = get_current_session() if sess is not None and sess.session_key is None: - return Permission.objects.none() + return False - if user_obj.is_superuser and get_current_session().get("permission_mask", 0) >= 42: + if user_obj.is_superuser and get_current_session().get("permission_mask", 42) >= 42: return True if obj is None: diff --git a/apps/treasury/views.py b/apps/treasury/views.py index 7361d1d2..8d744443 100644 --- a/apps/treasury/views.py +++ b/apps/treasury/views.py @@ -203,9 +203,9 @@ class RemittanceCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["table"] = RemittanceTable(data=Remittance.objects - .filter(PermissionBackend.filter_queryset(self.request.user, Remittance, "view")) - .all()) + context["table"] = RemittanceTable( + data=Remittance.objects.filter( + PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all()) context["special_transactions"] = SpecialTransactionTable(data=SpecialTransaction.objects.none()) return context diff --git a/static/js/base.js b/static/js/base.js index 179bdd3b..fc933794 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -119,7 +119,7 @@ function displayNote(note, alias, user_note_field=null, profile_pic_field=null) note.display_image = '/media/pic/default.png'; } let img = note.display_image; - if (alias !== note.name) + if (alias !== note.name && note.name) alias += " (aka. " + note.name + ")"; if (user_note_field !== null) From 6477590cd43af931b66f0d5e84193d90e7da08cf Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 10 Apr 2020 00:09:10 +0200 Subject: [PATCH 07/87] Fix devalidation --- apps/note/api/serializers.py | 4 ++++ apps/note/tables.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/note/api/serializers.py b/apps/note/api/serializers.py index ce2ed97e..70aecf44 100644 --- a/apps/note/api/serializers.py +++ b/apps/note/api/serializers.py @@ -111,6 +111,10 @@ class ConsumerSerializer(serializers.ModelSerializer): fields = '__all__' def get_note(self, obj): + """ + Display information about the associated note + """ + # If the user has no right to see the note, then we only display the note identifier if PermissionBackend.check_perm(get_current_authenticated_user(), "note.view_note", obj.note): print(obj.pk) return NotePolymorphicSerializer().to_representation(obj.note) diff --git a/apps/note/tables.py b/apps/note/tables.py index a38beb9a..8504121c 100644 --- a/apps/note/tables.py +++ b/apps/note/tables.py @@ -55,7 +55,7 @@ class HistoryTable(tables.Table): "class": lambda record: str(record.valid).lower() + ' validate', "data-toggle": "tooltip", "title": lambda record: _("Click to invalidate") if record.valid else _("Click to validate"), - "onclick": lambda record: 'in_validate(' + str(record.id) + ', ' + str(record.valid).lower() + ')', + "onclick": lambda record: 'de_validate(' + str(record.id) + ', ' + str(record.valid).lower() + ')', "onmouseover": lambda record: '$("#invalidity_reason_' + str(record.id) + '").show();$("#invalidity_reason_' + str(record.id) + '").focus();', From 31205b9b5d3519de1666bcbe5ff2635dffa3e4a0 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 10 Apr 2020 02:42:21 +0200 Subject: [PATCH 08/87] Fix devalidation, clear class on note description --- static/js/base.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/static/js/base.js b/static/js/base.js index fc933794..43aa5ce3 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -123,6 +123,7 @@ function displayNote(note, alias, user_note_field=null, profile_pic_field=null) alias += " (aka. " + note.name + ")"; if (user_note_field !== null) + $("#" + user_note_field).removeAttr('class'); $("#" + user_note_field).addClass(displayStyle(note.balance)); $("#" + user_note_field).text(alias + (note.balance == null ? "" : (":\n" + pretty_money(note.balance)))); if (profile_pic_field != null){ @@ -321,10 +322,11 @@ function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes // When a validate button is clicked, we switch the validation status function de_validate(id, validated) { + let invalidity_reason = $("#invalidity_reason_" + id).val(); $("#validate_" + id).html("⟳ ..."); // Perform a PATCH request to the API in order to update the transaction - // If the user has insuffisent rights, an error message will appear + // If the user has insufficient rights, an error message will appear $.ajax({ "url": "/api/note/transaction/transaction/" + id + "/", type: "PATCH", @@ -333,9 +335,9 @@ function de_validate(id, validated) { "X-CSRFTOKEN": CSRF_TOKEN }, data: { - resourcetype: "RecurrentTransaction", - valid: !validated, - invalidity_reason: invalidity_reason, + "resourcetype": "RecurrentTransaction", + "valid": !validated, + "invalidity_reason": invalidity_reason, }, success: function () { // Refresh jQuery objects From 658d40224200022122b263ca47c386f78aca3147 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 10 Apr 2020 02:56:56 +0200 Subject: [PATCH 09/87] Notes of users that don't have validated their email addresses are displayed with a different background --- apps/note/api/serializers.py | 7 + static/js/base.js | 253 ++++++++++++++++---------------- templates/member/user_list.html | 3 +- 3 files changed, 137 insertions(+), 126 deletions(-) diff --git a/apps/note/api/serializers.py b/apps/note/api/serializers.py index 70aecf44..c61ccd42 100644 --- a/apps/note/api/serializers.py +++ b/apps/note/api/serializers.py @@ -106,6 +106,8 @@ class ConsumerSerializer(serializers.ModelSerializer): """ note = serializers.SerializerMethodField() + email_confirmed = serializers.SerializerMethodField() + class Meta: model = Alias fields = '__all__' @@ -120,6 +122,11 @@ class ConsumerSerializer(serializers.ModelSerializer): return NotePolymorphicSerializer().to_representation(obj.note) return dict(id=obj.id) + def get_email_confirmed(self, obj): + if isinstance(obj.note, NoteUser): + return obj.note.user.profile.email_confirmed + return True + class TemplateCategorySerializer(serializers.ModelSerializer): """ diff --git a/static/js/base.js b/static/js/base.js index 43aa5ce3..56c23c85 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -21,7 +21,7 @@ function pretty_money(value) { * @param alert_type The type of the alert. Choices: info, success, warning, danger * @param timeout The delay (in millis) after that the message is auto-closed. If negative, then it is ignored. */ -function addMsg(msg, alert_type, timeout=-1) { +function addMsg(msg, alert_type, timeout = -1) { let msgDiv = $("#messages"); let html = msgDiv.html(); let id = Math.floor(10000 * Math.random() + 1); @@ -42,28 +42,28 @@ function addMsg(msg, alert_type, timeout=-1) { * @param errs_obj [{error_code:erro_message}] * @param timeout The delay (in millis) after that the message is auto-closed. If negative, then it is ignored. */ -function errMsg(errs_obj, timeout=-1) { +function errMsg(errs_obj, timeout = -1) { for (const err_msg of Object.values(errs_obj)) { - addMsg(err_msg,'danger', timeout); - } + addMsg(err_msg, 'danger', timeout); + } } var reloadWithTurbolinks = (function () { - var scrollPosition; + var scrollPosition; - function reload () { - scrollPosition = [window.scrollX, window.scrollY]; - Turbolinks.visit(window.location.toString(), { action: 'replace' }) - } - - document.addEventListener('turbolinks:load', function () { - if (scrollPosition) { - window.scrollTo.apply(window, scrollPosition); - scrollPosition = null + function reload() { + scrollPosition = [window.scrollX, window.scrollY]; + Turbolinks.visit(window.location.toString(), {action: 'replace'}) } - }); - return reload; + document.addEventListener('turbolinks:load', function () { + if (scrollPosition) { + window.scrollTo.apply(window, scrollPosition); + scrollPosition = null + } + }); + + return reload; })(); /** @@ -85,25 +85,27 @@ function getMatchedNotes(pattern, fun) { /** * Generate a
  • entry with a given id and text */ -function li(id, text,extra_css) { +function li(id, text, extra_css) { return "
  • " + text + "
  • \n"; + " id=\"" + id + "\">" + text + "\n"; } + /** - * Return style to apply according to the balance of the note - * @param balance The balance of the note. + * Return style to apply according to the balance of the note and the validation status of the email address + * @param note The concerned note. */ -function displayStyle(balance){ - if (balance < -5000){ - return " text-danger bg-dark" - } - else if(balance <-1000){ - return " text-danger" - } - else if(balance < 0){ - return "text-warning" - } - return "" +function displayStyle(note) { + let balance = note.balance; + var css = ""; + if (balance < -5000) + css += " text-danger bg-dark"; + else if (balance < -1000) + css += " text-danger"; + else if (balance < 0) + css += " text-warning"; + if (!note.email_confirmed) + css += " text-white bg-primary"; + return css; } @@ -114,29 +116,28 @@ function displayStyle(balance){ * @param user_note_field * @param profile_pic_field */ -function displayNote(note, alias, user_note_field=null, profile_pic_field=null) { +function displayNote(note, alias, user_note_field = null, profile_pic_field = null) { if (!note.display_image) { note.display_image = '/media/pic/default.png'; } let img = note.display_image; if (alias !== note.name && note.name) alias += " (aka. " + note.name + ")"; - if (user_note_field !== null) - + if (user_note_field !== null) { $("#" + user_note_field).removeAttr('class'); - $("#" + user_note_field).addClass(displayStyle(note.balance)); + $("#" + user_note_field).addClass(displayStyle(note)); $("#" + user_note_field).text(alias + (note.balance == null ? "" : (":\n" + pretty_money(note.balance)))); - if (profile_pic_field != null){ - $("#" + profile_pic_field).attr('src', img); - $("#" + profile_pic_field).click(function(){ - console.log(note); - if(note.resourcetype == "NoteUser"){ - document.location.href = "/accounts/user/" + note.user; - } - else if(note.resourcetype == "NoteClub"){ - document.location.href = "/accounts/club/" + note.club; - } - }); + if (profile_pic_field != null) { + $("#" + profile_pic_field).attr('src', img); + $("#" + profile_pic_field).click(function () { + console.log(note); + if (note.resourcetype === "NoteUser") { + document.location.href = "/accounts/user/" + note.user; + } else if (note.resourcetype === "NoteClub") { + document.location.href = "/accounts/club/" + note.club; + } + }); + } } } @@ -152,8 +153,8 @@ function displayNote(note, alias, user_note_field=null, profile_pic_field=null) * (useful in consumptions, put null if not used) * @returns an anonymous function to be compatible with jQuery events */ -function removeNote(d, note_prefix="note", notes_display, note_list_id, user_note_field=null, profile_pic_field=null) { - return (function() { +function removeNote(d, note_prefix = "note", notes_display, note_list_id, user_note_field = null, profile_pic_field = null) { + return (function () { let new_notes_display = []; let html = ""; notes_display.forEach(function (disp) { @@ -166,7 +167,7 @@ function removeNote(d, note_prefix="note", notes_display, note_list_id, user_not }); notes_display.length = 0; - new_notes_display.forEach(function(disp) { + new_notes_display.forEach(function (disp) { notes_display.push(disp); }); @@ -174,7 +175,7 @@ function removeNote(d, note_prefix="note", notes_display, note_list_id, user_not notes_display.forEach(function (disp) { let obj = $("#" + note_prefix + "_" + disp.id); obj.click(removeNote(disp, note_prefix, notes_display, note_list_id, user_note_field, profile_pic_field)); - obj.hover(function() { + obj.hover(function () { if (disp.note) displayNote(disp.note, disp.name, user_note_field, profile_pic_field); }); @@ -199,18 +200,18 @@ function removeNote(d, note_prefix="note", notes_display, note_list_id, user_not * the associated note is not displayed. * Useful for a consumption if the item is selected before. */ -function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes_display, alias_prefix="alias", - note_prefix="note", user_note_field=null, profile_pic_field=null, alias_click=null) { +function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes_display, alias_prefix = "alias", + note_prefix = "note", user_note_field = null, profile_pic_field = null, alias_click = null) { let field = $("#" + field_id); // When the user clicks on the search field, it is immediately cleared - field.click(function() { + field.click(function () { field.val(""); }); let old_pattern = null; // When the user type "Enter", the first alias is clicked - field.keypress(function(event) { + field.keypress(function (event) { if (event.originalEvent.charCode === 13 && notes.length > 0) { let li_obj = $("#" + alias_matched_id + " li").first(); displayNote(notes[0], li_obj.text(), user_note_field, profile_pic_field); @@ -219,7 +220,7 @@ function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes }); // When the user type something, the matched aliases are refreshed - field.keyup(function(e) { + field.keyup(function (e) { if (e.originalEvent.charCode === 13) return; @@ -233,7 +234,7 @@ function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes let aliases_matched_obj = $("#" + alias_matched_id); let aliases_matched_html = ""; // get matched Alias with note associated - if(pattern == ""){ + if (pattern === "") { aliases_matched_obj = $("#" + alias_matched_id); aliases_matched_html = ""; aliases_matched_obj.html("") @@ -242,81 +243,83 @@ function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes } $.getJSON("/api/note/consumer/?format=json&alias=" - + pattern - + "&search=user|club&ordering=normalized_name", - function(consumers){ - // The response arrived too late, we stop the request - if (pattern !== $("#" + field_id).val()) - return; - consumers.results.forEach(function (consumer){ - let note = consumer.note; - extra_css = displayStyle(note.balance); - aliases_matched_html += li(alias_prefix + '_' + consumer.id, - consumer.name, - extra_css); - notes.push(note); - }); - aliases_matched_obj.html(aliases_matched_html); - consumers.results.forEach(function(consumer){ - let note = consumer.note; - let consumer_obj = $("#" + alias_prefix+ "_" + consumer.id); - consumer_obj.hover(function(){ - displayNote(consumer.note, consumer.name, user_note_field,profile_pic_field) + + pattern + + "&search=user|club&ordering=normalized_name", + function (consumers) { + // The response arrived too late, we stop the request + if (pattern !== $("#" + field_id).val()) + return; + consumers.results.forEach(function (consumer) { + let note = consumer.note; + note.email_confirmed = consumer.email_confirmed; + let extra_css = displayStyle(note); + aliases_matched_html += li(alias_prefix + '_' + consumer.id, + consumer.name, + extra_css); + notes.push(note); }); - consumer_obj.click(function(){ - field.val(""); old_pattern = ""; // reset input field - var disp = null; - notes_display.forEach(function (d) { - // We compare the note ids - if (d.id === note.id) { - d.quantity += 1; - disp = d; + aliases_matched_obj.html(aliases_matched_html); + consumers.results.forEach(function (consumer) { + let note = consumer.note; + let consumer_obj = $("#" + alias_prefix + "_" + consumer.id); + consumer_obj.hover(function () { + displayNote(consumer.note, consumer.name, user_note_field, profile_pic_field) + }); + consumer_obj.click(function () { + field.val(""); + old_pattern = ""; // reset input field + var disp = null; + notes_display.forEach(function (d) { + // We compare the note ids + if (d.id === note.id) { + d.quantity += 1; + disp = d; + } + }); + // In the other case, we add a new emitter + if (disp == null) { + disp = { + name: consumer.name, + id: consumer.id, + note: note, + quantity: 1 + }; + notes_display.push(disp); } - }); - // In the other case, we add a new emitter - if (disp == null) { - disp = { - name: consumer.name, - id: consumer.id, - note: note, - quantity: 1 - }; - notes_display.push(disp); - } - // If the function alias_click exists, it is called. If it doesn't return true, then the notes are - // note displayed. Useful for a consumption when a button is already clicked - if (alias_click && !alias_click()) - return; + // If the function alias_click exists, it is called. If it doesn't return true, then the notes are + // note displayed. Useful for a consumption when a button is already clicked + if (alias_click && !alias_click()) + return; - let note_list = $("#" + note_list_id); - let html = ""; - notes_display.forEach(function (disp) { - html += li(note_prefix + "_" + disp.id, - disp.name - + "" - + disp.quantity + "", - displayStyle(disp.note.balance)); - }); - - // Emitters are displayed - note_list.html(html); - - notes_display.forEach(function (disp) { - let line_obj = $("#" + note_prefix + "_" + disp.id); - // Hover an emitter display also the profile picture - line_obj.hover(function () { - displayNote(disp.note, disp.name, user_note_field, profile_pic_field); + let note_list = $("#" + note_list_id); + let html = ""; + notes_display.forEach(function (disp) { + html += li(note_prefix + "_" + disp.id, + disp.name + + "" + + disp.quantity + "", + displayStyle(disp.note.balance)); }); - // When an emitter is clicked, it is removed - line_obj.click(removeNote(disp, note_prefix, notes_display, note_list_id, user_note_field, - profile_pic_field)); - }); - }) - }); + // Emitters are displayed + note_list.html(html); - });// end getJSON alias + notes_display.forEach(function (disp) { + let line_obj = $("#" + note_prefix + "_" + disp.id); + // Hover an emitter display also the profile picture + line_obj.hover(function () { + displayNote(disp.note, disp.name, user_note_field, profile_pic_field); + }); + + // When an emitter is clicked, it is removed + line_obj.click(removeNote(disp, note_prefix, notes_display, note_list_id, user_note_field, + profile_pic_field)); + }); + }) + }); + + });// end getJSON alias }); }// end function autocomplete @@ -347,7 +350,7 @@ function de_validate(id, validated) { // error if this method doesn't exist. Please define it. refreshHistory(); }, - error: function(err) { + error: function (err) { addMsg("Une erreur est survenue lors de la validation/dévalidation " + "de cette transaction : " + err.responseText, "danger"); diff --git a/templates/member/user_list.html b/templates/member/user_list.html index 0bcd7e89..68e23166 100644 --- a/templates/member/user_list.html +++ b/templates/member/user_list.html @@ -1,6 +1,7 @@ {% extends "base.html" %} {% load render_table from django_tables2 %} -{% load crispy_forms_tags%} +{% load crispy_forms_tags %} +{% load i18n %} {% block content %} From cf67e17e85dc565429df3151d7b5dbc1b7125315 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Fri, 10 Apr 2020 19:50:42 +0200 Subject: [PATCH 10/87] Do not use JavaScript to hide double conso after loading --- static/js/consos.js | 6 ++---- templates/note/conso_form.html | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/static/js/consos.js b/static/js/consos.js index 9450febc..943dbca3 100644 --- a/static/js/consos.js +++ b/static/js/consos.js @@ -26,7 +26,7 @@ $(document).ready(function() { // Switching in double consumptions mode should update the layout let double_conso_obj = $("#double_conso"); double_conso_obj.click(function() { - $("#consos_list_div").show(); + $("#consos_list_div").attr('class', 'col-xl-5'); $("#infos_div").attr('class', 'col-sm-5 col-xl-6'); $("#note_infos_div").attr('class', 'col-xl-3'); $("#user_select_div").attr('class', 'col-xl-4'); @@ -46,7 +46,7 @@ $(document).ready(function() { let single_conso_obj = $("#single_conso"); single_conso_obj.click(function() { - $("#consos_list_div").hide(); + $("#consos_list_div").attr('class', 'col-xl-5 d-none'); $("#infos_div").attr('class', 'col-sm-5 col-md-4'); $("#note_infos_div").attr('class', 'col-xl-5'); $("#user_select_div").attr('class', 'col-xl-7'); @@ -74,8 +74,6 @@ $(document).ready(function() { double_conso_obj.removeAttr('checked'); $("label[for='double_conso']").attr('class', 'btn btn-sm btn-outline-primary'); - $("#consos_list_div").hide(); - $("#consume_all").click(consumeAll); }); diff --git a/templates/note/conso_form.html b/templates/note/conso_form.html index 36659849..fd0923f2 100644 --- a/templates/note/conso_form.html +++ b/templates/note/conso_form.html @@ -42,7 +42,7 @@
    -
    +

    @@ -150,7 +150,7 @@ {% endblock %} {% block extrajavascript %} - + From b90ff59d989f360e47924d5af9a79d27b84bda1f Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Fri, 10 Apr 2020 22:46:57 +0200 Subject: [PATCH 21/87] Update tooltip position after new emitter --- static/js/base.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/static/js/base.js b/static/js/base.js index 02746302..af8eb8bf 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -317,6 +317,9 @@ function autoCompleteNote(field_id, note_list_id, notes, notes_display, alias_pr // Emitters are displayed note_list.html(html); + // Update tooltip position + field.tooltip('update'); + notes_display.forEach(function (disp) { let line_obj = $("#" + note_prefix + "_" + disp.id); // Hover an emitter display also the profile picture From 45c8fdf1c14a0b6a7e00644503d2851aed361914 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Fri, 10 Apr 2020 22:47:59 +0200 Subject: [PATCH 22/87] Revert margin for search field --- templates/note/conso_form.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/note/conso_form.html b/templates/note/conso_form.html index 35eff32c..d55da8b7 100644 --- a/templates/note/conso_form.html +++ b/templates/note/conso_form.html @@ -34,7 +34,7 @@

    {# User search with autocompletion #} -
    + From 5c1e3dd0ca58742c549f38008f70d326253d5eb4 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Fri, 10 Apr 2020 22:51:26 +0200 Subject: [PATCH 23/87] Make tooltips opaque again --- templates/base.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/base.html b/templates/base.html index 44bc8c82..7cfc474b 100644 --- a/templates/base.html +++ b/templates/base.html @@ -59,6 +59,9 @@ SPDX-License-Identifier: GPL-3.0-or-later cursor: pointer; text-decoration: underline; } + .tooltip.show { + opacity: 1; /* opaque tooltip */ + } .tooltip-inner { background-color: #fff; box-shadow: 0 .5rem 1rem rgba(0,0,0,.15); From 46b5f537bc7504cc2f2e42163a3df64da2a730a2 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Fri, 10 Apr 2020 23:04:25 +0200 Subject: [PATCH 24/87] Fix tooltip inner selecter --- static/js/base.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/base.js b/static/js/base.js index af8eb8bf..bad8ad97 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -223,7 +223,7 @@ function autoCompleteNote(field_id, note_list_id, notes, notes_display, alias_pr // When the user type "Enter", the first alias is clicked field.keypress(function (event) { if (event.originalEvent.charCode === 13 && notes.length > 0) { - let li_obj = $(".tooltip-inner ul li").first(); + let li_obj = field.parent().find("ul li").first(); displayNote(notes[0], li_obj.text(), user_note_field, profile_pic_field); li_obj.trigger("click"); } From 700ad7c9f706e00b49e034788b993157808e808c Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Fri, 10 Apr 2020 23:15:25 +0200 Subject: [PATCH 25/87] Make consume section more bootstrap --- templates/note/conso_form.html | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/templates/note/conso_form.html b/templates/note/conso_form.html index d55da8b7..89a92790 100644 --- a/templates/note/conso_form.html +++ b/templates/note/conso_form.html @@ -28,7 +28,7 @@ {% trans "Consum" %}

    -
    +
    @@ -48,11 +48,15 @@ {% trans "Select consumptions" %}

    -
      -
    - +
    +
      +
    +
    +
    From 7af76899e3591c03ccbf6fc4c33153f2ac5b1647 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Fri, 10 Apr 2020 23:40:19 +0200 Subject: [PATCH 26/87] Simplify grids --- static/js/consos.js | 8 ++------ templates/note/conso_form.html | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/static/js/consos.js b/static/js/consos.js index 11ad7302..2fa77249 100644 --- a/static/js/consos.js +++ b/static/js/consos.js @@ -26,10 +26,8 @@ $(document).ready(function() { // Switching in double consumptions mode should update the layout $("#double_conso").click(function() { $("#consos_list_div").removeClass('d-none'); - $("#infos_div").attr('class', 'col-sm-5 col-xl-6'); - $("#note_infos_div").attr('class', 'col-xl-3'); $("#user_select_div").attr('class', 'col-xl-4'); - $("#buttons_div").attr('class', 'col-sm-7 col-xl-6'); + $("#infos_div").attr('class', 'col-sm-5 col-xl-6'); let note_list_obj = $("#note_list"); if (buttons.length > 0 && note_list_obj.text().length > 0) { @@ -45,10 +43,8 @@ $(document).ready(function() { $("#single_conso").click(function() { $("#consos_list_div").addClass('d-none'); - $("#infos_div").attr('class', 'col-sm-5 col-md-4'); - $("#note_infos_div").attr('class', 'col-xl-5'); $("#user_select_div").attr('class', 'col-xl-7'); - $("#buttons_div").attr('class', 'col-sm-7 col-md-8'); + $("#infos_div").attr('class', 'col-sm-5 col-md-4'); let consos_list_obj = $("#consos_list"); if (buttons.length > 0) { diff --git a/templates/note/conso_form.html b/templates/note/conso_form.html index 89a92790..451d8a27 100644 --- a/templates/note/conso_form.html +++ b/templates/note/conso_form.html @@ -10,7 +10,7 @@
    {# User details column #} -
    +
    @@ -63,7 +63,7 @@
    {# Buttons column #} -
    +
    {# Show last used buttons #}
    From a609d7c9956790eda1e1d9b30ce096336c875950 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 10 Apr 2020 23:41:13 +0200 Subject: [PATCH 27/87] Fix credit/debit tabs --- apps/treasury/signals.py | 4 +- static/js/transfer.js | 65 ++++++++++++++++++++++++++++ templates/note/transaction_form.html | 52 +--------------------- 3 files changed, 69 insertions(+), 52 deletions(-) diff --git a/apps/treasury/signals.py b/apps/treasury/signals.py index 54c19c09..188be1a7 100644 --- a/apps/treasury/signals.py +++ b/apps/treasury/signals.py @@ -1,6 +1,7 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later +from note.models import NoteSpecial from treasury.models import SpecialTransactionProxy, RemittanceType @@ -8,5 +9,6 @@ def save_special_transaction(instance, created, **kwargs): """ When a special transaction is created, we create its linked proxy """ - if created and RemittanceType.objects.filter(note=instance.source).exists(): + if created and isinstance(instance.source, NoteSpecial) \ + and RemittanceType.objects.filter(note=instance.source).exists(): SpecialTransactionProxy.objects.create(transaction=instance, remittance=None).save() diff --git a/static/js/transfer.js b/static/js/transfer.js index 69fafac5..78a8ca08 100644 --- a/static/js/transfer.js +++ b/static/js/transfer.js @@ -34,6 +34,9 @@ $(document).ready(function() { if ($("#type_credit").is(":checked") || $("#type_debit").is(":checked")) { let arr = $("#type_credit").is(":checked") ? dests_notes_display : sources_notes_display; + if (arr.length === 0) + return; + let last = arr[arr.length - 1]; arr.length = 0; arr.push(last); @@ -65,6 +68,68 @@ $(document).ready(function() { autoCompleteNote("dest_note", "dest_note_list", dests, dests_notes_display, "dest_alias", "dest_note", "user_note", "profile_pic", checkUniqueNote); + let source = $("#source_note"); + let dest = $("#dest_note"); + + $("#type_gift").click(function() { + $("#special_transaction_div").addClass('d-none'); + source.attr('disabled', true); + source.val(username); + source.tooltip('hide'); + $("#source_note_list").addClass('d-none'); + dest.attr('disabled', false); + $("#dest_note_list").removeClass('d-none'); + }); + + $("#type_transfer").click(function() { + $("#special_transaction_div").addClass('d-none'); + source.attr('disabled', false); + $("#source_note_list").removeClass('d-none'); + dest.attr('disabled', false); + $("#dest_note_list").removeClass('d-none'); + }); + + $("#type_credit").click(function() { + $("#special_transaction_div").removeClass('d-none'); + $("#source_note_list").addClass('d-none'); + $("#dest_note_list").removeClass('d-none'); + source.attr('disabled', true); + source.val($("#credit_type option:selected").text()); + source.tooltip('hide'); + dest.attr('disabled', false); + dest.val(''); + dest.tooltip('hide'); + + if (dests_notes_display.length > 1) { + $("#dest_note_list").html(''); + dests_notes_display.length = 0; + } + }); + + $("#type_debit").click(function() { + $("#special_transaction_div").removeClass('d-none'); + $("#source_note_list").removeClass('d-none'); + $("#dest_note_list").addClass('d-none'); + source.attr('disabled', false); + source.val(''); + source.tooltip('hide'); + dest.attr('disabled', true); + dest.val($("#credit_type option:selected").text()); + dest.tooltip('hide'); + + if (sources_notes_display.length > 1) { + $("#source_note_list").html(''); + sources_notes_display.length = 0; + } + }); + + $("#credit_type").change(function() { + let type = $("#credit_type option:selected").text(); + if ($("#type_credit").is(":checked")) + source.val(type); + else + dest.val(type); + }); // Ensure we begin in gift mode. Removing these lines may cause problems when reloading. let type_gift = $("#type_gift"); // Default mode diff --git a/templates/note/transaction_form.html b/templates/note/transaction_form.html index d1cafa1f..c252f636 100644 --- a/templates/note/transaction_form.html +++ b/templates/note/transaction_form.html @@ -160,57 +160,7 @@ SPDX-License-Identifier: GPL-2.0-or-later TRANSFER_POLYMORPHIC_CTYPE = {{ polymorphic_ctype }}; SPECIAL_TRANSFER_POLYMORPHIC_CTYPE = {{ special_polymorphic_ctype }}; user_id = {{ user.note.pk }}; - - source = $("#source_note"); - dest = $("#dest_note"); - - $("#type_gift").click(function() { - $("#special_transaction_div").addClass('d-none'); - source.attr('disabled', true); - source.val("{{ user.username }}"); - source.tooltip('hide'); - $("#source_note_list").addClass('d-none'); - dest.attr('disabled', false); - $("#dest_note_list").removeClass('d-none'); - }); - - $("#type_transfer").click(function() { - $("#special_transaction_div").addClass('d-none'); - source.attr('disabled', false); - $("#source_note_list").removeClass('d-none'); - dest.attr('disabled', false); - $("#dest_note_list").removeClass('d-none'); - }); - - $("#type_credit").click(function() { - $("#special_transaction_div").removeClass('d-none'); - $("#source_note_list").addClass('d-none'); - $("#dest_note_list").removeClass('d-none'); - source.attr('disabled', true); - source.val($("#credit_type option:selected").text()); - source.tooltip('hide'); - dest.attr('disabled', false); - dest.val(''); - }); - - $("#type_debit").click(function() { - $("#special_transaction_div").removeClass('d-none'); - $("#source_note_list").removeClass('d-none'); - $("#dest_note_list").addClass('d-none'); - source.attr('disabled', false); - source.val(''); - dest.attr('disabled', true); - dest.val($("#credit_type option:selected").text()); - dest.tooltip('hide'); - }); - - $("#credit_type").change(function() { - let type = $("#credit_type option:selected").text(); - if ($("#type_credit").is(":checked")) - source.val(type); - else - dest.val(type); - }); + username = "{{ user.username }}"; {% endblock %} From d96c40a00f57823e310e2d1723cee794ce2e75aa Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 10 Apr 2020 23:46:16 +0200 Subject: [PATCH 28/87] Style was removed when a note was removed --- static/js/base.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/js/base.js b/static/js/base.js index bad8ad97..db12dae9 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -162,7 +162,8 @@ function removeNote(d, note_prefix = "note", notes_display, note_list_id, user_n disp.quantity -= disp.id === d.id ? 1 : 0; new_notes_display.push(disp); html += li(note_prefix + "_" + disp.id, disp.name - + "" + disp.quantity + ""); + + "" + disp.quantity + "", + displayStyle(disp.note)); } }); From 154ea64b9fb7a59470fbba0d3c94afb37781afb5 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sat, 11 Apr 2020 03:37:06 +0200 Subject: [PATCH 29/87] Launching WEI app, add models --- apps/activity/models.py | 4 +- apps/activity/views.py | 8 +- apps/treasury/views.py | 6 +- apps/wei/__init__.py | 4 + apps/wei/admin.py | 2 + apps/wei/apps.py | 11 ++ apps/wei/migrations/__init__.py | 0 apps/wei/models.py | 243 ++++++++++++++++++++++++++++++++ apps/wei/urls.py | 9 ++ apps/wei/views.py | 5 + note_kfet/settings/base.py | 1 + note_kfet/urls.py | 1 + 12 files changed, 285 insertions(+), 9 deletions(-) create mode 100644 apps/wei/__init__.py create mode 100644 apps/wei/admin.py create mode 100644 apps/wei/apps.py create mode 100644 apps/wei/migrations/__init__.py create mode 100644 apps/wei/models.py create mode 100644 apps/wei/urls.py create mode 100644 apps/wei/views.py diff --git a/apps/activity/models.py b/apps/activity/models.py index 29f04b39..cab229c4 100644 --- a/apps/activity/models.py +++ b/apps/activity/models.py @@ -139,7 +139,7 @@ class Entry(models.Model): verbose_name = _("entry") verbose_name_plural = _("entries") - def save(self, *args,**kwargs): + def save(self, *args, **kwargs): qs = Entry.objects.filter(~Q(pk=self.pk), activity=self.activity, note=self.note, guest=self.guest) if qs.exists(): @@ -153,7 +153,7 @@ class Entry(models.Model): if self.note.balance < 0: raise ValidationError(_("The balance is negative.")) - ret = super().save(*args,**kwargs) + ret = super().save(*args, **kwargs) if insert and self.guest: GuestTransaction.objects.create( diff --git a/apps/activity/views.py b/apps/activity/views.py index 14746929..12386bd1 100644 --- a/apps/activity/views.py +++ b/apps/activity/views.py @@ -45,8 +45,8 @@ class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView context['title'] = _("Activities") upcoming_activities = Activity.objects.filter(date_end__gt=datetime.now()) - context['upcoming'] = ActivityTable(data=upcoming_activities - .filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))) + context['upcoming'] = ActivityTable( + data=upcoming_activities.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))) return context @@ -153,9 +153,9 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView): context["title"] = _('Entry for activity "{}"').format(activity.name) context["noteuser_ctype"] = ContentType.objects.get_for_model(NoteUser).pk context["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk - + context["activities_open"] = Activity.objects.filter(open=True).filter( PermissionBackend.filter_queryset(self.request.user, Activity, "view")).filter( PermissionBackend.filter_queryset(self.request.user, Activity, "change")).all() - return context \ No newline at end of file + return context diff --git a/apps/treasury/views.py b/apps/treasury/views.py index 7361d1d2..8d744443 100644 --- a/apps/treasury/views.py +++ b/apps/treasury/views.py @@ -203,9 +203,9 @@ class RemittanceCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["table"] = RemittanceTable(data=Remittance.objects - .filter(PermissionBackend.filter_queryset(self.request.user, Remittance, "view")) - .all()) + context["table"] = RemittanceTable( + data=Remittance.objects.filter( + PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all()) context["special_transactions"] = SpecialTransactionTable(data=SpecialTransaction.objects.none()) return context diff --git a/apps/wei/__init__.py b/apps/wei/__init__.py new file mode 100644 index 00000000..ad360dae --- /dev/null +++ b/apps/wei/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +default_app_config = 'wei.apps.WeiConfig' diff --git a/apps/wei/admin.py b/apps/wei/admin.py new file mode 100644 index 00000000..4e945ad5 --- /dev/null +++ b/apps/wei/admin.py @@ -0,0 +1,2 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later diff --git a/apps/wei/apps.py b/apps/wei/apps.py new file mode 100644 index 00000000..f6332232 --- /dev/null +++ b/apps/wei/apps.py @@ -0,0 +1,11 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class WeiConfig(AppConfig): + name = 'wei' + verbose_name = _('WEI') + diff --git a/apps/wei/migrations/__init__.py b/apps/wei/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/wei/models.py b/apps/wei/models.py new file mode 100644 index 00000000..910f8309 --- /dev/null +++ b/apps/wei/models.py @@ -0,0 +1,243 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.contrib.auth.models import User +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from note.models import NoteSpecial + + +class WEI(models.Model): + """ + Store WEI information + """ + name = models.CharField( + max_length=255, + unique=True, + verbose_name=_("name"), + ) + + year = models.PositiveIntegerField( + unique=True, + verbose_name=_("year"), + ) + + start = models.DateField( + verbose_name=_("start date"), + ) + + end = models.DateField( + verbose_name=_("end date"), + ) + + price_paid = models.PositiveIntegerField( + verbose_name=_("Price for paid students"), + ) + + price_unpaid = models.PositiveIntegerField( + verbose_name=_("Price for unpaid students"), + ) + + email = models.EmailField( + verbose_name=_("contact email"), + ) + + registrations_open = models.BooleanField( + verbose_name=_("registrations open"), + ) + + def __str__(self): + return self.name + + class Meta: + verbose_name = _("WEI") + verbose_name_plural = _("WEI") + + +class Bus(models.Model): + """ + The best bus for the best WEI + """ + wei = models.ForeignKey( + WEI, + on_delete=models.PROTECT, + related_name="buses", + verbose_name=_("WEI"), + ) + + name = models.CharField( + max_length=255, + verbose_name=_("name"), + ) + + def __str__(self): + return self.name + + class Meta: + unique_together = ('wei', 'name',) + + +class BusTeam(models.Model): + """ + A bus has multiple teams + """ + bus = models.ForeignKey( + Bus, + on_delete=models.CASCADE, + related_name="teams", + verbose_name=_("bus"), + ) + + name = models.CharField( + max_length=255, + ) + + color = models.PositiveIntegerField( # Use a color picker to get the hexa code + verbose_name=_("color"), + help_text=_("The color of the T-Shirt, stored with its number equivalent"), + ) + + def __str__(self): + return self.name + " (" + str(self.bus) + ")" + + class Meta: + unique_together = ('bus', 'name',) + verbose_name = _("Bus team") + verbose_name_plural = _("Bus teams") + + +class WEIRole(models.Model): + """ + A Role for the WEI can be bus chief, team chief, free electron, ... + """ + name = models.CharField( + max_length=255, + unique=True, + ) + + +class WEIUser(models.Model): + """ + Store personal data that can be useful for the WEI. + """ + + user = models.ForeignKey( + User, + on_delete=models.PROTECT, + related_name="wei", + verbose_name=_("user"), + ) + + wei = models.ForeignKey( + WEI, + on_delete=models.PROTECT, + related_name="users", + verbose_name=_("WEI"), + ) + + role = models.ForeignKey( + WEIRole, + on_delete=models.PROTECT, + verbose_name=_("role"), + ) + + birth_date = models.DateField( + verbose_name=_("birth date"), + ) + + gender = models.CharField( + max_length=16, + choices=( + ('male', _("Male")), + ('female', _("Female")), + ('nonbinary', _("Non binary")), + ), + verbose_name=_("gender"), + ) + + health_issues = models.TextField( + verbose_name=_("health issues"), + ) + + emergency_contact_name = models.CharField( + max_length=255, + verbose_name=_("emergency contact name"), + ) + + emergency_contact_phone = models.CharField( + max_length=32, + verbose_name=_("emergency contact phone"), + ) + + payment_method = models.ForeignKey( + NoteSpecial, + on_delete=models.PROTECT, + null=True, # null = no credit, paid with note + related_name="+", + verbose_name=_("payment method"), + ) + + soge_credit = models.BooleanField( + verbose_name=_("Credit from Société générale"), + ) + + ml_events_registation = models.BooleanField( + verbose_name=_("Register on the mailing list to stay informed of the events of the campus (1 mail/week)"), + ) + + ml_sport_registation = models.BooleanField( + verbose_name=_("Register on the mailing list to stay informed of the sport events of the campus (1 mail/week)"), + ) + + ml_art_registation = models.BooleanField( + verbose_name=_("Register on the mailing list to stay informed of the art events of the campus (1 mail/week)"), + ) + + team = models.ForeignKey( + BusTeam, + on_delete=models.PROTECT, + related_name="users", + null=True, + blank=True, + verbose_name=_("team"), + ) + + bus_choice1 = models.ForeignKey( + Bus, + on_delete=models.PROTECT, + related_name="+", + verbose_name=_("bus choice 1"), + ) + + bus_choice2 = models.ForeignKey( + Bus, + on_delete=models.PROTECT, + related_name="+", + null=True, + blank=True, + verbose_name=_("bus choice 2"), + ) + + bus_choice3 = models.ForeignKey( + Bus, + on_delete=models.PROTECT, + related_name="+", + null=True, + blank=True, + verbose_name=_("bus choice 3"), + ) + + asked_roles = models.ManyToManyField( + WEIRole, + related_name="+", + verbose_name=_("asked roles"), + ) + + def __str__(self): + return str(self.user) + + class Meta: + unique_together = ('user', 'wei',) + verbose_name = _("WEI User") + verbose_name_plural = _("WEI Users") diff --git a/apps/wei/urls.py b/apps/wei/urls.py new file mode 100644 index 00000000..afd1c566 --- /dev/null +++ b/apps/wei/urls.py @@ -0,0 +1,9 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.urls import path + + +app_name = 'wei' +urlpatterns = [ +] diff --git a/apps/wei/views.py b/apps/wei/views.py new file mode 100644 index 00000000..8b245779 --- /dev/null +++ b/apps/wei/views.py @@ -0,0 +1,5 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.shortcuts import render + diff --git a/note_kfet/settings/base.py b/note_kfet/settings/base.py index 283f8e56..5ed8b1d8 100644 --- a/note_kfet/settings/base.py +++ b/note_kfet/settings/base.py @@ -62,6 +62,7 @@ INSTALLED_APPS = [ 'permission', 'registration', 'treasury', + 'wei', ] LOGIN_REDIRECT_URL = '/note/transfer/' diff --git a/note_kfet/urls.py b/note_kfet/urls.py index 90d44a07..4311c0b5 100644 --- a/note_kfet/urls.py +++ b/note_kfet/urls.py @@ -19,6 +19,7 @@ urlpatterns = [ path('registration/', include('registration.urls')), path('activity/', include('activity.urls')), path('treasury/', include('treasury.urls')), + path('wei/', include('wei.urls')), # Include Django Contrib and Core routers path('i18n/', include('django.conf.urls.i18n')), From a186ccbb269faa6b1b4a17c95e8c34228e73cfd0 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sat, 11 Apr 2020 17:42:08 +0200 Subject: [PATCH 30/87] Update WEI models --- apps/member/models.py | 1 + apps/wei/models.py | 181 +++++++++++++++++++----------------------- 2 files changed, 84 insertions(+), 98 deletions(-) diff --git a/apps/member/models.py b/apps/member/models.py index 3a022434..35406d54 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -171,6 +171,7 @@ class Club(models.Model): self.membership_start.month, self.membership_start.day) self.membership_end = datetime.date(self.membership_end.year + 1, self.membership_end.month, self.membership_end.day) + self._force_save = True self.save(force_update=True) def save(self, force_insert=False, force_update=False, using=None, diff --git a/apps/wei/models.py b/apps/wei/models.py index 910f8309..45834256 100644 --- a/apps/wei/models.py +++ b/apps/wei/models.py @@ -1,58 +1,28 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later +import json from django.contrib.auth.models import User from django.db import models from django.utils.translation import gettext_lazy as _ +from member.models import Role, Club, Membership from note.models import NoteSpecial -class WEI(models.Model): +class WEIClub(Club): """ - Store WEI information """ - name = models.CharField( - max_length=255, - unique=True, - verbose_name=_("name"), - ) - year = models.PositiveIntegerField( unique=True, verbose_name=_("year"), ) - start = models.DateField( - verbose_name=_("start date"), - ) - - end = models.DateField( - verbose_name=_("end date"), - ) - - price_paid = models.PositiveIntegerField( - verbose_name=_("Price for paid students"), - ) - - price_unpaid = models.PositiveIntegerField( - verbose_name=_("Price for unpaid students"), - ) - - email = models.EmailField( - verbose_name=_("contact email"), - ) - - registrations_open = models.BooleanField( - verbose_name=_("registrations open"), - ) - - def __str__(self): - return self.name - - class Meta: - verbose_name = _("WEI") - verbose_name_plural = _("WEI") + def update_membership_dates(self): + """ + We can't join the WEI next years. + """ + return class Bus(models.Model): @@ -60,7 +30,7 @@ class Bus(models.Model): The best bus for the best WEI """ wei = models.ForeignKey( - WEI, + WEIClub, on_delete=models.PROTECT, related_name="buses", verbose_name=_("WEI"), @@ -107,17 +77,19 @@ class BusTeam(models.Model): verbose_name_plural = _("Bus teams") -class WEIRole(models.Model): +class WEIRole(Role): """ A Role for the WEI can be bus chief, team chief, free electron, ... """ - name = models.CharField( - max_length=255, - unique=True, + bus = models.ForeignKey( + Bus, + on_delete=models.CASCADE, + related_name="roles", + verbose_name=_("bus"), ) -class WEIUser(models.Model): +class WEIRegistration(models.Model): """ Store personal data that can be useful for the WEI. """ @@ -130,16 +102,22 @@ class WEIUser(models.Model): ) wei = models.ForeignKey( - WEI, + WEIClub, on_delete=models.PROTECT, related_name="users", verbose_name=_("WEI"), ) - role = models.ForeignKey( - WEIRole, + payment_method = models.ForeignKey( + NoteSpecial, on_delete=models.PROTECT, - verbose_name=_("role"), + null=True, # null = no credit, paid with note + related_name="+", + verbose_name=_("payment method"), + ) + + soge_credit = models.BooleanField( + verbose_name=_("Credit from Société générale"), ) birth_date = models.DateField( @@ -170,69 +148,45 @@ class WEIUser(models.Model): verbose_name=_("emergency contact phone"), ) - payment_method = models.ForeignKey( - NoteSpecial, - on_delete=models.PROTECT, - null=True, # null = no credit, paid with note - related_name="+", - verbose_name=_("payment method"), - ) - - soge_credit = models.BooleanField( - verbose_name=_("Credit from Société générale"), - ) - - ml_events_registation = models.BooleanField( + ml_events_registration = models.BooleanField( verbose_name=_("Register on the mailing list to stay informed of the events of the campus (1 mail/week)"), ) - ml_sport_registation = models.BooleanField( + ml_sport_registration = models.BooleanField( verbose_name=_("Register on the mailing list to stay informed of the sport events of the campus (1 mail/week)"), ) - ml_art_registation = models.BooleanField( + ml_art_registration = models.BooleanField( verbose_name=_("Register on the mailing list to stay informed of the art events of the campus (1 mail/week)"), ) - team = models.ForeignKey( - BusTeam, - on_delete=models.PROTECT, - related_name="users", - null=True, - blank=True, - verbose_name=_("team"), + information_json = models.TextField( + verbose_name=_("registration information"), + help_text=_("Information about the registration (buses for old members, survey fot the new members), " + "encoded in JSON"), ) - bus_choice1 = models.ForeignKey( - Bus, - on_delete=models.PROTECT, - related_name="+", - verbose_name=_("bus choice 1"), - ) + @property + def information(self): + """ + The information about the registration (the survey for the new members, the bus for the older members, ...) + are stored in a dictionary that can evolve following the years. The dictionary is stored as a JSON string. + """ + return json.loads(self.information_json) - bus_choice2 = models.ForeignKey( - Bus, - on_delete=models.PROTECT, - related_name="+", - null=True, - blank=True, - verbose_name=_("bus choice 2"), - ) + @information.setter + def information(self, information): + """ + Store information as a JSON string + """ + self.information_json = json.dumps(information) - bus_choice3 = models.ForeignKey( - Bus, - on_delete=models.PROTECT, - related_name="+", - null=True, - blank=True, - verbose_name=_("bus choice 3"), - ) - - asked_roles = models.ManyToManyField( - WEIRole, - related_name="+", - verbose_name=_("asked roles"), - ) + @property + def is_1A(self): + """ + We assume that a user is a new member if it not fully registered yet. + """ + return not self.user.profile.registration_valid def __str__(self): return str(self.user) @@ -241,3 +195,34 @@ class WEIUser(models.Model): unique_together = ('user', 'wei',) verbose_name = _("WEI User") verbose_name_plural = _("WEI Users") + + +class WEIMembership(Membership): + bus = models.ForeignKey( + Bus, + on_delete=models.PROTECT, + null=True, + default=None, + verbose_name=_("bus"), + ) + + team = models.ForeignKey( + BusTeam, + on_delete=models.PROTECT, + related_name="memberships", + null=True, + blank=True, + default=None, + verbose_name=_("team"), + ) + + registration = models.OneToOneField( + WEIRegistration, + on_delete=models.PROTECT, + null=True, + blank=True, + default=None, + related_name="membership", + verbose_name=_("WEI registration"), + ) + From 31d2224b8ffe64d07d60fb5ab34781237b068d11 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sat, 11 Apr 2020 23:02:12 +0200 Subject: [PATCH 31/87] List wei --- apps/api/urls.py | 2 + apps/wei/api/__init__.py | 0 apps/wei/api/serializers.py | 17 ++++++++ apps/wei/api/urls.py | 11 +++++ apps/wei/api/views.py | 20 +++++++++ apps/wei/fixtures/initial.json | 76 +++++++++++++++++++++++++++++++++ apps/wei/models.py | 13 +++++- apps/wei/tables.py | 25 +++++++++++ apps/wei/urls.py | 3 ++ apps/wei/views.py | 14 +++++- static/js/base.js | 2 +- templates/base.html | 5 ++- templates/member/club_info.html | 4 +- templates/wei/weiclub_list.html | 70 ++++++++++++++++++++++++++++++ tox.ini | 2 +- 15 files changed, 257 insertions(+), 7 deletions(-) create mode 100644 apps/wei/api/__init__.py create mode 100644 apps/wei/api/serializers.py create mode 100644 apps/wei/api/urls.py create mode 100644 apps/wei/api/views.py create mode 100644 apps/wei/fixtures/initial.json create mode 100644 apps/wei/tables.py create mode 100644 templates/wei/weiclub_list.html diff --git a/apps/api/urls.py b/apps/api/urls.py index 67fdba30..03d6bd68 100644 --- a/apps/api/urls.py +++ b/apps/api/urls.py @@ -15,6 +15,7 @@ from note.api.urls import register_note_urls from treasury.api.urls import register_treasury_urls from logs.api.urls import register_logs_urls from permission.api.urls import register_permission_urls +from wei.api.urls import register_wei_urls class UserSerializer(serializers.ModelSerializer): @@ -78,6 +79,7 @@ register_note_urls(router, 'note') register_treasury_urls(router, 'treasury') register_permission_urls(router, 'permission') register_logs_urls(router, 'logs') +register_wei_urls(router, 'wei') app_name = 'api' diff --git a/apps/wei/api/__init__.py b/apps/wei/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/wei/api/serializers.py b/apps/wei/api/serializers.py new file mode 100644 index 00000000..5b91e2b1 --- /dev/null +++ b/apps/wei/api/serializers.py @@ -0,0 +1,17 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from rest_framework import serializers + +from ..models import WEIClub + + +class WEIClubSerializer(serializers.ModelSerializer): + """ + REST API Serializer for Clubs. + The djangorestframework plugin will analyse the model `WEIClub` and parse all fields in the API. + """ + + class Meta: + model = WEIClub + fields = '__all__' diff --git a/apps/wei/api/urls.py b/apps/wei/api/urls.py new file mode 100644 index 00000000..f5836b8c --- /dev/null +++ b/apps/wei/api/urls.py @@ -0,0 +1,11 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from .views import WEIClubViewSet + + +def register_wei_urls(router, path): + """ + Configure router for Member REST API. + """ + router.register(path + '/club', WEIClubViewSet) diff --git a/apps/wei/api/views.py b/apps/wei/api/views.py new file mode 100644 index 00000000..c93512e2 --- /dev/null +++ b/apps/wei/api/views.py @@ -0,0 +1,20 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from rest_framework.filters import SearchFilter +from api.viewsets import ReadProtectedModelViewSet + +from .serializers import WEIClubSerializer +from ..models import WEIClub + + +class WEIClubViewSet(ReadProtectedModelViewSet): + """ + REST API View set. + The djangorestframework plugin will get all `WEIClub` objects, serialize it to JSON with the given serializer, + then render it on /api/wei/club/ + """ + queryset = WEIClub.objects.all() + serializer_class = WEIClubSerializer + filter_backends = [SearchFilter] + search_fields = ['$name', ] diff --git a/apps/wei/fixtures/initial.json b/apps/wei/fixtures/initial.json new file mode 100644 index 00000000..db371985 --- /dev/null +++ b/apps/wei/fixtures/initial.json @@ -0,0 +1,76 @@ +[ + { + "model": "member.club", + "pk": 3, + "fields": { + "name": "A[WEI] from Home", + "email": "gc.wei@example.com", + "parent_club": 2, + "require_memberships": true, + "membership_fee_paid": 16500, + "membership_fee_unpaid": 9500, + "membership_duration": 30, + "membership_start": "2019-09-01", + "membership_end": "2019-09-16" + } + }, + { + "model": "wei.weiclub", + "pk": 1, + "fields": { + "club_ptr_id": 3, + "year": 2019, + "date_start": "2019-09-14", + "date_end": "2019-09-16" + } + }, + { + "model": "note.note", + "pk": 7, + "fields": { + "polymorphic_ctype": [ + "note", + "noteclub" + ], + "balance": 0, + "last_negative": null, + "is_active": true, + "display_image": "pic/default.png", + "created_at": "2020-02-20T20:16:14.753Z" + } + }, + { + "model": "note.noteclub", + "pk": 7, + "fields": { + "club": 3 + } + }, + { + "model": "note.alias", + "pk": 7, + "fields": { + "name": "A[WEI] from Home", + "normalized_name": "aweifromhome", + "note": 7 + } + }, + { + "model": "note.alias", + "pk": 8, + "fields": { + "name": "WEI 2019", + "normalized_name": "wei2019", + "note": 7 + } + }, + { + "model": "note.alias", + "pk": 9, + "fields": { + "name": "WEI", + "normalized_name": "wei", + "note": 7 + } + } +] diff --git a/apps/wei/models.py b/apps/wei/models.py index 45834256..08ebbc8c 100644 --- a/apps/wei/models.py +++ b/apps/wei/models.py @@ -1,23 +1,32 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later + import json from django.contrib.auth.models import User from django.db import models from django.utils.translation import gettext_lazy as _ - from member.models import Role, Club, Membership from note.models import NoteSpecial class WEIClub(Club): """ + The WEI is a club. Register to the WEI is equivalent than be member of the club. """ year = models.PositiveIntegerField( unique=True, verbose_name=_("year"), ) + date_start = models.DateField( + verbose_name=_("date start"), + ) + + date_end = models.DateField( + verbose_name=_("date end"), + ) + def update_membership_dates(self): """ We can't join the WEI next years. @@ -84,6 +93,8 @@ class WEIRole(Role): bus = models.ForeignKey( Bus, on_delete=models.CASCADE, + null=True, + default=None, related_name="roles", verbose_name=_("bus"), ) diff --git a/apps/wei/tables.py b/apps/wei/tables.py new file mode 100644 index 00000000..3c1bd3af --- /dev/null +++ b/apps/wei/tables.py @@ -0,0 +1,25 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +import django_tables2 as tables +from django.urls import reverse_lazy + +from wei.models import WEIClub + + +class WEITable(tables.Table): + """ + List all clubs. + """ + class Meta: + attrs = { + 'class': 'table table-condensed table-striped table-hover' + } + model = WEIClub + template_name = 'django_tables2/bootstrap4.html' + fields = ('name', 'year', 'date_start', 'date_end',) + row_attrs = { + 'class': 'table-row', + 'id': lambda record: "row-" + str(record.pk), + 'data-href': lambda record: reverse_lazy('member:club_detail', args=(record.pk,)) + } diff --git a/apps/wei/urls.py b/apps/wei/urls.py index afd1c566..9c898139 100644 --- a/apps/wei/urls.py +++ b/apps/wei/urls.py @@ -3,7 +3,10 @@ from django.urls import path +from .views import WEIListView + app_name = 'wei' urlpatterns = [ + path('list/', WEIListView.as_view(), name="wei_list"), ] diff --git a/apps/wei/views.py b/apps/wei/views.py index 8b245779..7d7fc01f 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -1,5 +1,17 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later -from django.shortcuts import render +from django.contrib.auth.mixins import LoginRequiredMixin +from django_tables2 import SingleTableView +from permission.views import ProtectQuerysetMixin +from wei.models import WEIClub +from .tables import WEITable + + +class WEIListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): + """ + List existing WEI + """ + model = WEIClub + table_class = WEITable diff --git a/static/js/base.js b/static/js/base.js index bb73b328..f9f040c1 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -312,7 +312,7 @@ function in_validate(id, validated) { else invalidity_reason = null; - $("#validate_" + id).html("⟳ ..."); + $("#validate_" + id).html(""); // Perform a PATCH request to the API in order to update the transaction // If the user has insuffisent rights, an error message will appear diff --git a/templates/base.html b/templates/base.html index 3c2c637f..810927f9 100644 --- a/templates/base.html +++ b/templates/base.html @@ -108,9 +108,12 @@ SPDX-License-Identifier: GPL-3.0-or-later {% endif %} {% if "treasury.invoice"|not_empty_model_change_list %} {% endif %} +
    + +
    + + + + {% endif %} diff --git a/templates/wei/weilist_sample.tex b/templates/wei/weilist_sample.tex new file mode 100644 index 00000000..f22659a4 --- /dev/null +++ b/templates/wei/weilist_sample.tex @@ -0,0 +1,37 @@ +\documentclass{article} + +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage[french]{babel} + +\usepackage[margin=2cm]{geometry} +\usepackage{tabularx} + +\begin{document} +\begin{center} +\huge{Liste des inscrits \og {{ wei.name }} \fg{}} + +{% if bus %} +\LARGE{Bus {{ bus.name }}} + + +{% if team %} +\Large{Équipe {{ team.name }}} +{% endif %} +{% endif %} +\end{center} + +\begin{center} +\begin{tabularx}{\textwidth}{|c|c|c|c|c|c|c|X|} +\hline +\textbf{Nom} & \textbf{Prénom} & \textbf{Genre} & \textbf{Département} & \textbf{Année} & \textbf{Bus} & \textbf{Équipe} & \textbf{Rôles} \\ +\hline +{% for membership in memberships %} +{{ membership.user.last_name|safe }} & {{ membership.user.first_name|safe }} & {{ membership.registration.get_gender_display|safe }} +& {{ membership.user.profile.get_department_display|safe }} & {{ membership.user.profile.ens_year|safe }}A & {{ membership.bus.name|safe }} +& {% if membership.team %}{{ membership.team.name|safe }}{% else %}--{% endif %} & {{ membership.roles.all|join:", "|safe }} \\ +\hline +{% endfor %} +\end{tabularx} +\end{center} +\end{document} diff --git a/templates/wei/weimembership_list.html b/templates/wei/weimembership_list.html index 461c988f..d058211f 100644 --- a/templates/wei/weimembership_list.html +++ b/templates/wei/weimembership_list.html @@ -23,6 +23,12 @@ + +
    + + + + {% endblock %} {% block extrajavascript %} From d4ddbc3565e212536a93201826f53156ca4b314b Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 23 Apr 2020 21:13:33 +0200 Subject: [PATCH 69/87] Add script to export the WEI registrations as CSV format --- templates/wei/weilist_sample.tex | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/templates/wei/weilist_sample.tex b/templates/wei/weilist_sample.tex index f22659a4..98fc9ee4 100644 --- a/templates/wei/weilist_sample.tex +++ b/templates/wei/weilist_sample.tex @@ -1,10 +1,11 @@ -\documentclass{article} +\documentclass[landscape,10pt]{article} \usepackage[utf8]{inputenc} \usepackage[T1]{fontenc} \usepackage[french]{babel} -\usepackage[margin=2cm]{geometry} +\usepackage[margin=1.5cm]{geometry} +\usepackage{ltablex} \usepackage{tabularx} \begin{document} @@ -22,16 +23,29 @@ \end{center} \begin{center} -\begin{tabularx}{\textwidth}{|c|c|c|c|c|c|c|X|} -\hline -\textbf{Nom} & \textbf{Prénom} & \textbf{Genre} & \textbf{Département} & \textbf{Année} & \textbf{Bus} & \textbf{Équipe} & \textbf{Rôles} \\ -\hline +\footnotesize +\begin{tabularx}{\textwidth}{ccccccccc} +\textbf{Nom} & \textbf{Prénom} & \textbf{Date de naissance} & \textbf{Genre} & \textbf{Section} + & \textbf{Bus} & \textbf{Équipe} & \textbf{Rôles} \\ {% for membership in memberships %} -{{ membership.user.last_name|safe }} & {{ membership.user.first_name|safe }} & {{ membership.registration.get_gender_display|safe }} -& {{ membership.user.profile.get_department_display|safe }} & {{ membership.user.profile.ens_year|safe }}A & {{ membership.bus.name|safe }} +{{ membership.user.last_name|safe }} & {{ membership.user.first_name|safe }} & {{ membership.registration.birth_date|safe }} +& {{ membership.registration.get_gender_display|safe }} & {{ membership.user.profile.section_generated|safe }} & {{ membership.bus.name|safe }} & {% if membership.team %}{{ membership.team.name|safe }}{% else %}--{% endif %} & {{ membership.roles.all|join:", "|safe }} \\ -\hline {% endfor %} \end{tabularx} \end{center} + +\footnotesize +Section = Année à l'ENS + code du département + +\begin{center} +\begin{tabular}{ccccccccc} +\textbf{Code} & A0 & A1 & A2 & A'2 & A''2 & A3 & B1234 & B1 \\ +\textbf{Département} & Informatique & Maths & Physique & Physique appliquée & Chimie & Biologie & SAPHIRE & Mécanique \\ +\hline +\textbf{Code} & B2 & B3 & B4 & C & D2 & D3 & E & EXT \\ +\textbf{Département} & Génie civil & Génie mécanique & EEA & Design & Éco-gestion & Sciences sociales & Anglais & Extérieur +\end{tabular} +\end{center} + \end{document} From 60b579f7c6f966462c30bcc35ac01c49dec76e4c Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 23 Apr 2020 21:16:14 +0200 Subject: [PATCH 70/87] Update the generation of the PDF files for WEI registrations (now borderless and smaller) --- apps/scripts | 2 +- apps/wei/views.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/scripts b/apps/scripts index 8f2976b4..8b903808 160000 --- a/apps/scripts +++ b/apps/scripts @@ -1 +1 @@ -Subproject commit 8f2976b43293b0163f5aad0fcdb5c35eb38b70dc +Subproject commit 8b90380866b2ce4ed55bbc9a181463bcd1282ccd diff --git a/apps/wei/views.py b/apps/wei/views.py index 93edb491..c6ac0aa8 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -86,7 +86,7 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): club = context["club"] club_transactions = Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note)) \ - .filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view"))\ + .filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view")) \ .order_by('-created_at', '-id') history_table = HistoryTable(club_transactions, prefix="history-") history_table.paginate(per_page=20, page=self.request.GET.get('history-page', 1)) @@ -116,7 +116,7 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): my_registration = None context["my_registration"] = my_registration - buses = Bus.objects.filter(PermissionBackend.filter_queryset(self.request.user, Bus, "view"))\ + buses = Bus.objects.filter(PermissionBackend.filter_queryset(self.request.user, Bus, "view")) \ .filter(wei=self.object).annotate(count=Count("memberships")) bus_table = BusTable(data=buses, prefix="bus-") context['buses'] = bus_table @@ -307,7 +307,7 @@ class BusManageView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): context["club"] = self.object.wei bus = self.object - teams = BusTeam.objects.filter(PermissionBackend.filter_queryset(self.request.user, BusTeam, "view"))\ + teams = BusTeam.objects.filter(PermissionBackend.filter_queryset(self.request.user, BusTeam, "view")) \ .filter(bus=bus).annotate(count=Count("memberships")) teams_table = BusTeamTable(data=teams, prefix="team-") context["teams"] = teams_table From edf2f793e226fc79834c7a1c499d67d82044216f Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 23 Apr 2020 21:29:43 +0200 Subject: [PATCH 71/87] Fields were missing in the generated CSV, better to be coherent with the header --- apps/scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/scripts b/apps/scripts index 8b903808..26be04c4 160000 --- a/apps/scripts +++ b/apps/scripts @@ -1 +1 @@ -Subproject commit 8b90380866b2ce4ed55bbc9a181463bcd1282ccd +Subproject commit 26be04c435b782b0d3561466a683152abed79d30 From 16079e3cc5b1d71eee3ee00b25ed2b9bb175a8e7 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 24 Apr 2020 14:03:25 +0200 Subject: [PATCH 72/87] Fix broken WEI permissions --- apps/permission/fixtures/initial.json | 249 ++++++++++++++++++++++---- apps/wei/views.py | 23 ++- templates/base.html | 4 +- templates/wei/weilist_sample.tex | 2 +- 4 files changed, 232 insertions(+), 46 deletions(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 7e7709e3..ce0fcd3d 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -118,6 +118,13 @@ "name": "1A" } }, + { + "model": "member.role", + "pk": 18, + "fields": { + "name": "Adhérent WEI" + } + }, { "model": "wei.weirole", "pk": 12, @@ -148,6 +155,11 @@ "pk": 17, "fields": {} }, + { + "model": "wei.weirole", + "pk": 18, + "fields": {} + }, { "model": "permission.permissionmask", "pk": 1, @@ -1320,11 +1332,11 @@ "wei", "weiclub" ], - "query": "{}", + "query": "{\"pk\": [\"club\", \"pk\"]}", "type": "view", "mask": 1, "field": "", - "description": "View all WEI" + "description": "View my WEI" } }, { @@ -1410,7 +1422,7 @@ "wei", "weiregistration" ], - "query": "{\"user\": [\"user\"], \"wei\": [\"club\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"], \"membership\": null}", + "query": "{\"user\": [\"user\"], \"wei__year\": [\"today\", \"year\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"], \"membership\": null}", "type": "add", "mask": 1, "field": "", @@ -1650,7 +1662,7 @@ "wei", "weiregistration" ], - "query": "{\"user\": [\"user\"], \"club\": [\"club\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"]}", + "query": "{\"user\": [\"user\"], \"wei\": [\"club\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"]}", "type": "change", "mask": 1, "field": "emergency_contact_name", @@ -1680,7 +1692,7 @@ "wei", "weiregistration" ], - "query": "{\"user\": [\"user\"], \"club\": [\"club\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"]}", + "query": "{\"user\": [\"user\"], \"wei\": [\"club\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"]}", "type": "change", "mask": 1, "field": "emergency_contact_phone", @@ -1725,7 +1737,7 @@ "wei", "bus" ], - "query": "{\"wei\": [\"club\"]}", + "query": "{\"wei\": [\"club\"], \"wei__membership_end__gte\": [\"today\"]}", "type": "change", "mask": 3, "field": "name", @@ -1785,7 +1797,7 @@ "wei", "bus" ], - "query": "[\"AND\", {\"wei\": [\"club\"]}, [\"OR\", {\"first_year\": false, \"club__weiclub__end_date__lte\": [\"today\"]}]]", + "query": "[\"AND\", {\"wei\": [\"club\"]}, [\"OR\", [\"NOT\", [\"membership\", \"registration\", \"first_year\"]], {\"wei__date_end__lte\": [\"today\"]}]]", "type": "view", "mask": 1, "field": "", @@ -1800,7 +1812,7 @@ "wei", "busteam" ], - "query": "[\"AND\", {\"wei\": [\"club\"]}, [\"OR\", {\"first_year\": false, \"club__weiclub__end_date__lte\": [\"today\"]}]]", + "query": "[\"AND\", {\"wei\": [\"club\"]}, [\"OR\", [\"NOT\", [\"membership\", \"registration\", \"first_year\"]], {\"wei__date_end__lte\": [\"today\"]}]]", "type": "view", "mask": 1, "field": "", @@ -1815,7 +1827,7 @@ "wei", "weimembership" ], - "query": "[\"AND\", {\"wei\": [\"club\"], \"wei__membership_end__gte\": [\"today\"]}, [\"OR\", {\"registration__soge_credit\": true}, {\"user__note__balance__gte\": [\"F\", \"fee\"]}]]", + "query": "[\"AND\", {\"club\": [\"club\"], \"club__weiclub__membership_end__gte\": [\"today\"]}, [\"OR\", {\"registration__soge_credit\": true}, {\"user__note__balance__gte\": [\"F\", \"fee\"]}]]", "type": "add", "mask": 3, "field": "", @@ -1830,7 +1842,7 @@ "wei", "weimembership" ], - "query": "{\"wei\": [\"club\"], \"wei__membership_end__gte\": [\"today\"]}", + "query": "{\"club\": [\"club\"], \"club__weiclub__membership_end__gte\": [\"today\"]}", "type": "change", "mask": 1, "field": "bus", @@ -1845,7 +1857,7 @@ "wei", "weimembership" ], - "query": "{\"wei\": [\"club\"], \"wei__membership_end__gte\": [\"today\"]}", + "query": "{\"club\": [\"club\"], \"club__weiclub__membership_end__gte\": [\"today\"]}", "type": "change", "mask": 1, "field": "team", @@ -1860,7 +1872,7 @@ "wei", "weimembership" ], - "query": "{\"wei\": [\"club\"]}", + "query": "{\"club\": [\"club\"]}", "type": "view", "mask": 1, "field": "", @@ -1875,7 +1887,7 @@ "wei", "weimembership" ], - "query": "[\"AND\", {\"user\": [\"user\"], \"wei\": [\"club\"]}, [\"OR\", {\"first_year\": false, \"club__weiclub__end_date__lte\": [\"today\"]}]]", + "query": "[\"AND\", {\"user\": [\"user\"], \"club\": [\"club\"]}, [\"OR\", {\"registration__first_year\": false, \"club__weiclub__date_end__lte\": [\"today\"]}]]", "type": "view", "mask": 1, "field": "", @@ -1890,7 +1902,7 @@ "wei", "weimembership" ], - "query": "{\"wei\": [\"club\"], \"bus\": [\"membership\", \"weimembership\", \"bus\"]}", + "query": "{\"club\": [\"club\"], \"bus\": [\"membership\", \"weimembership\", \"bus\"]}", "type": "view", "mask": 1, "field": "", @@ -1905,13 +1917,148 @@ "wei", "weimembership" ], - "query": "{\"wei\": [\"club\"], \"team\": [\"membership\", \"weimembership\", \"team\"]}", + "query": "{\"club\": [\"club\"], \"team\": [\"membership\", \"weimembership\", \"team\"]}", "type": "view", "mask": 1, "field": "", "description": "View the members of the team" } }, + { + "model": "permission.permission", + "pk": 117, + "fields": { + "model": [ + "wei", + "bus" + ], + "query": "{\"pk\": [\"membership\", \"bus\", \"pk\"], \"wei__date_end__gte\": [\"today\"]}", + "type": "change", + "mask": 1, + "field": "name", + "description": "Update the name of my bus" + } + }, + { + "model": "permission.permission", + "pk": 118, + "fields": { + "model": [ + "wei", + "bus" + ], + "query": "{\"pk\": [\"membership\", \"bus\", \"pk\"], \"wei__date_end__gte\": [\"today\"]}", + "type": "change", + "mask": 1, + "field": "description", + "description": "Update the description of my bus" + } + }, + { + "model": "permission.permission", + "pk": 119, + "fields": { + "model": [ + "wei", + "busteam" + ], + "query": "{\"bus\": [\"membership\", \"bus\"], \"wei__date_end__gte\": [\"today\"]}", + "type": "add", + "mask": 1, + "field": "", + "description": "Add a team to my bus" + } + }, + { + "model": "permission.permission", + "pk": 120, + "fields": { + "model": [ + "wei", + "busteam" + ], + "query": "{\"bus\": [\"membership\", \"bus\"], \"wei__date_end__gte\": [\"today\"]}", + "type": "change", + "mask": 1, + "field": "name", + "description": "Update the name of a team of my bus" + } + }, + { + "model": "permission.permission", + "pk": 121, + "fields": { + "model": [ + "wei", + "busteam" + ], + "query": "{\"bus\": [\"membership\", \"bus\"], \"wei__date_end__gte\": [\"today\"]}", + "type": "change", + "mask": 1, + "field": "color", + "description": "Update the color of a team of my bus" + } + }, + { + "model": "permission.permission", + "pk": 122, + "fields": { + "model": [ + "wei", + "busteam" + ], + "query": "{\"bus\": [\"membership\", \"bus\"], \"wei__date_end__gte\": [\"today\"]}", + "type": "change", + "mask": 1, + "field": "description", + "description": "Update the description of a team of my bus" + } + }, + { + "model": "permission.permission", + "pk": 123, + "fields": { + "model": [ + "wei", + "busteam" + ], + "query": "{\"pk\": [\"membership\", \"team\", \"pk\"], \"wei__date_end__gte\": [\"today\"]}", + "type": "change", + "mask": 1, + "field": "name", + "description": "Update the name of my team" + } + }, + { + "model": "permission.permission", + "pk": 124, + "fields": { + "model": [ + "wei", + "busteam" + ], + "query": "{\"pk\": [\"membership\", \"team\", \"pk\"], \"wei__date_end__gte\": [\"today\"]}", + "type": "change", + "mask": 1, + "field": "color", + "description": "Update the color of my team" + } + }, + { + "model": "permission.permission", + "pk": 125, + "fields": { + "model": [ + "wei", + "busteam" + ], + "query": "{\"pk\": [\"membership\", \"team\", \"pk\"], \"wei__date_end__gte\": [\"today\"]}", + "type": "change", + "mask": 1, + "field": "description", + "description": "Update the description of my team" + } + }, { "model": "permission.rolepermissions", "pk": 1, @@ -1952,21 +2099,12 @@ 16, 17, 18, - 97, - 99, - 101, 70, 108, 109, 78, 79, - 114, - 83, - 84, - 87, - 90, - 93, - 95 + 83 ] } }, @@ -2180,7 +2318,18 @@ 111, 112, 113, - 114 + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125 ] } }, @@ -2190,15 +2339,6 @@ "fields": { "role": 10, "permissions": [ - 32, - 33, - 56, - 58, - 55, - 57, - 52, - 53, - 54, 23, 24, 25, @@ -2207,7 +2347,16 @@ 28, 29, 30, - 31 + 31, + 32, + 33, + 52, + 53, + 54, + 55, + 56, + 57, + 58 ] } }, @@ -2267,7 +2416,12 @@ "fields": { "role": 13, "permissions": [ - 115 + 115, + 117, + 118, + 120, + 121, + 122 ] } }, @@ -2280,5 +2434,26 @@ 116 ] } + }, + { + "model": "permission.rolepermissions", + "pk": 16, + "fields": { + "role": 18, + "permissions": [ + 77, + 84, + 87, + 90, + 93, + 95, + 97, + 99, + 101, + 108, + 109, + 114 + ] + } } ] \ No newline at end of file diff --git a/apps/wei/views.py b/apps/wei/views.py index c6ac0aa8..c4423e8a 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -11,7 +11,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.models import User from django.core.exceptions import PermissionDenied from django.db.models import Q, Count -from django.db.models.functions import Lower +from django.db.models.functions.text import Lower from django.forms import HiddenInput from django.http import HttpResponse from django.shortcuts import redirect @@ -540,6 +540,9 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update model = WEIRegistration form_class = WEIRegistrationForm + def get_queryset(self, **kwargs): + return WEIRegistration.objects + def dispatch(self, request, *args, **kwargs): wei = self.get_object().wei today = date.today() @@ -702,7 +705,9 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Crea if "preferred_team_pk" in information and len(information["preferred_team_pk"]) == 1: form["team"].initial = Bus.objects.get(pk=information["preferred_team_pk"][0]) if "preferred_roles_pk" in information: - form["roles"].initial = WEIRole.objects.filter(pk__in=information["preferred_roles_pk"]).all() + form["roles"].initial = WEIRole.objects.filter( + Q(pk__in=information["preferred_roles_pk"]) | Q(name="Adhérent WEI") + ).all() return form def form_valid(self, form): @@ -749,7 +754,12 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Crea membership.roles.set(WEIRole.objects.filter(name="1A").all()) membership.save() - return super().form_valid(form) + ret = super().form_valid(form) + + membership.refresh_from_db() + membership.roles.add(WEIRole.objects.get("Adhérent WEI")) + + return ret def get_success_url(self): self.object.refresh_from_db() @@ -845,10 +855,11 @@ class MemberListRenderView(LoginRequiredMixin, View): qs = qs.filter(club__pk=self.kwargs["wei_pk"]).order_by( Lower('bus__name'), Lower('team__name'), - 'roles', + 'user__profile__promotion', Lower('user__last_name'), Lower('user__first_name'), - ).distinct() + 'id', + ) if "bus_pk" in self.kwargs: qs = qs.filter(bus__pk=self.kwargs["bus_pk"]) @@ -856,7 +867,7 @@ class MemberListRenderView(LoginRequiredMixin, View): if "team_pk" in self.kwargs: qs = qs.filter(team__pk=self.kwargs["team_pk"] if self.kwargs["team_pk"] else None) - return qs + return qs.distinct() def get(self, request, **kwargs): qs = self.get_queryset() diff --git a/templates/base.html b/templates/base.html index f2d0b3d1..56abd22f 100644 --- a/templates/base.html +++ b/templates/base.html @@ -106,12 +106,12 @@ SPDX-License-Identifier: GPL-3.0-or-later {% trans 'Activities' %} {% endif %} - {% if "treasury.invoice"|not_empty_model_change_list %} + {% if "treasury.invoice"|not_empty_model_list %} {% endif %} - {% if "wei.weiclub"|not_empty_model_change_list %} + {% if "wei.weiclub"|not_empty_model_list %} diff --git a/templates/wei/weilist_sample.tex b/templates/wei/weilist_sample.tex index 98fc9ee4..a2ff0755 100644 --- a/templates/wei/weilist_sample.tex +++ b/templates/wei/weilist_sample.tex @@ -30,7 +30,7 @@ {% for membership in memberships %} {{ membership.user.last_name|safe }} & {{ membership.user.first_name|safe }} & {{ membership.registration.birth_date|safe }} & {{ membership.registration.get_gender_display|safe }} & {{ membership.user.profile.section_generated|safe }} & {{ membership.bus.name|safe }} -& {% if membership.team %}{{ membership.team.name|safe }}{% else %}--{% endif %} & {{ membership.roles.all|join:", "|safe }} \\ +& {% if membership.team %}{{ membership.team.name|safe }}{% else %}--{% endif %} & {{ membership.roles.first|safe }} \\ {% endfor %} \end{tabularx} \end{center} From 96a87483cc921e436c3feef6f110ee655943462e Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 24 Apr 2020 14:05:06 +0200 Subject: [PATCH 73/87] Order registration by promotion rather than by roles --- apps/scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/scripts b/apps/scripts index 26be04c4..256ac0a7 160000 --- a/apps/scripts +++ b/apps/scripts @@ -1 +1 @@ -Subproject commit 26be04c435b782b0d3561466a683152abed79d30 +Subproject commit 256ac0a7f2a0a1d7ab6a510c85552cbfc1fc6a7a From cdff0d3893088edc7b6ec5ad6e9fbfb9376cd212 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 24 Apr 2020 14:30:18 +0200 Subject: [PATCH 74/87] We can view and update our WEI registration while we are not fully registered to the WEI. --- apps/permission/fixtures/initial.json | 14 ++++----- apps/wei/views.py | 42 ++++++++++++++++++++------- templates/wei/weiclub_info.html | 4 ++- 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index ce0fcd3d..bad0ec80 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -1482,7 +1482,7 @@ "wei", "weiregistration" ], - "query": "{\"user\": [\"user\"], \"wei\": [\"club\"], \"wei__membership_start__lte\": [\"today\"]}", + "query": "[\"AND\", {\"user\": [\"user\"]}, [\"OR\", {\"wei\": [\"club\"]}, {\"wei__year\": [\"today\", \"year\"], \"membership\": null}]]", "type": "view", "mask": 1, "field": "", @@ -1527,7 +1527,7 @@ "wei", "weiregistration" ], - "query": "{\"user\": [\"user\"], \"wei\": [\"club\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"]}", + "query": "[\"AND\", {\"user\": [\"user\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"]}, [\"OR\", {\"wei\": [\"club\"]}, {\"wei__year\": [\"today\", \"year\"], \"membership\": null}]]", "type": "change", "mask": 1, "field": "soge_credit", @@ -1572,7 +1572,7 @@ "wei", "weiregistration" ], - "query": "{\"user\": [\"user\"], \"wei\": [\"club\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"]}", + "query": "[\"AND\", {\"user\": [\"user\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"]}, [\"OR\", {\"wei\": [\"club\"]}, {\"wei__year\": [\"today\", \"year\"], \"membership\": null}]]", "type": "change", "mask": 1, "field": "birth_date", @@ -1602,7 +1602,7 @@ "wei", "weiregistration" ], - "query": "{\"user\": [\"user\"], \"wei\": [\"club\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"]}", + "query": "[\"AND\", {\"user\": [\"user\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"]}, [\"OR\", {\"wei\": [\"club\"]}, {\"wei__year\": [\"today\", \"year\"], \"membership\": null}]]", "type": "change", "mask": 1, "field": "gender", @@ -1632,7 +1632,7 @@ "wei", "weiregistration" ], - "query": "{\"user\": [\"user\"], \"wei\": [\"club\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"]}", + "query": "[\"AND\", {\"user\": [\"user\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"]}, [\"OR\", {\"wei\": [\"club\"]}, {\"wei__year\": [\"today\", \"year\"], \"membership\": null}]]", "type": "change", "mask": 1, "field": "health_issues", @@ -1662,7 +1662,7 @@ "wei", "weiregistration" ], - "query": "{\"user\": [\"user\"], \"wei\": [\"club\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"]}", + "query": "[\"AND\", {\"user\": [\"user\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"]}, [\"OR\", {\"wei\": [\"club\"]}, {\"wei__year\": [\"today\", \"year\"], \"membership\": null}]]", "type": "change", "mask": 1, "field": "emergency_contact_name", @@ -1692,7 +1692,7 @@ "wei", "weiregistration" ], - "query": "{\"user\": [\"user\"], \"wei\": [\"club\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"]}", + "query": "[\"AND\", {\"user\": [\"user\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"]}, [\"OR\", {\"wei\": [\"club\"]}, {\"wei__year\": [\"today\", \"year\"], \"membership\": null}]]", "type": "change", "mask": 1, "field": "emergency_contact_phone", diff --git a/apps/wei/views.py b/apps/wei/views.py index c4423e8a..0f9ffd2c 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -121,16 +121,38 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): bus_table = BusTable(data=buses, prefix="bus-") context['buses'] = bus_table - # Check if the user has the right to create a membership with a random user, to display the button. - empty_membership = Membership( - club=club, - user=User.objects.first(), - date_start=datetime.now().date(), - date_end=datetime.now().date(), - fee=0, - ) - context["can_add_members"] = PermissionBackend \ - .check_perm(self.request.user, "member.add_membership", empty_membership) + random_user = User.objects.filter(~Q(wei__wei__in=[club])).first() + + if random_user is None: + # This case occurs when all users are registered to the WEI. + # Don't worry, Pikachu never went to the WEI. + # This bug can arrive only in dev mode. + context["can_add_first_year_member"] = True + context["can_add_any_member"] = True + else: + # Check if the user has the right to create a registration of a random first year member. + empty_fy_registration = WEIRegistration( + user=random_user, + first_year=True, + birth_date="1970-01-01", + gender="No", + emergency_contact_name="No", + emergency_contact_phone="No", + ) + context["can_add_first_year_member"] = PermissionBackend \ + .check_perm(self.request.user, "wei.add_weiregistration", empty_fy_registration) + + # Check if the user has the right to create a registration of a random old member. + empty_old_registration = WEIRegistration( + user=User.objects.filter(~Q(wei__wei__in=[club])).first(), + first_year=False, + birth_date="1970-01-01", + gender="No", + emergency_contact_name="No", + emergency_contact_phone="No", + ) + context["can_add_any_member"] = PermissionBackend \ + .check_perm(self.request.user, "wei.add_weiregistration", empty_old_registration) empty_bus = Bus( wei=club, diff --git a/templates/wei/weiclub_info.html b/templates/wei/weiclub_info.html index 7f1803d9..4af6afa0 100644 --- a/templates/wei/weiclub_info.html +++ b/templates/wei/weiclub_info.html @@ -58,8 +58,10 @@ {% trans "WEI list" %} {% endif %} {% if club.is_current_wei %} - {% if can_add_members %} + {% if can_add_first_year_member %} {% trans "Register 1A" %} + {% endif %} + {% if can_add_any_member %} {% trans "Register 2A+" %} {% endif %} {% if "wei.change_"|has_perm:club %} From 705313edb6eeade86344f80c906ab7669bd5f1ef Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 24 Apr 2020 19:34:20 +0200 Subject: [PATCH 75/87] Add Bus information in the WEI survey "api" --- apps/wei/forms/surveys/base.py | 25 +++++++++++++++++++++++++ apps/wei/views.py | 1 + 2 files changed, 26 insertions(+) diff --git a/apps/wei/forms/surveys/base.py b/apps/wei/forms/surveys/base.py index 80ee6fc3..f43dafc2 100644 --- a/apps/wei/forms/surveys/base.py +++ b/apps/wei/forms/surveys/base.py @@ -36,6 +36,16 @@ class WEISurveyInformation: registration.information = self.__dict__ +class WEIBusInformation: + """ + Abstract data of the bus. + """ + + def __init__(self, bus: Bus): + self.__dict__.update(bus.information) + self.bus = bus + + class WEISurveyAlgorithm: """ Abstract algorithm that attributes a bus to each new member. @@ -48,6 +58,14 @@ class WEISurveyAlgorithm: """ raise NotImplementedError + @classmethod + def get_bus_information_class(cls): + """ + The class of the information associated to a bus extending WEIBusInformation. + Default: WEIBusInformation (contains nothing) + """ + return WEIBusInformation + @classmethod def get_registrations(cls) -> QuerySet: """ @@ -62,6 +80,13 @@ class WEISurveyAlgorithm: """ return Bus.objects.filter(wei__year=cls.get_survey_class().get_year()) + @classmethod + def get_bus_information(cls, bus): + """ + Return the WEIBusInformation object containing the data stored in a given bus. + """ + return cls.get_bus_information_class()(bus) + def run_algorithm(self) -> None: """ Once this method implemented, run the algorithm that attributes a bus to each first year member. diff --git a/apps/wei/views.py b/apps/wei/views.py index 0f9ffd2c..ca596053 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -516,6 +516,7 @@ class WEIRegister2AView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): form.fields["soge_credit"].disabled = True form.fields["soge_credit"].help_text = _("You already opened an account in the Société générale.") + del form.fields["caution_check"] del form.fields["first_year"] del form.fields["ml_events_registration"] del form.fields["ml_art_registration"] From e42680997f78e2a2906bbc7059ef8f0495f39e0d Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sat, 25 Apr 2020 15:54:29 +0200 Subject: [PATCH 76/87] Translate all fields, remove old CAS client configuration, remove packaged templates in source --- locale/de/LC_MESSAGES/django.po | 97 +++-------- locale/fr/LC_MESSAGES/django.po | 150 +++++++----------- note_kfet/settings/__init__.py | 27 +--- requirements/cas.txt | 1 - templates/cas_server/form.html | 26 --- templates/cas_server/logged.html | 21 --- templates/cas_server/login.html | 33 ---- templates/cas_server/logout.html | 7 - templates/cas_server/proxy.xml | 5 - templates/cas_server/samlValidate.xml | 59 ------- templates/cas_server/samlValidateError.xml | 14 -- templates/cas_server/serviceValidate.xml | 19 --- templates/cas_server/serviceValidateError.xml | 3 - templates/cas_server/warn.html | 11 -- .../rest_framework/crispy_form.html | 5 - .../django_filters/rest_framework/form.html | 6 - .../django_filters/widgets/multiwidget.html | 1 - templates/registration/login.html | 7 - 18 files changed, 77 insertions(+), 415 deletions(-) delete mode 100644 templates/cas_server/form.html delete mode 100644 templates/cas_server/logged.html delete mode 100644 templates/cas_server/login.html delete mode 100644 templates/cas_server/logout.html delete mode 100644 templates/cas_server/proxy.xml delete mode 100644 templates/cas_server/samlValidate.xml delete mode 100644 templates/cas_server/samlValidateError.xml delete mode 100644 templates/cas_server/serviceValidate.xml delete mode 100644 templates/cas_server/serviceValidateError.xml delete mode 100644 templates/cas_server/warn.html delete mode 100644 templates/django_filters/rest_framework/crispy_form.html delete mode 100644 templates/django_filters/rest_framework/form.html delete mode 100644 templates/django_filters/widgets/multiwidget.html diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 5e7ae0c0..de84718f 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 18:21+0200\n" +"POT-Creation-Date: 2020-04-25 15:52+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -275,7 +275,7 @@ msgstr "" msgid "changelogs" msgstr "" -#: apps/member/apps.py:14 +#: apps/member/apps.py:14 apps/wei/tables.py:150 apps/wei/tables.py:181 msgid "member" msgstr "" @@ -509,7 +509,7 @@ msgstr "" msgid "fee" msgstr "" -#: apps/member/models.py:320 apps/member/views.py:505 apps/wei/views.py:738 +#: apps/member/models.py:320 apps/member/views.py:505 apps/wei/views.py:768 msgid "User is not a member of the parent club" msgstr "" @@ -552,7 +552,7 @@ msgstr "" msgid "Search user" msgstr "" -#: apps/member/views.py:500 apps/wei/views.py:729 +#: apps/member/views.py:500 apps/wei/views.py:759 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." @@ -806,7 +806,7 @@ msgstr "" #: apps/note/tables.py:146 apps/wei/tables.py:42 apps/wei/tables.py:43 #: templates/member/club_info.html:67 templates/note/conso_form.html:121 #: templates/wei/bus_tables.html:15 templates/wei/busteam_tables.html:15 -#: templates/wei/busteam_tables.html:33 templates/wei/weiclub_info.html:66 +#: templates/wei/busteam_tables.html:33 templates/wei/weiclub_info.html:68 msgid "Edit" msgstr "" @@ -926,7 +926,6 @@ msgstr "" #: apps/treasury/forms.py:85 apps/treasury/forms.py:133 #: templates/activity/activity_form.html:9 #: templates/activity/activity_invite.html:8 -#: templates/django_filters/rest_framework/form.html:5 #: templates/member/add_members.html:14 templates/member/club_form.html:9 #: templates/treasury/invoice_form.html:46 templates/wei/bus_form.html:13 #: templates/wei/busteam_form.html:13 templates/wei/weiclub_form.html:15 @@ -1316,61 +1315,54 @@ msgstr "" msgid "Teams" msgstr "" -#: apps/wei/tables.py:143 apps/wei/tables.py:185 +#: apps/wei/tables.py:143 apps/wei/tables.py:184 msgid "Members count" msgstr "" -#: apps/wei/tables.py:150 apps/wei/tables.py:151 apps/wei/tables.py:182 +#: apps/wei/tables.py:150 apps/wei/tables.py:181 msgid "members" msgstr "" -#: apps/wei/views.py:177 +#: apps/wei/views.py:201 msgid "Find WEI Membership" msgstr "" -#: apps/wei/views.py:212 +#: apps/wei/views.py:236 msgid "Find WEI Registration" msgstr "" -#: apps/wei/views.py:421 templates/wei/weiclub_info.html:62 +#: apps/wei/views.py:445 templates/wei/weiclub_info.html:62 msgid "Register 1A" msgstr "" -#: apps/wei/views.py:442 apps/wei/views.py:510 +#: apps/wei/views.py:466 apps/wei/views.py:535 msgid "This user is already registered to this WEI." msgstr "" -#: apps/wei/views.py:447 +#: apps/wei/views.py:471 msgid "" "This user can't be in her/his first year since he/she has already participed " "to a WEI." msgstr "" -#: apps/wei/views.py:475 templates/wei/weiclub_info.html:63 +#: apps/wei/views.py:499 templates/wei/weiclub_info.html:65 msgid "Register 2A+" msgstr "" -#: apps/wei/views.py:493 apps/wei/views.py:576 +#: apps/wei/views.py:517 apps/wei/views.py:604 msgid "You already opened an account in the Société générale." msgstr "" -#: apps/wei/views.py:733 +#: apps/wei/views.py:763 msgid "This user didn't give her/his caution check." msgstr "" -#: apps/wei/views.py:802 apps/wei/views.py:822 apps/wei/views.py:832 +#: apps/wei/views.py:837 apps/wei/views.py:857 apps/wei/views.py:867 #: templates/wei/survey.html:12 templates/wei/survey_closed.html:12 #: templates/wei/survey_end.html:12 msgid "Survey WEI" msgstr "" -#: note_kfet/settings/__init__.py:63 -msgid "" -"The Central Authentication Service grants you access to most of our websites " -"by authenticating only once, so you don't need to type your credentials " -"again unless your session expires or you logout." -msgstr "" - #: note_kfet/settings/base.py:154 msgid "German" msgstr "" @@ -1474,49 +1466,6 @@ msgid "" "upgrading." msgstr "" -#: templates/cas_server/logged.html:4 -msgid "" -"

    Log In Successful

    You have successfully logged into the Central " -"Authentication Service.
    For security reasons, please Log Out and Exit " -"your web browser when you are done accessing services that require " -"authentication!" -msgstr "" - -#: templates/cas_server/logged.html:8 -msgid "Log me out from all my sessions" -msgstr "" - -#: templates/cas_server/logged.html:14 -msgid "Forget the identity provider" -msgstr "" - -#: templates/cas_server/logged.html:18 -msgid "Logout" -msgstr "" - -#: templates/cas_server/login.html:6 -msgid "Please log in" -msgstr "" - -#: templates/cas_server/login.html:11 -msgid "" -"If you don't have any Note Kfet account, please follow this link to sign up." -msgstr "" - -#: templates/cas_server/login.html:17 -msgid "Login" -msgstr "" - -#: templates/cas_server/warn.html:9 -msgid "Connect to the service" -msgstr "" - -#: templates/django_filters/rest_framework/crispy_form.html:4 -#: templates/django_filters/rest_framework/form.html:2 -msgid "Field filters" -msgstr "" - #: templates/member/alias_update.html:5 msgid "Add alias" msgstr "" @@ -1763,7 +1712,7 @@ msgid "Log in again" msgstr "" #: templates/registration/login.html:7 templates/registration/login.html:8 -#: templates/registration/login.html:28 +#: templates/registration/login.html:21 #: templates/registration/password_reset_complete.html:10 msgid "Log in" msgstr "" @@ -1776,14 +1725,6 @@ msgid "" msgstr "" #: templates/registration/login.html:22 -msgid "You can also register via the central authentification server " -msgstr "" - -#: templates/registration/login.html:23 -msgid "using this link " -msgstr "" - -#: templates/registration/login.html:29 msgid "Forgotten your password or username?" msgstr "" @@ -2051,11 +1992,11 @@ msgstr "" msgid "WEI list" msgstr "" -#: templates/wei/weiclub_info.html:69 +#: templates/wei/weiclub_info.html:71 msgid "Add bus" msgstr "" -#: templates/wei/weiclub_info.html:73 +#: templates/wei/weiclub_info.html:75 msgid "View WEI" msgstr "" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 68d05045..af56309d 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -1,9 +1,14 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 18:21+0200\n" +"POT-Creation-Date: 2020-04-25 15:52+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -265,13 +270,13 @@ msgstr "Les logs ne peuvent pas être détruits." #: apps/logs/models.py:80 msgid "changelog" -msgstr "" +msgstr "journal de modification" #: apps/logs/models.py:81 msgid "changelogs" -msgstr "" +msgstr "journaux de modifications" -#: apps/member/apps.py:14 +#: apps/member/apps.py:14 apps/wei/tables.py:150 apps/wei/tables.py:181 msgid "member" msgstr "adhérent" @@ -509,7 +514,7 @@ msgstr "l'adhésion finit le" msgid "fee" msgstr "cotisation" -#: apps/member/models.py:320 apps/member/views.py:505 apps/wei/views.py:738 +#: apps/member/models.py:320 apps/member/views.py:505 apps/wei/views.py:768 msgid "User is not a member of the parent club" msgstr "L'utilisateur n'est pas membre du club parent" @@ -552,7 +557,7 @@ msgstr "Un alias avec un nom similaire existe déjà." msgid "Search user" msgstr "Chercher un utilisateur" -#: apps/member/views.py:500 apps/wei/views.py:729 +#: apps/member/views.py:500 apps/wei/views.py:759 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." @@ -809,7 +814,7 @@ msgstr "Supprimer" #: apps/note/tables.py:146 apps/wei/tables.py:42 apps/wei/tables.py:43 #: templates/member/club_info.html:67 templates/note/conso_form.html:121 #: templates/wei/bus_tables.html:15 templates/wei/busteam_tables.html:15 -#: templates/wei/busteam_tables.html:33 templates/wei/weiclub_info.html:66 +#: templates/wei/busteam_tables.html:33 templates/wei/weiclub_info.html:68 msgid "Edit" msgstr "Éditer" @@ -824,12 +829,12 @@ msgstr "Consommations" #: apps/permission/models.py:82 #, python-brace-format msgid "Can {type} {model}.{field} in {query}" -msgstr "" +msgstr "Can {type} {model}.{field} in {query}" #: apps/permission/models.py:84 #, python-brace-format msgid "Can {type} {model} in {query}" -msgstr "" +msgstr "Can {type} {model} in {query}" #: apps/permission/models.py:97 msgid "rank" @@ -866,6 +871,8 @@ msgstr "permissions" #: apps/permission/models.py:187 msgid "Specifying field applies only to view and change permission types." msgstr "" +"Spécifie le champ concerné, ne fonctionne que pour les permissions view et " +"change." #: apps/permission/models.py:322 apps/permission/models.py:323 msgid "role permissions" @@ -934,7 +941,6 @@ msgstr "Trésorerie" #: apps/treasury/forms.py:85 apps/treasury/forms.py:133 #: templates/activity/activity_form.html:9 #: templates/activity/activity_invite.html:8 -#: templates/django_filters/rest_framework/form.html:5 #: templates/member/add_members.html:14 templates/member/club_form.html:9 #: templates/treasury/invoice_form.html:46 templates/wei/bus_form.html:13 #: templates/wei/busteam_form.html:13 templates/wei/weiclub_form.html:15 @@ -1334,38 +1340,38 @@ msgstr "Valider" #: apps/wei/tables.py:96 msgid "Year" -msgstr "" +msgstr "Année" #: apps/wei/tables.py:134 templates/wei/bus_tables.html:26 #: templates/wei/busteam_tables.html:43 msgid "Teams" msgstr "Équipes" -#: apps/wei/tables.py:143 apps/wei/tables.py:185 +#: apps/wei/tables.py:143 apps/wei/tables.py:184 msgid "Members count" msgstr "Nombre de membres" -#: apps/wei/tables.py:150 apps/wei/tables.py:151 apps/wei/tables.py:182 +#: apps/wei/tables.py:150 apps/wei/tables.py:181 msgid "members" msgstr "adhérents" -#: apps/wei/views.py:177 +#: apps/wei/views.py:201 msgid "Find WEI Membership" msgstr "Trouver une adhésion au WEI" -#: apps/wei/views.py:212 +#: apps/wei/views.py:236 msgid "Find WEI Registration" msgstr "Trouver une inscription au WEI" -#: apps/wei/views.py:421 templates/wei/weiclub_info.html:62 +#: apps/wei/views.py:445 templates/wei/weiclub_info.html:62 msgid "Register 1A" msgstr "Inscrire un 1A" -#: apps/wei/views.py:442 apps/wei/views.py:510 +#: apps/wei/views.py:466 apps/wei/views.py:535 msgid "This user is already registered to this WEI." msgstr "Cette personne est déjà inscrite au WEI." -#: apps/wei/views.py:447 +#: apps/wei/views.py:471 msgid "" "This user can't be in her/his first year since he/she has already participed " "to a WEI." @@ -1373,31 +1379,24 @@ msgstr "" "Cet utilisateur ne peut pas être en première année puisqu'iel a déjà " "participé à un WEI." -#: apps/wei/views.py:475 templates/wei/weiclub_info.html:63 +#: apps/wei/views.py:499 templates/wei/weiclub_info.html:65 msgid "Register 2A+" msgstr "Inscrire un 2A+" -#: apps/wei/views.py:493 apps/wei/views.py:576 +#: apps/wei/views.py:517 apps/wei/views.py:604 msgid "You already opened an account in the Société générale." msgstr "Vous avez déjà ouvert un compte auprès de la société générale." -#: apps/wei/views.py:733 +#: apps/wei/views.py:763 msgid "This user didn't give her/his caution check." msgstr "Cet utilisateur n'a pas donné son chèque de caution." -#: apps/wei/views.py:802 apps/wei/views.py:822 apps/wei/views.py:832 +#: apps/wei/views.py:837 apps/wei/views.py:857 apps/wei/views.py:867 #: templates/wei/survey.html:12 templates/wei/survey_closed.html:12 #: templates/wei/survey_end.html:12 msgid "Survey WEI" msgstr "Questionnaire WEI" -#: note_kfet/settings/__init__.py:63 -msgid "" -"The Central Authentication Service grants you access to most of our websites " -"by authenticating only once, so you don't need to type your credentials " -"again unless your session expires or you logout." -msgstr "" - #: note_kfet/settings/base.py:154 msgid "German" msgstr "Allemand" @@ -1493,7 +1492,7 @@ msgstr "" #: templates/cas_server/base.html:7 msgid "Central Authentication Service" -msgstr "" +msgstr "Service Central d'Authentification" #: templates/cas_server/base.html:43 #, python-format @@ -1502,51 +1501,9 @@ msgid "" "%(VERSION)s and the last version is %(LAST_VERSION)s. Please consider " "upgrading." msgstr "" - -#: templates/cas_server/logged.html:4 -msgid "" -"

    Log In Successful

    You have successfully logged into the Central " -"Authentication Service.
    For security reasons, please Log Out and Exit " -"your web browser when you are done accessing services that require " -"authentication!" -msgstr "" - -#: templates/cas_server/logged.html:8 -msgid "Log me out from all my sessions" -msgstr "" - -#: templates/cas_server/logged.html:14 -msgid "Forget the identity provider" -msgstr "" - -#: templates/cas_server/logged.html:18 -msgid "Logout" -msgstr "" - -#: templates/cas_server/login.html:6 -msgid "Please log in" -msgstr "" - -#: templates/cas_server/login.html:11 -msgid "" -"If you don't have any Note Kfet account, please follow this link to sign up." -msgstr "" -"Si vous n'avez pas de compte Note Kfet, veuillez suivre ce lien pour vous inscrire." - -#: templates/cas_server/login.html:17 -msgid "Login" -msgstr "" - -#: templates/cas_server/warn.html:9 -msgid "Connect to the service" -msgstr "" - -#: templates/django_filters/rest_framework/crispy_form.html:4 -#: templates/django_filters/rest_framework/form.html:2 -msgid "Field filters" -msgstr "" +"Une nouvelle version de l'application est disponible. Cette instance utilise " +"la version %(VERSION)s et la dernière version est %(LAST_VERSION)s. Merci de " +"vous mettre à jour." #: templates/member/alias_update.html:5 msgid "Add alias" @@ -1791,17 +1748,17 @@ msgstr "Il n'y a pas d'inscription en attente avec cette entrée." #: templates/registration/logged_out.html:8 msgid "Thanks for spending some quality time with the Web site today." -msgstr "" +msgstr "Merci d'avoir utilisé la Note Kfet." #: templates/registration/logged_out.html:9 msgid "Log in again" -msgstr "" +msgstr "Se connecter à nouveau" #: templates/registration/login.html:7 templates/registration/login.html:8 -#: templates/registration/login.html:28 +#: templates/registration/login.html:21 #: templates/registration/password_reset_complete.html:10 msgid "Log in" -msgstr "" +msgstr "Se connecter" #: templates/registration/login.html:13 #, python-format @@ -1809,18 +1766,12 @@ msgid "" "You are authenticated as %(username)s, but are not authorized to access this " "page. Would you like to login to a different account?" msgstr "" +"Vous êtes connecté en tant que %(username)s, mais vous n'avez le droit " +"d'accéder à cette page. Voulez vous essayer avec un autre compte ?" #: templates/registration/login.html:22 -msgid "You can also register via the central authentification server " -msgstr "" - -#: templates/registration/login.html:23 -msgid "using this link " -msgstr "" - -#: templates/registration/login.html:29 msgid "Forgotten your password or username?" -msgstr "" +msgstr "Mot de passe ou pseudo oublié ?" #: templates/registration/mails/email_validation_email.html:3 msgid "Hi" @@ -1871,45 +1822,57 @@ msgstr "" #: templates/registration/password_change_form.html:11 #: templates/registration/password_reset_confirm.html:12 msgid "Change my password" -msgstr "" +msgstr "Changer mon mot de passe" #: templates/registration/password_reset_complete.html:8 msgid "Your password has been set. You may go ahead and log in now." msgstr "" +"Votre mot de passe a été enregistré. Vous pouvez vous connecter dès à " +"présent." #: templates/registration/password_reset_confirm.html:9 msgid "" "Please enter your new password twice so we can verify you typed it in " "correctly." msgstr "" +"Entrer votre nouveau mot de passe, et le confirmer en le renseignant une " +"seconde fois." #: templates/registration/password_reset_confirm.html:15 msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" +"Le lien de reinitialisation du mot de passe est invalide, il a peut-être été " +"déjà utilisé. Faites une nouvelle demande." #: templates/registration/password_reset_done.html:8 msgid "" "We've emailed you instructions for setting your password, if an account " "exists with the email you entered. You should receive them shortly." msgstr "" +"Nous vous avons envoyé par mail les instructions pour changer votre mot de " +"passe." #: templates/registration/password_reset_done.html:9 msgid "" "If you don't receive an email, please make sure you've entered the address " "you registered with, and check your spam folder." msgstr "" +"Si vous ne recevez pas d'email, vérifiez que vous avez bien utilisé " +"l'adresse associé à votre compte, et regarder également le dossier spam." #: templates/registration/password_reset_form.html:8 msgid "" "Forgotten your password? Enter your email address below, and we'll email " "instructions for setting a new one." msgstr "" +"Mot de passe oublié ? Entrez votre adresse mail ci-dessous, et vous recevrez " +"les instructions pour choisir un nouveau mot de passe." #: templates/registration/password_reset_form.html:11 msgid "Reset my password" -msgstr "" +msgstr "Réinitialiser mon mot de passe" #: templates/registration/signup.html:5 templates/registration/signup.html:8 #: templates/registration/signup.html:19 @@ -2111,11 +2074,11 @@ msgstr "Prix du WEI / incluant l'adhésion BDE/Kfet (étudiants)" msgid "WEI list" msgstr "Liste des WEI" -#: templates/wei/weiclub_info.html:69 +#: templates/wei/weiclub_info.html:71 msgid "Add bus" msgstr "Ajouter un bus" -#: templates/wei/weiclub_info.html:73 +#: templates/wei/weiclub_info.html:75 msgid "View WEI" msgstr "Voir le WEI" @@ -2305,6 +2268,3 @@ msgstr "Il n'y a pas de pré-inscription en attente avec cette entrée." #: templates/wei/weiregistration_list.html:24 msgid "View validated memberships..." msgstr "Voir les adhésions validées ..." - -#~ msgid "Attribute to each first year member a bus for the WEI" -#~ msgstr "Attribuer à chaque première année un bus pour le WEI" diff --git a/note_kfet/settings/__init__.py b/note_kfet/settings/__init__.py index 1ab06b9c..73aae469 100644 --- a/note_kfet/settings/__init__.py +++ b/note_kfet/settings/__init__.py @@ -39,41 +39,20 @@ else: from .development import * try: - #in secrets.py defines everything you want + # in secrets.py defines everything you want from .secrets import * + INSTALLED_APPS += OPTIONAL_APPS except ImportError: pass -if "cas" in INSTALLED_APPS: - MIDDLEWARE += ['cas.middleware.CASMiddleware'] +if "cas_server" in INSTALLED_APPS: # CAS Settings - CAS_SERVER_URL = "https://" + os.getenv("NOTE_URL", "note.example.com") + "/cas/" CAS_AUTO_CREATE_USER = False CAS_LOGO_URL = "/static/img/Saperlistpopette.png" CAS_FAVICON_URL = "/static/favicon/favicon-32x32.png" - CAS_SHOW_SERVICE_MESSAGES = True CAS_SHOW_POWERED = False - CAS_REDIRECT_TO_LOGIN_AFTER_LOGOUT = False - CAS_PROVIDE_URL_TO_LOGOUT = True - CAS_INFO_MESSAGES = { - "cas_explained": { - "message": _( - u"The Central Authentication Service grants you access to most of our websites by " - u"authenticating only once, so you don't need to type your credentials again unless " - u"your session expires or you logout." - ), - "discardable": True, - "type": "info", # one of info, success, info, warning, danger - }, - } - - CAS_INFO_MESSAGES_ORDER = [ - 'cas_explained', - ] - AUTHENTICATION_BACKENDS += ('cas.backends.CASBackend',) - if "logs" in INSTALLED_APPS: MIDDLEWARE += ('note_kfet.middlewares.SessionMiddleware',) diff --git a/requirements/cas.txt b/requirements/cas.txt index d468d2d5..c7c696fb 100644 --- a/requirements/cas.txt +++ b/requirements/cas.txt @@ -1,2 +1 @@ -django-cas-client==1.5.3 django-cas-server==1.1.0 diff --git a/templates/cas_server/form.html b/templates/cas_server/form.html deleted file mode 100644 index 405dedd1..00000000 --- a/templates/cas_server/form.html +++ /dev/null @@ -1,26 +0,0 @@ -{% load cas_server %} -{% for error in form.non_field_errors %} -
    - - {{error}} -
    -{% endfor %} -{% for field in form %}{% if not field|is_hidden %} -
    {% spaceless %} - {% if field|is_checkbox %} -
    - {% else %} - - {{field}} - {% endif %} - {% for error in field.errors %} - {{error}} - {% endfor %} -{% endspaceless %}
    -{% else %}{{field}}{% endif %}{% endfor %} diff --git a/templates/cas_server/logged.html b/templates/cas_server/logged.html deleted file mode 100644 index 46e1c9a8..00000000 --- a/templates/cas_server/logged.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "cas_server/base.html" %} -{% load i18n %} -{% block content %} - - -{% endblock %} - diff --git a/templates/cas_server/login.html b/templates/cas_server/login.html deleted file mode 100644 index ddc2eb32..00000000 --- a/templates/cas_server/login.html +++ /dev/null @@ -1,33 +0,0 @@ -{% extends "cas_server/base.html" %} -{% load i18n %} - -{% block ante_messages %} -{% if auto_submit %}{% endif %} -{% endblock %} -{% block content %} -
    - {% trans "If you don't have any Note Kfet account, please follow this link to sign up." %} -
    - -{% endblock %} -{% block javascript_inline %} -jQuery(function( $ ){ - $("#id_warn").click(function(e){ - if($("#id_warn").is(':checked')){ - createCookie("warn", "on", 10 * 365); - } else { - eraseCookie("warn"); - } - }); -});{% if auto_submit %} -document.getElementById('login_form').submit(); // SUBMIT FORM{% endif %} -{% endblock %} - diff --git a/templates/cas_server/logout.html b/templates/cas_server/logout.html deleted file mode 100644 index 80693376..00000000 --- a/templates/cas_server/logout.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "cas_server/base.html" %} -{% load static %} -{% load i18n %} -{% block content %} - -{% endblock %} - diff --git a/templates/cas_server/proxy.xml b/templates/cas_server/proxy.xml deleted file mode 100644 index ab51d89a..00000000 --- a/templates/cas_server/proxy.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - {{ticket}} - - diff --git a/templates/cas_server/samlValidate.xml b/templates/cas_server/samlValidate.xml deleted file mode 100644 index 3b130fd2..00000000 --- a/templates/cas_server/samlValidate.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - {{Recipient}} - - - - - - {{username}} - - - urn:oasis:names:tc:SAML:1.0:cm:artifact - - - - - {{auth_date}} - - - false{# we do not support long-term (Remember-Me) auth #} - - - {{is_new_login}} - -{% for name, value in attributes %} - {{value}} - -{% endfor %} - - - {{username}} - - - urn:oasis:names:tc:SAML:1.0:cm:artifact - - - - - - - - diff --git a/templates/cas_server/samlValidateError.xml b/templates/cas_server/samlValidateError.xml deleted file mode 100644 index c72daba1..00000000 --- a/templates/cas_server/samlValidateError.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - {{msg}} - - - - diff --git a/templates/cas_server/serviceValidate.xml b/templates/cas_server/serviceValidate.xml deleted file mode 100644 index f583dbea..00000000 --- a/templates/cas_server/serviceValidate.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - {{username}} - - {{auth_date}} - false{# we do not support long-term (Remember-Me) auth #} - {{is_new_login}} -{% for key, value in attributes %} {{value}} -{% endfor %} - - - -{% for key, value in attributes %} -{% endfor %}{% if proxyGrantingTicket %} {{proxyGrantingTicket}} -{% endif %}{% if proxies %} -{% for proxy in proxies %} {{proxy}} -{% endfor %} -{% endif %} - diff --git a/templates/cas_server/serviceValidateError.xml b/templates/cas_server/serviceValidateError.xml deleted file mode 100644 index cab8d9bd..00000000 --- a/templates/cas_server/serviceValidateError.xml +++ /dev/null @@ -1,3 +0,0 @@ - - {{msg}} - diff --git a/templates/cas_server/warn.html b/templates/cas_server/warn.html deleted file mode 100644 index 4f80b15a..00000000 --- a/templates/cas_server/warn.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "cas_server/base.html" %} -{% load static %} -{% load i18n %} - -{% block content %} - -{% endblock %} diff --git a/templates/django_filters/rest_framework/crispy_form.html b/templates/django_filters/rest_framework/crispy_form.html deleted file mode 100644 index 171767c0..00000000 --- a/templates/django_filters/rest_framework/crispy_form.html +++ /dev/null @@ -1,5 +0,0 @@ -{% load crispy_forms_tags %} -{% load i18n %} - -

    {% trans "Field filters" %}

    -{% crispy filter.form %} diff --git a/templates/django_filters/rest_framework/form.html b/templates/django_filters/rest_framework/form.html deleted file mode 100644 index b116e353..00000000 --- a/templates/django_filters/rest_framework/form.html +++ /dev/null @@ -1,6 +0,0 @@ -{% load i18n %} -

    {% trans "Field filters" %}

    -
    - {{ filter.form.as_p }} - -
    diff --git a/templates/django_filters/widgets/multiwidget.html b/templates/django_filters/widgets/multiwidget.html deleted file mode 100644 index 089ddb20..00000000 --- a/templates/django_filters/widgets/multiwidget.html +++ /dev/null @@ -1 +0,0 @@ -{% for widget in widget.subwidgets %}{% include widget.template_name %}{% if forloop.first %}-{% endif %}{% endfor %} diff --git a/templates/registration/login.html b/templates/registration/login.html index 175d37e0..32e54e00 100644 --- a/templates/registration/login.html +++ b/templates/registration/login.html @@ -16,13 +16,6 @@ SPDX-License-Identifier: GPL-2.0-or-later {% endblocktrans %}

    {% endif %} - {%url 'cas_login' as cas_url %} - {% if cas_url %} -
    - {% trans "You can also register via the central authentification server " %} - {% trans "using this link "%} -
    - {%endif%}
    {% csrf_token %} {{ form | crispy }} From f35246c9e2a6ed35649c0b096e5f97683b4c6061 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sat, 25 Apr 2020 19:29:01 +0200 Subject: [PATCH 77/87] Send a mail to webmasters when an error occurs (in production mode) --- .env_example | 2 ++ README.md | 2 ++ apps/scripts | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.env_example b/.env_example index d903b724..9afdd138 100644 --- a/.env_example +++ b/.env_example @@ -11,3 +11,5 @@ DJANGO_SETTINGS_MODULE=note_kfet.settings DOMAIN=localhost CONTACT_EMAIL=tresorerie.bde@localhost NOTE_URL=localhost +NOTE_MAIL=notekfet@localhost +WEBMASTER_MAIL=notekfet@localhost diff --git a/README.md b/README.md index 0c0cfb2e..be27e63e 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,8 @@ On supposera pour la suite que vous utilisez Debian/Ubuntu sur un serveur tout n DOMAIN=localhost # note.example.com CONTACT_EMAIL=tresorerie.bde@localhost NOTE_URL=localhost # serveur cas note.example.com si auto-hébergé. + NOTE_MAIL=notekfet@localhost # Adresse expéditrice des mails + WEBMASTER_MAIL=notekfet@localhost # Adresse sur laquelle contacter les webmasters de la note Ensuite on (re)bascule dans l'environement virtuel et on lance les migrations diff --git a/apps/scripts b/apps/scripts index 256ac0a7..e59001b1 160000 --- a/apps/scripts +++ b/apps/scripts @@ -1 +1 @@ -Subproject commit 256ac0a7f2a0a1d7ab6a510c85552cbfc1fc6a7a +Subproject commit e59001b1b55824a2c6d807c4aa35db3b0ec223e0 From 957344922b60461f4449d8af93bd6ba2255414cf Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sat, 25 Apr 2020 19:29:18 +0200 Subject: [PATCH 78/87] Custom error pages --- apps/permission/signals.py | 15 ++++- apps/wei/views.py | 2 +- locale/de/LC_MESSAGES/django.po | 71 ++++++++++++++++++++++- locale/fr/LC_MESSAGES/django.po | 86 +++++++++++++++++++++++++++- note_kfet/settings/development.py | 4 -- note_kfet/settings/production.py | 3 - note_kfet/urls.py | 9 +++ templates/400.html | 8 +++ templates/403.html | 13 +++++ templates/404.html | 13 +++++ templates/500.html | 8 +++ templates/scripts/mail-error500.html | 2 + templates/scripts/mail-error500.txt | 7 +++ 13 files changed, 228 insertions(+), 13 deletions(-) create mode 100644 templates/400.html create mode 100644 templates/403.html create mode 100644 templates/404.html create mode 100644 templates/500.html create mode 100644 templates/scripts/mail-error500.html create mode 100644 templates/scripts/mail-error500.txt diff --git a/apps/permission/signals.py b/apps/permission/signals.py index bf54b72f..cac0a8a0 100644 --- a/apps/permission/signals.py +++ b/apps/permission/signals.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later from django.core.exceptions import PermissionDenied +from django.utils.translation import gettext_lazy as _ from note_kfet.middlewares import get_current_authenticated_user from permission.backends import PermissionBackend @@ -57,13 +58,19 @@ def pre_save_object(sender, instance, **kwargs): if old_value == new_value: continue if not PermissionBackend.check_perm(user, app_label + ".change_" + model_name + "_" + field_name, instance): - raise PermissionDenied + raise PermissionDenied( + _("You don't have the permission to change the field {field} on this instance of model" + " {app_label}.{model_name}.") + .format(field=field_name, app_label=app_label, model_name=model_name, ) + ) else: # We check if the user has right to add the object has_perm = PermissionBackend.check_perm(user, app_label + ".add_" + model_name, instance) if not has_perm: - raise PermissionDenied + raise PermissionDenied( + _("You don't have the permission to add this instance of model {app_label}.{model_name}.") + .format(app_label=app_label, model_name=model_name, )) def pre_delete_object(instance, **kwargs): @@ -88,4 +95,6 @@ def pre_delete_object(instance, **kwargs): # We check if the user has rights to delete the object if not PermissionBackend.check_perm(user, app_label + ".delete_" + model_name, instance): - raise PermissionDenied + raise PermissionDenied( + _("You don't have the permission to delete this instance of model {app_label}.{model_name}.") + .format(app_label=app_label, model_name=model_name)) diff --git a/apps/wei/views.py b/apps/wei/views.py index ca596053..b35e27be 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -661,7 +661,7 @@ class WEIDeleteRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Delete return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,))) if not PermissionBackend.check_perm(self.request.user, "wei.delete_weiregistration", object): - raise PermissionDenied + raise PermissionDenied(_("You don't have the right to delete this WEI registration.")) return super().dispatch(request, *args, **kwargs) diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index de84718f..4a42dca7 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-25 15:52+0200\n" +"POT-Creation-Date: 2020-04-25 19:12+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -868,6 +868,27 @@ msgstr "" msgid "role permissions" msgstr "" +#: apps/permission/signals.py:62 +#, python-brace-format +msgid "" +"You don't have the permission to change the field {field} on this instance " +"of model {app_label}.{model_name}." +msgstr "" + +#: apps/permission/signals.py:72 +#, python-brace-format +msgid "" +"You don't have the permission to add this instance of model {app_label}." +"{model_name}." +msgstr "" + +#: apps/permission/signals.py:100 +#, python-brace-format +msgid "" +"You don't have the permission to delete this instance of model {app_label}." +"{model_name}." +msgstr "" + #: apps/registration/apps.py:10 msgid "registration" msgstr "" @@ -1353,6 +1374,10 @@ msgstr "" msgid "You already opened an account in the Société générale." msgstr "" +#: apps/wei/views.py:664 +msgid "You don't have the right to delete this WEI registration." +msgstr "" + #: apps/wei/views.py:763 msgid "This user didn't give her/his caution check." msgstr "" @@ -1375,6 +1400,50 @@ msgstr "" msgid "French" msgstr "" +#: templates/400.html:6 +msgid "Bad request" +msgstr "" + +#: templates/400.html:7 +msgid "" +"Sorry, your request was bad. Don't know what could be wrong. An email has " +"been sent to webmasters with the details of the error. You can now drink a " +"coke." +msgstr "" + +#: templates/403.html:6 +msgid "Permission denied" +msgstr "" + +#: templates/403.html:7 +msgid "You don't have the right to perform this request." +msgstr "" + +#: templates/403.html:10 templates/404.html:10 +msgid "Exception message:" +msgstr "" + +#: templates/404.html:6 +msgid "Page not found" +msgstr "" + +#: templates/404.html:7 +#, python-format +msgid "" +"The requested path %(request_path)s was not found on the server." +msgstr "" + +#: templates/500.html:6 +msgid "Server error" +msgstr "" + +#: templates/500.html:7 +msgid "" +"Sorry, an error occurred when processing your request. An email has been " +"sent to webmasters with the detail of the error, and this will be fixed " +"soon. You can now drink a beer." +msgstr "" + #: templates/activity/activity_detail.html:29 msgid "creater" msgstr "" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index af56309d..339a3ab2 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-25 15:52+0200\n" +"POT-Creation-Date: 2020-04-25 19:12+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -878,6 +878,33 @@ msgstr "" msgid "role permissions" msgstr "Permissions par rôles" +#: apps/permission/signals.py:62 +#, python-brace-format +msgid "" +"You don't have the permission to change the field {field} on this instance " +"of model {app_label}.{model_name}." +msgstr "" +"Vous n'avez pas la permission de modifier le champ {field} sur l'instance du " +"modèle {app_label}.{model_name}." + +#: apps/permission/signals.py:72 +#, python-brace-format +msgid "" +"You don't have the permission to add this instance of model {app_label}." +"{model_name}." +msgstr "" +"Vous n'avez pas la permission d'ajouter cette instance du modèle {app_label}." +"{model_name}." + +#: apps/permission/signals.py:100 +#, python-brace-format +msgid "" +"You don't have the permission to delete this instance of model {app_label}." +"{model_name}." +msgstr "" +"Vous n'avez pas la permission de supprimer cette instance du modèle " +"{app_label}.{model_name}." + #: apps/registration/apps.py:10 msgid "registration" msgstr "inscription" @@ -1387,6 +1414,10 @@ msgstr "Inscrire un 2A+" msgid "You already opened an account in the Société générale." msgstr "Vous avez déjà ouvert un compte auprès de la société générale." +#: apps/wei/views.py:664 +msgid "You don't have the right to delete this WEI registration." +msgstr "Vous n'avez pas la permission de supprimer cette inscription au WEI." + #: apps/wei/views.py:763 msgid "This user didn't give her/his caution check." msgstr "Cet utilisateur n'a pas donné son chèque de caution." @@ -1409,6 +1440,59 @@ msgstr "Anglais" msgid "French" msgstr "Français" +#: templates/400.html:6 +msgid "Bad request" +msgstr "Requête invalide" + +#: templates/400.html:7 +msgid "" +"Sorry, your request was bad. Don't know what could be wrong. An email has " +"been sent to webmasters with the details of the error. You can now drink a " +"coke." +msgstr "" +"Désolé, votre requête est invalide. Aucune idée de ce qui a pu se produire. " +"Un e-mail a été envoyé aux responsables de la plateforme avec les détails de " +"cette erreur. Vous pouvez désormais allez boire un coca." + +#: templates/403.html:6 +msgid "Permission denied" +msgstr "Accès refusé" + +#: templates/403.html:7 +msgid "You don't have the right to perform this request." +msgstr "Vous n'avez pas la permission d'exécuter cette requête." + +#: templates/403.html:10 templates/404.html:10 +msgid "Exception message:" +msgstr "Message d'erreur :" + +#: templates/404.html:6 +msgid "Page not found" +msgstr "Page inexistante" + +#: templates/404.html:7 +#, python-format +msgid "" +"The requested path %(request_path)s was not found on the server." +msgstr "" +"The chemin demandé %(request_path)s n'a pas été trouvé sur le " +"serveur." + +#: templates/500.html:6 +msgid "Server error" +msgstr "Erreur du serveur" + +#: templates/500.html:7 +msgid "" +"Sorry, an error occurred when processing your request. An email has been " +"sent to webmasters with the detail of the error, and this will be fixed " +"soon. You can now drink a beer." +msgstr "" +"Désolé, une erreur est survenue lors de l'analyse de votre requête. Un email " +"a été envoyé aux responsables de la plateforme avec les détails de cette " +"erreur, qui sera corrigée rapidement. Vous pouvez désormais aller boire une " +"bière." + #: templates/activity/activity_detail.html:29 msgid "creater" msgstr "Créateur" diff --git a/note_kfet/settings/development.py b/note_kfet/settings/development.py index 66ad4fd4..0a0c21e1 100644 --- a/note_kfet/settings/development.py +++ b/note_kfet/settings/development.py @@ -62,10 +62,6 @@ CSRF_COOKIE_HTTPONLY = False X_FRAME_OPTIONS = 'DENY' SESSION_COOKIE_AGE = 60 * 60 * 3 -# CAS Client settings -# Can be modified in secrets.py -CAS_SERVER_URL = "http://localhost:8000/cas/" - STATIC_ROOT = '' # not needed in development settings STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static')] diff --git a/note_kfet/settings/production.py b/note_kfet/settings/production.py index 5be8a3b8..7b8d37ef 100644 --- a/note_kfet/settings/production.py +++ b/note_kfet/settings/production.py @@ -51,6 +51,3 @@ CSRF_COOKIE_SECURE = False CSRF_COOKIE_HTTPONLY = False X_FRAME_OPTIONS = 'DENY' SESSION_COOKIE_AGE = 60 * 60 * 3 - -# CAS Client settings -CAS_SERVER_URL = "https://" + os.getenv("NOTE_URL", "note.example.com") + "/cas/" diff --git a/note_kfet/urls.py b/note_kfet/urls.py index 4311c0b5..2cd7d6c6 100644 --- a/note_kfet/urls.py +++ b/note_kfet/urls.py @@ -5,6 +5,7 @@ from django.conf import settings from django.conf.urls.static import static from django.contrib import admin from django.urls import path, include +from django.views.defaults import bad_request, permission_denied, page_not_found, server_error from django.views.generic import RedirectView from member.views import CustomLoginView @@ -45,3 +46,11 @@ if "debug_toolbar" in settings.INSTALLED_APPS: urlpatterns = [ path('__debug__/', include(debug_toolbar.urls)), ] + urlpatterns + + +handler400 = bad_request +handler403 = permission_denied + +# Only displayed in production, when debug mode is set to False +handler404 = page_not_found +handler500 = server_error diff --git a/templates/400.html b/templates/400.html new file mode 100644 index 00000000..35606525 --- /dev/null +++ b/templates/400.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} + +{% load i18n %} + +{% block content %} +

    {% trans "Bad request" %}

    + {% blocktrans %}Sorry, your request was bad. Don't know what could be wrong. An email has been sent to webmasters with the details of the error. You can now drink a coke.{% endblocktrans %} +{% endblock %} \ No newline at end of file diff --git a/templates/403.html b/templates/403.html new file mode 100644 index 00000000..317865f2 --- /dev/null +++ b/templates/403.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} + +{% load i18n %} + +{% block content %} +

    {% trans "Permission denied" %}

    + {% blocktrans %}You don't have the right to perform this request.{% endblocktrans %} + {% if exception %} +
    + {% trans "Exception message:" %} {{ exception }} +
    + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/templates/404.html b/templates/404.html new file mode 100644 index 00000000..8477f914 --- /dev/null +++ b/templates/404.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} + +{% load i18n %} + +{% block content %} +

    {% trans "Page not found" %}

    + {% blocktrans %}The requested path {{ request_path }} was not found on the server.{% endblocktrans %} + {% if exception != "Resolver404" %} +
    + {% trans "Exception message:" %} {{ exception }} +
    + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/templates/500.html b/templates/500.html new file mode 100644 index 00000000..50b62bc2 --- /dev/null +++ b/templates/500.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} + +{% load i18n %} + +{% block content %} +

    {% trans "Server error" %}

    + {% blocktrans %}Sorry, an error occurred when processing your request. An email has been sent to webmasters with the detail of the error, and this will be fixed soon. You can now drink a beer.{% endblocktrans %} +{% endblock %} \ No newline at end of file diff --git a/templates/scripts/mail-error500.html b/templates/scripts/mail-error500.html new file mode 100644 index 00000000..f4bb796f --- /dev/null +++ b/templates/scripts/mail-error500.html @@ -0,0 +1,2 @@ +{# The data is already sent as HTML, so we return only the HTML data. Devs don't need a pretty mail... #} +{{ error }} \ No newline at end of file diff --git a/templates/scripts/mail-error500.txt b/templates/scripts/mail-error500.txt new file mode 100644 index 00000000..b7aabd39 --- /dev/null +++ b/templates/scripts/mail-error500.txt @@ -0,0 +1,7 @@ +Une erreur est survenue dans la Note Kfet. Les détails sont ci-dessous. + +Cordialement, + +L'équipe de la Note Kfet. + +{{ error }} \ No newline at end of file From b0f6ec1061daa05696a980e0775bce58d3b4afa5 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 26 Apr 2020 01:19:19 +0200 Subject: [PATCH 79/87] Remove debug print line --- apps/scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/scripts b/apps/scripts index e59001b1..1090fe0b 160000 --- a/apps/scripts +++ b/apps/scripts @@ -1 +1 @@ -Subproject commit e59001b1b55824a2c6d807c4aa35db3b0ec223e0 +Subproject commit 1090fe0b26928c0ee2205cfbebae0605dde3f370 From a83ab4bf857a3df94e87680428169abf7a8840d2 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 26 Apr 2020 01:20:46 +0200 Subject: [PATCH 80/87] Add a public rights page to view which permissions are granted to which role, update Font Awesome to 5.13 --- apps/permission/models.py | 5 +-- apps/permission/urls.py | 10 ++++++ apps/permission/views.py | 28 ++++++++++++++- locale/de/LC_MESSAGES/django.po | 34 +++++++++++++++--- locale/fr/LC_MESSAGES/django.po | 34 +++++++++++++++--- note_kfet/urls.py | 3 +- templates/base.html | 38 ++++++++++++-------- templates/permission/all_rights.html | 52 ++++++++++++++++++++++++++++ 8 files changed, 175 insertions(+), 29 deletions(-) create mode 100644 apps/permission/urls.py create mode 100644 templates/permission/all_rights.html diff --git a/apps/permission/models.py b/apps/permission/models.py index 949e036a..fe18c226 100644 --- a/apps/permission/models.py +++ b/apps/permission/models.py @@ -305,14 +305,15 @@ class RolePermissions(models.Model): """ Permissions associated with a Role """ - role = models.ForeignKey( + role = models.OneToOneField( Role, on_delete=models.PROTECT, - related_name='+', + related_name='permissions', verbose_name=_('role'), ) permissions = models.ManyToManyField( Permission, + verbose_name=_("permissions"), ) def __str__(self): diff --git a/apps/permission/urls.py b/apps/permission/urls.py new file mode 100644 index 00000000..c571c520 --- /dev/null +++ b/apps/permission/urls.py @@ -0,0 +1,10 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.urls import path +from permission.views import RightsView + +app_name = 'permission' +urlpatterns = [ + path('rights', RightsView.as_view(), name="rights"), +] diff --git a/apps/permission/views.py b/apps/permission/views.py index 2e41810d..cbd26a19 100644 --- a/apps/permission/views.py +++ b/apps/permission/views.py @@ -1,14 +1,18 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later +from datetime import date from django.forms import HiddenInput -from django.views.generic import UpdateView +from django.utils.translation import gettext_lazy as _ +from django.views.generic import UpdateView, TemplateView +from member.models import Role, Membership from .backends import PermissionBackend class ProtectQuerysetMixin: """ + This is a View class decorator and not a proper View class. Ensure that the user has the right to see or update objects. Display 404 error if the user can't see an object, remove the fields the user can't update on an update form (useful if the user can't change only specified fields). @@ -32,3 +36,25 @@ class ProtectQuerysetMixin: form.fields[key].widget = HiddenInput() return form + + +class RightsView(TemplateView): + template_name = "permission/all_rights.html" + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + context["title"] = _("All rights") + roles = Role.objects.all() + context["roles"] = roles + if self.request.user.is_authenticated: + active_memberships = Membership.objects.filter(user=self.request.user, + date_start__lte=date.today(), + date_end__gte=date.today()).all() + else: + active_memberships = Membership.objects.none() + + for role in roles: + role.clubs = [membership.club for membership in active_memberships if role in membership.roles.all()] + + return context diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 4a42dca7..408123ff 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-25 19:12+0200\n" +"POT-Creation-Date: 2020-04-26 00:45+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -856,7 +856,7 @@ msgstr "" msgid "permission" msgstr "" -#: apps/permission/models.py:182 +#: apps/permission/models.py:182 apps/permission/models.py:316 msgid "permissions" msgstr "" @@ -864,7 +864,7 @@ msgstr "" msgid "Specifying field applies only to view and change permission types." msgstr "" -#: apps/permission/models.py:322 apps/permission/models.py:323 +#: apps/permission/models.py:323 apps/permission/models.py:324 msgid "role permissions" msgstr "" @@ -882,13 +882,17 @@ msgid "" "{model_name}." msgstr "" -#: apps/permission/signals.py:100 +#: apps/permission/signals.py:99 #, python-brace-format msgid "" "You don't have the permission to delete this instance of model {app_label}." "{model_name}." msgstr "" +#: apps/permission/views.py:47 +msgid "All rights" +msgstr "" + #: apps/registration/apps.py:10 msgid "registration" msgstr "" @@ -1517,7 +1521,11 @@ msgstr "" msgid "Registrations" msgstr "" -#: templates/base.html:155 +#: templates/base.html:120 +msgid "Rights" +msgstr "" + +#: templates/base.html:158 msgid "" "Your e-mail address is not validated. Please check your mail inbox and click " "on the validation link." @@ -1729,6 +1737,22 @@ msgstr "" msgid "Unable to delete button " msgstr "" +#: templates/permission/all_rights.html:10 +msgid "Filter with roles that I have in at least one club" +msgstr "" + +#: templates/permission/all_rights.html:21 +msgid "Own this role in the clubs" +msgstr "" + +#: templates/permission/all_rights.html:26 +msgid "Query:" +msgstr "" + +#: templates/permission/all_rights.html:28 +msgid "No associated permission" +msgstr "" + #: templates/registration/email_validation_complete.html:6 msgid "Your email have successfully been validated." msgstr "" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 339a3ab2..65c41e93 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-25 19:12+0200\n" +"POT-Creation-Date: 2020-04-26 00:45+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -864,7 +864,7 @@ msgstr "champ" msgid "permission" msgstr "permission" -#: apps/permission/models.py:182 +#: apps/permission/models.py:182 apps/permission/models.py:316 msgid "permissions" msgstr "permissions" @@ -874,7 +874,7 @@ msgstr "" "Spécifie le champ concerné, ne fonctionne que pour les permissions view et " "change." -#: apps/permission/models.py:322 apps/permission/models.py:323 +#: apps/permission/models.py:323 apps/permission/models.py:324 msgid "role permissions" msgstr "Permissions par rôles" @@ -896,7 +896,7 @@ msgstr "" "Vous n'avez pas la permission d'ajouter cette instance du modèle {app_label}." "{model_name}." -#: apps/permission/signals.py:100 +#: apps/permission/signals.py:99 #, python-brace-format msgid "" "You don't have the permission to delete this instance of model {app_label}." @@ -905,6 +905,10 @@ msgstr "" "Vous n'avez pas la permission de supprimer cette instance du modèle " "{app_label}.{model_name}." +#: apps/permission/views.py:47 +msgid "All rights" +msgstr "Tous les droits" + #: apps/registration/apps.py:10 msgid "registration" msgstr "inscription" @@ -1566,7 +1570,11 @@ msgstr "Clubs" msgid "Registrations" msgstr "Inscriptions" -#: templates/base.html:155 +#: templates/base.html:120 +msgid "Rights" +msgstr "Droits" + +#: templates/base.html:158 msgid "" "Your e-mail address is not validated. Please check your mail inbox and click " "on the validation link." @@ -1783,6 +1791,22 @@ msgstr "Le bouton a bien été supprimé" msgid "Unable to delete button " msgstr "Impossible de supprimer le bouton " +#: templates/permission/all_rights.html:10 +msgid "Filter with roles that I have in at least one club" +msgstr "Filtrer les rôles que je possède dans au moins un club" + +#: templates/permission/all_rights.html:21 +msgid "Own this role in the clubs" +msgstr "Possède ce rôle dans les clubs" + +#: templates/permission/all_rights.html:26 +msgid "Query:" +msgstr "Requête :" + +#: templates/permission/all_rights.html:28 +msgid "No associated permission" +msgstr "Pas de permission associée" + #: templates/registration/email_validation_complete.html:6 msgid "Your email have successfully been validated." msgstr "Votre adresse e-mail a bien été validée." diff --git a/note_kfet/urls.py b/note_kfet/urls.py index 2cd7d6c6..9717087a 100644 --- a/note_kfet/urls.py +++ b/note_kfet/urls.py @@ -25,10 +25,11 @@ urlpatterns = [ # Include Django Contrib and Core routers path('i18n/', include('django.conf.urls.i18n')), path('admin/doc/', include('django.contrib.admindocs.urls')), - path('admin/', admin.site.urls), + path('admin/', admin.site.urls, name="admin"), path('accounts/login/', CustomLoginView.as_view()), path('accounts/', include('django.contrib.auth.urls')), path('api/', include('api.urls')), + path('permission/', include('permission.urls')), ] urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/templates/base.html b/templates/base.html index 56abd22f..2a1519f4 100644 --- a/templates/base.html +++ b/templates/base.html @@ -31,8 +31,8 @@ SPDX-License-Identifier: GPL-3.0-or-later href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"> - + + {# JQuery, Bootstrap and Turbolinks JavaScript #} +{% endblock %} From 2bc20480767a16c9a9b8f02e0f82c2a64643c5c1 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 27 Apr 2020 03:21:13 +0200 Subject: [PATCH 81/87] RecurrentTransaction amount history --- apps/note/forms.py | 8 +- apps/note/views.py | 28 ++++ locale/de/LC_MESSAGES/django.po | 135 ++++++++------- locale/fr/LC_MESSAGES/django.po | 165 +++++++++++-------- templates/note/transactiontemplate_form.html | 28 +++- 5 files changed, 220 insertions(+), 144 deletions(-) diff --git a/apps/note/forms.py b/apps/note/forms.py index 50f226f2..bc479e20 100644 --- a/apps/note/forms.py +++ b/apps/note/forms.py @@ -4,7 +4,7 @@ from django import forms from django.contrib.contenttypes.models import ContentType from django.utils.translation import gettext_lazy as _ -from note_kfet.inputs import Autocomplete +from note_kfet.inputs import Autocomplete, AmountInput from .models import TransactionTemplate, NoteClub @@ -24,11 +24,6 @@ class TransactionTemplateForm(forms.ModelForm): model = TransactionTemplate fields = '__all__' - # Le champ de destination est remplacé par un champ d'auto-complétion. - # Quand des lettres sont tapées, une requête est envoyée sur l'API d'auto-complétion - # et récupère les aliases valides - # Pour force le type d'une note, il faut rajouter le paramètre : - # forward=(forward.Const('TYPE', 'note_type') où TYPE est dans {user, club, special} widgets = { 'destination': Autocomplete( @@ -41,4 +36,5 @@ class TransactionTemplateForm(forms.ModelForm): 'placeholder': 'Note ...', }, ), + 'amount': AmountInput(), } diff --git a/apps/note/views.py b/apps/note/views.py index 88d47847..85f31d7e 100644 --- a/apps/note/views.py +++ b/apps/note/views.py @@ -1,5 +1,6 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later +import json from django.conf import settings from django.contrib.auth.mixins import LoginRequiredMixin @@ -80,6 +81,33 @@ class TransactionTemplateUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, Up form_class = TransactionTemplateForm success_url = reverse_lazy('note:template_list') + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + if "logs" in settings.INSTALLED_APPS: + from logs.models import Changelog + update_logs = Changelog.objects.filter( + model=ContentType.objects.get_for_model(TransactionTemplate), + instance_pk=self.object.pk, + action="edit", + ) + price_history = [] + for log in update_logs.all(): + old_dict = json.loads(log.previous) + new_dict = json.loads(log.data) + old_price = old_dict["amount"] + new_price = new_dict["amount"] + if old_price != new_price: + price_history.append(dict(price=old_price, time=log.timestamp)) + + price_history.append(dict(price=self.object.amount, time=None)) + + price_history.reverse() + + context["price_history"] = price_history + + return context + class ConsoView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): """ diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index f616ffd6..3daca779 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-09 21:59+0200\n" +"POT-Creation-Date: 2020-04-27 03:19+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -19,27 +19,27 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: apps/activity/apps.py:10 apps/activity/models.py:102 -#: apps/activity/models.py:111 +#: apps/activity/models.py:117 msgid "activity" msgstr "" -#: apps/activity/forms.py:45 apps/activity/models.py:208 +#: apps/activity/forms.py:45 apps/activity/models.py:213 msgid "You can't invite someone once the activity is started." msgstr "" -#: apps/activity/forms.py:48 apps/activity/models.py:211 +#: apps/activity/forms.py:48 apps/activity/models.py:216 msgid "This activity is not validated yet." msgstr "" -#: apps/activity/forms.py:58 apps/activity/models.py:219 +#: apps/activity/forms.py:58 apps/activity/models.py:224 msgid "This person has been already invited 5 times this year." msgstr "" -#: apps/activity/forms.py:62 apps/activity/models.py:223 +#: apps/activity/forms.py:62 apps/activity/models.py:228 msgid "This person is already invited." msgstr "" -#: apps/activity/forms.py:66 apps/activity/models.py:227 +#: apps/activity/forms.py:66 apps/activity/models.py:232 msgid "You can't invite more than 3 people to this activity." msgstr "" @@ -113,57 +113,57 @@ msgstr "" msgid "activities" msgstr "" -#: apps/activity/models.py:116 +#: apps/activity/models.py:122 msgid "entry time" msgstr "" -#: apps/activity/models.py:122 apps/note/apps.py:14 +#: apps/activity/models.py:128 apps/note/apps.py:14 #: apps/note/models/notes.py:58 msgid "note" msgstr "" -#: apps/activity/models.py:133 templates/activity/activity_entry.html:38 +#: apps/activity/models.py:139 templates/activity/activity_entry.html:38 msgid "entry" msgstr "" -#: apps/activity/models.py:134 templates/activity/activity_entry.html:38 +#: apps/activity/models.py:140 templates/activity/activity_entry.html:38 msgid "entries" msgstr "" -#: apps/activity/models.py:141 +#: apps/activity/models.py:146 msgid "Already entered on " msgstr "" -#: apps/activity/models.py:141 apps/activity/tables.py:54 +#: apps/activity/models.py:146 apps/activity/tables.py:54 msgid "{:%Y-%m-%d %H:%M:%S}" msgstr "" -#: apps/activity/models.py:149 +#: apps/activity/models.py:154 msgid "The balance is negative." msgstr "" -#: apps/activity/models.py:179 +#: apps/activity/models.py:184 msgid "last name" msgstr "" -#: apps/activity/models.py:184 templates/member/profile_info.html:14 +#: apps/activity/models.py:189 templates/member/profile_info.html:14 #: templates/registration/future_profile_detail.html:16 msgid "first name" msgstr "" -#: apps/activity/models.py:191 +#: apps/activity/models.py:196 msgid "inviter" msgstr "" -#: apps/activity/models.py:232 +#: apps/activity/models.py:237 msgid "guest" msgstr "" -#: apps/activity/models.py:233 +#: apps/activity/models.py:238 msgid "guests" msgstr "" -#: apps/activity/models.py:245 +#: apps/activity/models.py:250 msgid "Invitation" msgstr "" @@ -186,7 +186,7 @@ msgstr "" #: apps/activity/tables.py:79 apps/member/forms.py:80 #: apps/registration/forms.py:60 apps/treasury/forms.py:123 -#: templates/note/transaction_form.html:97 +#: templates/note/transaction_form.html:126 msgid "First name" msgstr "" @@ -198,11 +198,11 @@ msgstr "" msgid "Balance" msgstr "" -#: apps/activity/views.py:45 templates/base.html:106 +#: apps/activity/views.py:46 templates/base.html:121 msgid "Activities" msgstr "" -#: apps/activity/views.py:154 +#: apps/activity/views.py:160 msgid "Entry for activity \"{}\"" msgstr "" @@ -296,7 +296,7 @@ msgid "Credit amount" msgstr "" #: apps/member/forms.py:85 apps/registration/forms.py:65 -#: apps/treasury/forms.py:125 templates/note/transaction_form.html:103 +#: apps/treasury/forms.py:125 templates/note/transaction_form.html:132 msgid "Bank" msgstr "" @@ -666,9 +666,9 @@ msgid "transactions" msgstr "" #: apps/note/models/transactions.py:207 -#: templates/activity/activity_entry.html:13 templates/base.html:84 +#: templates/activity/activity_entry.html:13 templates/base.html:99 #: templates/note/transaction_form.html:19 -#: templates/note/transaction_form.html:145 +#: templates/note/transaction_form.html:140 msgid "Transfer" msgstr "" @@ -719,15 +719,15 @@ msgid "Delete" msgstr "" #: apps/note/tables.py:146 templates/member/club_info.html:55 -#: templates/note/conso_form.html:121 +#: templates/note/conso_form.html:128 msgid "Edit" msgstr "" -#: apps/note/views.py:40 +#: apps/note/views.py:41 msgid "Transfer money" msgstr "" -#: apps/note/views.py:109 templates/base.html:79 +#: apps/note/views.py:137 templates/base.html:94 msgid "Consumptions" msgstr "" @@ -810,7 +810,7 @@ msgid "" "The entered amount is not enough for the memberships, should be at least {}" msgstr "" -#: apps/treasury/apps.py:12 templates/base.html:111 +#: apps/treasury/apps.py:12 templates/base.html:126 msgid "Treasury" msgstr "" @@ -836,7 +836,7 @@ msgid "You can't change the type of the remittance." msgstr "" #: apps/treasury/forms.py:127 apps/treasury/tables.py:47 -#: templates/note/transaction_form.html:133 +#: templates/note/transaction_form.html:95 #: templates/treasury/remittance_form.html:18 msgid "Amount" msgstr "" @@ -857,7 +857,7 @@ msgstr "" msgid "Description" msgstr "" -#: apps/treasury/models.py:46 templates/note/transaction_form.html:91 +#: apps/treasury/models.py:46 templates/note/transaction_form.html:120 msgid "Name" msgstr "" @@ -1052,19 +1052,19 @@ msgstr "" msgid "The ENS Paris-Saclay BDE note." msgstr "" -#: templates/base.html:89 +#: templates/base.html:104 msgid "Users" msgstr "" -#: templates/base.html:94 +#: templates/base.html:109 msgid "Clubs" msgstr "" -#: templates/base.html:100 +#: templates/base.html:115 msgid "Registrations" msgstr "" -#: templates/base.html:150 +#: templates/base.html:165 msgid "" "Your e-mail address is not validated. Please check your mail inbox and click " "on the validation link." @@ -1227,36 +1227,41 @@ msgstr "" msgid "Save Changes" msgstr "" -#: templates/member/user_list.html:14 +#: templates/member/user_list.html:15 #: templates/registration/future_user_list.html:17 msgid "There is no pending user with this pattern." msgstr "" -#: templates/note/conso_form.html:28 templates/note/transaction_form.html:55 -msgid "Select emitters" +#: templates/note/conso_form.html:28 +msgid "Consum" msgstr "" -#: templates/note/conso_form.html:45 +#: templates/note/conso_form.html:39 templates/note/transaction_form.html:61 +#: templates/note/transaction_form.html:76 +msgid "Name or alias..." +msgstr "" + +#: templates/note/conso_form.html:48 msgid "Select consumptions" msgstr "" -#: templates/note/conso_form.html:51 +#: templates/note/conso_form.html:57 msgid "Consume!" msgstr "" -#: templates/note/conso_form.html:64 +#: templates/note/conso_form.html:71 msgid "Most used buttons" msgstr "" -#: templates/note/conso_form.html:126 +#: templates/note/conso_form.html:134 msgid "Single consumptions" msgstr "" -#: templates/note/conso_form.html:130 +#: templates/note/conso_form.html:139 msgid "Double consumptions" msgstr "" -#: templates/note/conso_form.html:141 templates/note/transaction_form.html:152 +#: templates/note/conso_form.html:150 templates/note/transaction_form.html:151 msgid "Recent transactions history" msgstr "" @@ -1264,36 +1269,42 @@ msgstr "" msgid "Gift" msgstr "" -#: templates/note/transaction_form.html:73 -msgid "External payment" +#: templates/note/transaction_form.html:55 +msgid "Select emitters" msgstr "" -#: templates/note/transaction_form.html:81 -msgid "Transfer type" -msgstr "" - -#: templates/note/transaction_form.html:116 -#: templates/note/transaction_form.html:169 -#: templates/note/transaction_form.html:176 +#: templates/note/transaction_form.html:70 msgid "Select receivers" msgstr "" -#: templates/note/transaction_form.html:138 +#: templates/note/transaction_form.html:87 +msgid "Action" +msgstr "" + +#: templates/note/transaction_form.html:102 msgid "Reason" msgstr "" -#: templates/note/transaction_form.html:183 -msgid "Credit note" +#: templates/note/transaction_form.html:110 +msgid "Transfer type" msgstr "" -#: templates/note/transaction_form.html:190 -msgid "Debit note" -msgstr "" - -#: templates/note/transactiontemplate_form.html:6 +#: templates/note/transactiontemplate_form.html:10 msgid "Buttons list" msgstr "" +#: templates/note/transactiontemplate_form.html:21 +msgid "Price history" +msgstr "" + +#: templates/note/transactiontemplate_form.html:24 +msgid "Obsolete since" +msgstr "" + +#: templates/note/transactiontemplate_form.html:24 +msgid "Current price" +msgstr "" + #: templates/note/transactiontemplate_list.html:9 msgid "search button" msgstr "" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 27636a65..51075884 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-09 21:59+0200\n" +"POT-Creation-Date: 2020-04-27 03:19+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -14,28 +14,28 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: apps/activity/apps.py:10 apps/activity/models.py:102 -#: apps/activity/models.py:111 +#: apps/activity/models.py:117 msgid "activity" msgstr "activité" -#: apps/activity/forms.py:45 apps/activity/models.py:208 +#: apps/activity/forms.py:45 apps/activity/models.py:213 msgid "You can't invite someone once the activity is started." msgstr "" "Vous ne pouvez pas inviter quelqu'un une fois que l'activité a démarré." -#: apps/activity/forms.py:48 apps/activity/models.py:211 +#: apps/activity/forms.py:48 apps/activity/models.py:216 msgid "This activity is not validated yet." msgstr "Cette activité n'est pas encore validée." -#: apps/activity/forms.py:58 apps/activity/models.py:219 +#: apps/activity/forms.py:58 apps/activity/models.py:224 msgid "This person has been already invited 5 times this year." msgstr "Cette personne a déjà été invitée 5 fois cette année." -#: apps/activity/forms.py:62 apps/activity/models.py:223 +#: apps/activity/forms.py:62 apps/activity/models.py:228 msgid "This person is already invited." msgstr "Cette personne est déjà invitée." -#: apps/activity/forms.py:66 apps/activity/models.py:227 +#: apps/activity/forms.py:66 apps/activity/models.py:232 msgid "You can't invite more than 3 people to this activity." msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité." @@ -109,57 +109,57 @@ msgstr "ouvrir" msgid "activities" msgstr "activités" -#: apps/activity/models.py:116 +#: apps/activity/models.py:122 msgid "entry time" msgstr "heure d'entrée" -#: apps/activity/models.py:122 apps/note/apps.py:14 +#: apps/activity/models.py:128 apps/note/apps.py:14 #: apps/note/models/notes.py:58 msgid "note" msgstr "note" -#: apps/activity/models.py:133 templates/activity/activity_entry.html:38 +#: apps/activity/models.py:139 templates/activity/activity_entry.html:38 msgid "entry" msgstr "entrée" -#: apps/activity/models.py:134 templates/activity/activity_entry.html:38 +#: apps/activity/models.py:140 templates/activity/activity_entry.html:38 msgid "entries" msgstr "entrées" -#: apps/activity/models.py:141 +#: apps/activity/models.py:146 msgid "Already entered on " msgstr "Déjà rentré le " -#: apps/activity/models.py:141 apps/activity/tables.py:54 +#: apps/activity/models.py:146 apps/activity/tables.py:54 msgid "{:%Y-%m-%d %H:%M:%S}" msgstr "{:%d/%m/%Y %H:%M:%S}" -#: apps/activity/models.py:149 +#: apps/activity/models.py:154 msgid "The balance is negative." msgstr "La note est en négatif." -#: apps/activity/models.py:179 +#: apps/activity/models.py:184 msgid "last name" msgstr "nom de famille" -#: apps/activity/models.py:184 templates/member/profile_info.html:14 +#: apps/activity/models.py:189 templates/member/profile_info.html:14 #: templates/registration/future_profile_detail.html:16 msgid "first name" msgstr "prénom" -#: apps/activity/models.py:191 +#: apps/activity/models.py:196 msgid "inviter" msgstr "hôte" -#: apps/activity/models.py:232 +#: apps/activity/models.py:237 msgid "guest" msgstr "invité" -#: apps/activity/models.py:233 +#: apps/activity/models.py:238 msgid "guests" msgstr "invités" -#: apps/activity/models.py:245 +#: apps/activity/models.py:250 msgid "Invitation" msgstr "Invitation" @@ -182,7 +182,7 @@ msgstr "Nom de famille" #: apps/activity/tables.py:79 apps/member/forms.py:80 #: apps/registration/forms.py:60 apps/treasury/forms.py:123 -#: templates/note/transaction_form.html:97 +#: templates/note/transaction_form.html:126 msgid "First name" msgstr "Prénom" @@ -194,11 +194,11 @@ msgstr "Note" msgid "Balance" msgstr "Solde du compte" -#: apps/activity/views.py:45 templates/base.html:106 +#: apps/activity/views.py:46 templates/base.html:121 msgid "Activities" msgstr "Activités" -#: apps/activity/views.py:154 +#: apps/activity/views.py:160 msgid "Entry for activity \"{}\"" msgstr "Entrées pour l'activité « {} »" @@ -292,7 +292,7 @@ msgid "Credit amount" msgstr "Montant à créditer" #: apps/member/forms.py:85 apps/registration/forms.py:65 -#: apps/treasury/forms.py:125 templates/note/transaction_form.html:103 +#: apps/treasury/forms.py:125 templates/note/transaction_form.html:132 msgid "Bank" msgstr "Banque" @@ -669,9 +669,9 @@ msgid "transactions" msgstr "transactions" #: apps/note/models/transactions.py:207 -#: templates/activity/activity_entry.html:13 templates/base.html:84 +#: templates/activity/activity_entry.html:13 templates/base.html:99 #: templates/note/transaction_form.html:19 -#: templates/note/transaction_form.html:145 +#: templates/note/transaction_form.html:140 msgid "Transfer" msgstr "Virement" @@ -722,15 +722,15 @@ msgid "Delete" msgstr "Supprimer" #: apps/note/tables.py:146 templates/member/club_info.html:55 -#: templates/note/conso_form.html:121 +#: templates/note/conso_form.html:128 msgid "Edit" msgstr "Éditer" -#: apps/note/views.py:40 +#: apps/note/views.py:41 msgid "Transfer money" msgstr "Transférer de l'argent" -#: apps/note/views.py:109 templates/base.html:79 +#: apps/note/views.py:137 templates/base.html:94 msgid "Consumptions" msgstr "Consommations" @@ -815,7 +815,7 @@ msgstr "" "Le montant crédité est trop faible pour adhérer, il doit être au minimum de " "{}" -#: apps/treasury/apps.py:12 templates/base.html:111 +#: apps/treasury/apps.py:12 templates/base.html:126 msgid "Treasury" msgstr "Trésorerie" @@ -841,7 +841,7 @@ msgid "You can't change the type of the remittance." msgstr "Vous ne pouvez pas changer le type de la remise." #: apps/treasury/forms.py:127 apps/treasury/tables.py:47 -#: templates/note/transaction_form.html:133 +#: templates/note/transaction_form.html:95 #: templates/treasury/remittance_form.html:18 msgid "Amount" msgstr "Montant" @@ -862,7 +862,7 @@ msgstr "Objet" msgid "Description" msgstr "Description" -#: apps/treasury/models.py:46 templates/note/transaction_form.html:91 +#: apps/treasury/models.py:46 templates/note/transaction_form.html:120 msgid "Name" msgstr "Nom" @@ -1057,19 +1057,19 @@ msgstr "Toutes les activités" msgid "The ENS Paris-Saclay BDE note." msgstr "La note du BDE de l'ENS Paris-Saclay." -#: templates/base.html:89 +#: templates/base.html:104 msgid "Users" msgstr "Utilisateurs" -#: templates/base.html:94 +#: templates/base.html:109 msgid "Clubs" msgstr "Clubs" -#: templates/base.html:100 +#: templates/base.html:115 msgid "Registrations" msgstr "Inscriptions" -#: templates/base.html:150 +#: templates/base.html:165 msgid "" "Your e-mail address is not validated. Please check your mail inbox and click " "on the validation link." @@ -1234,36 +1234,43 @@ msgstr "Voir mes adhésions" msgid "Save Changes" msgstr "Sauvegarder les changements" -#: templates/member/user_list.html:14 +#: templates/member/user_list.html:15 #: templates/registration/future_user_list.html:17 msgid "There is no pending user with this pattern." msgstr "Il n'y a pas d'inscription en attente avec cette entrée." -#: templates/note/conso_form.html:28 templates/note/transaction_form.html:55 -msgid "Select emitters" -msgstr "Sélection des émetteurs" +#: templates/note/conso_form.html:28 +#, fuzzy +#| msgid "Consume!" +msgid "Consum" +msgstr "Consommer !" -#: templates/note/conso_form.html:45 +#: templates/note/conso_form.html:39 templates/note/transaction_form.html:61 +#: templates/note/transaction_form.html:76 +msgid "Name or alias..." +msgstr "" + +#: templates/note/conso_form.html:48 msgid "Select consumptions" msgstr "Sélection des consommations" -#: templates/note/conso_form.html:51 +#: templates/note/conso_form.html:57 msgid "Consume!" msgstr "Consommer !" -#: templates/note/conso_form.html:64 +#: templates/note/conso_form.html:71 msgid "Most used buttons" msgstr "Boutons les plus utilisés" -#: templates/note/conso_form.html:126 +#: templates/note/conso_form.html:134 msgid "Single consumptions" msgstr "Consommations simples" -#: templates/note/conso_form.html:130 +#: templates/note/conso_form.html:139 msgid "Double consumptions" msgstr "Consommations doubles" -#: templates/note/conso_form.html:141 templates/note/transaction_form.html:152 +#: templates/note/conso_form.html:150 templates/note/transaction_form.html:151 msgid "Recent transactions history" msgstr "Historique des transactions récentes" @@ -1271,36 +1278,44 @@ msgstr "Historique des transactions récentes" msgid "Gift" msgstr "Don" -#: templates/note/transaction_form.html:73 -msgid "External payment" -msgstr "Paiement externe" +#: templates/note/transaction_form.html:55 +msgid "Select emitters" +msgstr "Sélection des émetteurs" -#: templates/note/transaction_form.html:81 -msgid "Transfer type" -msgstr "Type de transfert" - -#: templates/note/transaction_form.html:116 -#: templates/note/transaction_form.html:169 -#: templates/note/transaction_form.html:176 +#: templates/note/transaction_form.html:70 msgid "Select receivers" msgstr "Sélection des destinataires" -#: templates/note/transaction_form.html:138 +#: templates/note/transaction_form.html:87 +#, fuzzy +#| msgid "action" +msgid "Action" +msgstr "Action" + +#: templates/note/transaction_form.html:102 msgid "Reason" msgstr "Raison" -#: templates/note/transaction_form.html:183 -msgid "Credit note" -msgstr "Note à recharger" +#: templates/note/transaction_form.html:110 +msgid "Transfer type" +msgstr "Type de transfert" -#: templates/note/transaction_form.html:190 -msgid "Debit note" -msgstr "Note à débiter" - -#: templates/note/transactiontemplate_form.html:6 +#: templates/note/transactiontemplate_form.html:10 msgid "Buttons list" msgstr "Liste des boutons" +#: templates/note/transactiontemplate_form.html:21 +msgid "Price history" +msgstr "Historique des prix" + +#: templates/note/transactiontemplate_form.html:24 +msgid "Obsolete since" +msgstr "Obsolète depuis" + +#: templates/note/transactiontemplate_form.html:24 +msgid "Current price" +msgstr "Prix actuel" + #: templates/note/transactiontemplate_list.html:9 msgid "search button" msgstr "Chercher un bouton" @@ -1334,15 +1349,16 @@ msgstr "Vous pouvez désormais vous connecter." msgid "" "You must pay now your membership in the Kfet to complete your registration." msgstr "" -"Vous devez désormais payer votre adhésion à la Kfet pour compléter votre inscription." +"Vous devez désormais payer votre adhésion à la Kfet pour compléter votre " +"inscription." #: templates/registration/email_validation_complete.html:13 msgid "" "The link was invalid. The token may have expired. Please send us an email to " "activate your account." msgstr "" -"Le lien est invalide. Le jeton a sans doute expiré. Merci de nous contacter pour " -"activer votre compte." +"Le lien est invalide. Le jeton a sans doute expiré. Merci de nous contacter " +"pour activer votre compte." #: templates/registration/future_profile_detail.html:56 msgid "Delete registration" @@ -1414,8 +1430,8 @@ msgid "" "After that, you'll have to wait that someone validates your account before " "you can log in. You will need to pay your membership in the Kfet." msgstr "" -"Après cela, vous devrez attendre que quelqu'un valide votre compte avant " -"de pouvoir vous connecter. Vous devrez payer votre adhésion à la Kfet." +"Après cela, vous devrez attendre que quelqu'un valide votre compte avant de " +"pouvoir vous connecter. Vous devrez payer votre adhésion à la Kfet." #: templates/registration/mails/email_validation_email.html:13 msgid "Thanks" @@ -1552,5 +1568,14 @@ msgstr "Il n'y a pas de transaction associée à une remise ouverte." msgid "Closed remittances" msgstr "Remises fermées" +#~ msgid "External payment" +#~ msgstr "Paiement externe" + +#~ msgid "Credit note" +#~ msgstr "Note à recharger" + +#~ msgid "Debit note" +#~ msgstr "Note à débiter" + #~ msgid "This membership is already renewed" #~ msgstr "Cette adhésion est déjà renouvelée" diff --git a/templates/note/transactiontemplate_form.html b/templates/note/transactiontemplate_form.html index 1f9a574a..26cd97b5 100644 --- a/templates/note/transactiontemplate_form.html +++ b/templates/note/transactiontemplate_form.html @@ -1,12 +1,28 @@ {% extends "base.html" %} + {% load static %} {% load i18n %} {% load crispy_forms_tags %} +{% load pretty_money %} + {% block content %} -

    {% trans "Buttons list" %}

    - -{% csrf_token %} -{{form|crispy}} - - +

    + {% trans "Buttons list" %} +

    +
    + {% csrf_token %} + {{form|crispy}} + +
    + + {% if price_history %} +
    + +

    {% trans "Price history" %}

    +
      + {% for price in price_history %} +
    • {{ price.price|pretty_money }} {% if price.time %}({% trans "Obsolete since" %} {{ price.time }}){% else %}({% trans "Current price" %}){% endif %}
    • + {% endfor %} +
    + {% endif %} {% endblock %} From 5ea531fe68bb72399341545eeb7e1e9db39e460e Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 27 Apr 2020 03:56:22 +0200 Subject: [PATCH 82/87] Filter buttons list with visible buttons only --- apps/note/models/transactions.py | 3 +- apps/note/tables.py | 3 +- locale/de/LC_MESSAGES/django.po | 45 +++++--- locale/fr/LC_MESSAGES/django.po | 45 +++++--- templates/note/transactiontemplate_form.html | 4 +- templates/note/transactiontemplate_list.html | 111 +++++++++++-------- 6 files changed, 125 insertions(+), 86 deletions(-) diff --git a/apps/note/models/transactions.py b/apps/note/models/transactions.py index 83f8f914..455ac841 100644 --- a/apps/note/models/transactions.py +++ b/apps/note/models/transactions.py @@ -214,8 +214,7 @@ class RecurrentTransaction(Transaction): template = models.ForeignKey( TransactionTemplate, - null=True, - on_delete=models.SET_NULL, + on_delete=models.PROTECT, ) category = models.ForeignKey( TemplateCategory, diff --git a/apps/note/tables.py b/apps/note/tables.py index 8504121c..a1385abc 100644 --- a/apps/note/tables.py +++ b/apps/note/tables.py @@ -129,13 +129,14 @@ class ButtonTable(tables.Table): 'table table-bordered condensed table-hover' } row_attrs = { - 'class': lambda record: 'table-row ' + 'table-success' if record.display else 'table-danger', + 'class': lambda record: 'table-row ' + ('table-success' if record.display else 'table-danger'), 'id': lambda record: "row-" + str(record.pk), 'data-href': lambda record: record.pk } model = TransactionTemplate exclude = ('id',) + order_by = ('type', '-display', 'destination__name', 'name',) edit = tables.LinkColumn('note:template_update', args=[A('pk')], diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 3daca779..111f60b6 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-27 03:19+0200\n" +"POT-Creation-Date: 2020-04-27 03:55+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -46,7 +46,7 @@ msgstr "" #: apps/activity/models.py:23 apps/activity/models.py:48 #: apps/member/models.py:99 apps/member/models.py:202 #: apps/note/models/notes.py:188 apps/note/models/transactions.py:24 -#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:237 +#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:236 #: templates/member/club_info.html:13 templates/member/profile_info.html:14 #: templates/registration/future_profile_detail.html:16 msgid "name" @@ -238,12 +238,12 @@ msgstr "" msgid "create" msgstr "" -#: apps/logs/models.py:61 apps/note/tables.py:144 +#: apps/logs/models.py:61 apps/note/tables.py:145 #: templates/activity/activity_detail.html:67 msgid "edit" msgstr "" -#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:149 +#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:150 msgid "delete" msgstr "" @@ -672,33 +672,33 @@ msgstr "" msgid "Transfer" msgstr "" -#: apps/note/models/transactions.py:227 +#: apps/note/models/transactions.py:226 msgid "Template" msgstr "" -#: apps/note/models/transactions.py:242 +#: apps/note/models/transactions.py:241 msgid "first_name" msgstr "" -#: apps/note/models/transactions.py:247 +#: apps/note/models/transactions.py:246 msgid "bank" msgstr "" -#: apps/note/models/transactions.py:253 +#: apps/note/models/transactions.py:252 #: templates/activity/activity_entry.html:17 #: templates/note/transaction_form.html:24 msgid "Credit" msgstr "" -#: apps/note/models/transactions.py:253 templates/note/transaction_form.html:28 +#: apps/note/models/transactions.py:252 templates/note/transaction_form.html:28 msgid "Debit" msgstr "" -#: apps/note/models/transactions.py:269 apps/note/models/transactions.py:274 +#: apps/note/models/transactions.py:268 apps/note/models/transactions.py:273 msgid "membership transaction" msgstr "" -#: apps/note/models/transactions.py:270 +#: apps/note/models/transactions.py:269 msgid "membership transactions" msgstr "" @@ -714,11 +714,11 @@ msgstr "" msgid "No reason specified" msgstr "" -#: apps/note/tables.py:122 apps/note/tables.py:151 +#: apps/note/tables.py:122 apps/note/tables.py:152 msgid "Delete" msgstr "" -#: apps/note/tables.py:146 templates/member/club_info.html:55 +#: apps/note/tables.py:147 templates/member/club_info.html:55 #: templates/note/conso_form.html:128 msgid "Edit" msgstr "" @@ -819,6 +819,7 @@ msgstr "" #: templates/activity/activity_invite.html:8 #: templates/django_filters/rest_framework/form.html:5 #: templates/member/add_members.html:14 templates/member/club_form.html:9 +#: templates/note/transactiontemplate_form.html:15 #: templates/treasury/invoice_form.html:46 msgid "Submit" msgstr "" @@ -1306,22 +1307,30 @@ msgid "Current price" msgstr "" #: templates/note/transactiontemplate_list.html:9 -msgid "search button" +msgid "Search button" msgstr "" -#: templates/note/transactiontemplate_list.html:13 +#: templates/note/transactiontemplate_list.html:11 +msgid "Name of the button..." +msgstr "" + +#: templates/note/transactiontemplate_list.html:16 +msgid "Display visible buttons only" +msgstr "" + +#: templates/note/transactiontemplate_list.html:21 msgid "New button" msgstr "" -#: templates/note/transactiontemplate_list.html:20 +#: templates/note/transactiontemplate_list.html:28 msgid "buttons listing " msgstr "" -#: templates/note/transactiontemplate_list.html:70 +#: templates/note/transactiontemplate_list.html:86 msgid "button successfully deleted " msgstr "" -#: templates/note/transactiontemplate_list.html:74 +#: templates/note/transactiontemplate_list.html:90 msgid "Unable to delete button " msgstr "" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 51075884..ab608942 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-27 03:19+0200\n" +"POT-Creation-Date: 2020-04-27 03:55+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -42,7 +42,7 @@ msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité." #: apps/activity/models.py:23 apps/activity/models.py:48 #: apps/member/models.py:99 apps/member/models.py:202 #: apps/note/models/notes.py:188 apps/note/models/transactions.py:24 -#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:237 +#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:236 #: templates/member/club_info.html:13 templates/member/profile_info.html:14 #: templates/registration/future_profile_detail.html:16 msgid "name" @@ -234,12 +234,12 @@ msgstr "Nouvelles données" msgid "create" msgstr "Créer" -#: apps/logs/models.py:61 apps/note/tables.py:144 +#: apps/logs/models.py:61 apps/note/tables.py:145 #: templates/activity/activity_detail.html:67 msgid "edit" msgstr "Modifier" -#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:149 +#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:150 msgid "delete" msgstr "Supprimer" @@ -675,33 +675,33 @@ msgstr "transactions" msgid "Transfer" msgstr "Virement" -#: apps/note/models/transactions.py:227 +#: apps/note/models/transactions.py:226 msgid "Template" msgstr "Bouton" -#: apps/note/models/transactions.py:242 +#: apps/note/models/transactions.py:241 msgid "first_name" msgstr "prénom" -#: apps/note/models/transactions.py:247 +#: apps/note/models/transactions.py:246 msgid "bank" msgstr "banque" -#: apps/note/models/transactions.py:253 +#: apps/note/models/transactions.py:252 #: templates/activity/activity_entry.html:17 #: templates/note/transaction_form.html:24 msgid "Credit" msgstr "Crédit" -#: apps/note/models/transactions.py:253 templates/note/transaction_form.html:28 +#: apps/note/models/transactions.py:252 templates/note/transaction_form.html:28 msgid "Debit" msgstr "Débit" -#: apps/note/models/transactions.py:269 apps/note/models/transactions.py:274 +#: apps/note/models/transactions.py:268 apps/note/models/transactions.py:273 msgid "membership transaction" msgstr "Transaction d'adhésion" -#: apps/note/models/transactions.py:270 +#: apps/note/models/transactions.py:269 msgid "membership transactions" msgstr "Transactions d'adhésion" @@ -717,11 +717,11 @@ msgstr "Cliquez pour valider" msgid "No reason specified" msgstr "Pas de motif spécifié" -#: apps/note/tables.py:122 apps/note/tables.py:151 +#: apps/note/tables.py:122 apps/note/tables.py:152 msgid "Delete" msgstr "Supprimer" -#: apps/note/tables.py:146 templates/member/club_info.html:55 +#: apps/note/tables.py:147 templates/member/club_info.html:55 #: templates/note/conso_form.html:128 msgid "Edit" msgstr "Éditer" @@ -824,6 +824,7 @@ msgstr "Trésorerie" #: templates/activity/activity_invite.html:8 #: templates/django_filters/rest_framework/form.html:5 #: templates/member/add_members.html:14 templates/member/club_form.html:9 +#: templates/note/transactiontemplate_form.html:15 #: templates/treasury/invoice_form.html:46 msgid "Submit" msgstr "Envoyer" @@ -1317,22 +1318,30 @@ msgid "Current price" msgstr "Prix actuel" #: templates/note/transactiontemplate_list.html:9 -msgid "search button" +msgid "Search button" msgstr "Chercher un bouton" -#: templates/note/transactiontemplate_list.html:13 +#: templates/note/transactiontemplate_list.html:11 +msgid "Name of the button..." +msgstr "Nom du bouton ..." + +#: templates/note/transactiontemplate_list.html:16 +msgid "Display visible buttons only" +msgstr "N'afficher que les boutons visibles uniquement" + +#: templates/note/transactiontemplate_list.html:21 msgid "New button" msgstr "Nouveau bouton" -#: templates/note/transactiontemplate_list.html:20 +#: templates/note/transactiontemplate_list.html:28 msgid "buttons listing " msgstr "Liste des boutons" -#: templates/note/transactiontemplate_list.html:70 +#: templates/note/transactiontemplate_list.html:86 msgid "button successfully deleted " msgstr "Le bouton a bien été supprimé" -#: templates/note/transactiontemplate_list.html:74 +#: templates/note/transactiontemplate_list.html:90 msgid "Unable to delete button " msgstr "Impossible de supprimer le bouton " diff --git a/templates/note/transactiontemplate_form.html b/templates/note/transactiontemplate_form.html index 26cd97b5..e4bc42a7 100644 --- a/templates/note/transactiontemplate_form.html +++ b/templates/note/transactiontemplate_form.html @@ -12,10 +12,10 @@
    {% csrf_token %} {{form|crispy}} - +
    - {% if price_history %} + {% if price_history and price_history.1 %}

    {% trans "Price history" %}

    diff --git a/templates/note/transactiontemplate_list.html b/templates/note/transactiontemplate_list.html index cf9bc5ed..b298f58a 100644 --- a/templates/note/transactiontemplate_list.html +++ b/templates/note/transactiontemplate_list.html @@ -6,9 +6,17 @@

    - {% trans "search button" %} + {% trans "Search button" %}

    - + +
    +
    + +
    +

    {% trans "New button" %}
    @@ -29,50 +37,63 @@ {% block extrajavascript %} {% endblock %} From 368d28574792c30baa352a684cc7177879b33ab5 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 27 Apr 2020 04:01:30 +0200 Subject: [PATCH 83/87] Display by default only visible buttons --- templates/note/transactiontemplate_list.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/templates/note/transactiontemplate_list.html b/templates/note/transactiontemplate_list.html index b298f58a..280f9faf 100644 --- a/templates/note/transactiontemplate_list.html +++ b/templates/note/transactiontemplate_list.html @@ -12,7 +12,7 @@
    @@ -94,6 +94,8 @@ $(document).ready(function() { $("#search_field").keyup(search_field_moved); $("#active_only").change(search_field_moved); + + search_field_moved(); }); {% endblock %} From 72642d470c645e5d9978e09ed83f7889bec83434 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 27 Apr 2020 04:08:02 +0200 Subject: [PATCH 84/87] Fix two translations --- locale/fr/LC_MESSAGES/django.po | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index ab608942..feb06dd8 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -1241,10 +1241,8 @@ msgid "There is no pending user with this pattern." msgstr "Il n'y a pas d'inscription en attente avec cette entrée." #: templates/note/conso_form.html:28 -#, fuzzy -#| msgid "Consume!" msgid "Consum" -msgstr "Consommer !" +msgstr "Consommer" #: templates/note/conso_form.html:39 templates/note/transaction_form.html:61 #: templates/note/transaction_form.html:76 @@ -1288,8 +1286,6 @@ msgid "Select receivers" msgstr "Sélection des destinataires" #: templates/note/transaction_form.html:87 -#, fuzzy -#| msgid "action" msgid "Action" msgstr "Action" From eb39aff1ab74101265995fca19e5d2e774c4b2fb Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 27 Apr 2020 17:59:34 +0200 Subject: [PATCH 85/87] Fix email validation, turbolinks link --- apps/member/models.py | 2 +- apps/scripts | 2 +- templates/base.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/member/models.py b/apps/member/models.py index 016542b3..e1c829a4 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -137,7 +137,7 @@ class Profile(models.Model): 'user': self.user, 'domain': os.getenv("NOTE_URL", "note.example.com"), 'token': email_validation_token.make_token(self.user), - 'uid': urlsafe_base64_encode(force_bytes(self.user.pk)).decode('UTF-8'), + 'uid': urlsafe_base64_encode(force_bytes(self.user.pk)), }) self.user.email_user(subject, message) diff --git a/apps/scripts b/apps/scripts index 1090fe0b..f0aa4269 160000 --- a/apps/scripts +++ b/apps/scripts @@ -1 +1 @@ -Subproject commit 1090fe0b26928c0ee2205cfbebae0605dde3f370 +Subproject commit f0aa426950b9b867bf99233795e260871be2cb99 diff --git a/templates/base.html b/templates/base.html index 2a1519f4..646f27e9 100644 --- a/templates/base.html +++ b/templates/base.html @@ -121,7 +121,7 @@ SPDX-License-Identifier: GPL-3.0-or-later {% if user.is_staff %} {% endif %} From 8c0ccdfdd02d9f4d83f3a0efd5bd483ab4b25d40 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 27 Apr 2020 20:25:02 +0200 Subject: [PATCH 86/87] Don't change the password of another member (+ minor fixes on WEI app) --- apps/member/models.py | 2 +- apps/wei/models.py | 2 ++ apps/wei/views.py | 2 +- templates/member/profile_info.html | 14 ++++++++------ templates/registration/future_profile_detail.html | 7 ------- templates/wei/weimembership_form.html | 4 +++- 6 files changed, 15 insertions(+), 16 deletions(-) diff --git a/apps/member/models.py b/apps/member/models.py index e1c829a4..17b8f044 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -119,7 +119,7 @@ class Profile(models.Model): def soge(self): if "treasury" in settings.INSTALLED_APPS: from treasury.models import SogeCredit - return SogeCredit.objects.filter(user=self.user, credit_transaction=None).exists() + return SogeCredit.objects.filter(user=self.user, credit_transaction__isnull=False).exists() return False class Meta: diff --git a/apps/wei/models.py b/apps/wei/models.py index 3b41fdd0..9cee0d61 100644 --- a/apps/wei/models.py +++ b/apps/wei/models.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later import json +from datetime import date from django.conf import settings from django.contrib.auth.models import User @@ -17,6 +18,7 @@ class WEIClub(Club): """ year = models.PositiveIntegerField( unique=True, + default=date.today().year, verbose_name=_("year"), ) diff --git a/apps/wei/views.py b/apps/wei/views.py index b35e27be..597a44d4 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -780,7 +780,7 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Crea ret = super().form_valid(form) membership.refresh_from_db() - membership.roles.add(WEIRole.objects.get("Adhérent WEI")) + membership.roles.add(WEIRole.objects.get(name="Adhérent WEI")) return ret diff --git a/templates/member/profile_info.html b/templates/member/profile_info.html index 74856355..7be10ba1 100644 --- a/templates/member/profile_info.html +++ b/templates/member/profile_info.html @@ -17,12 +17,14 @@
    {% trans 'username'|capfirst %}
    {{ object.username }}
    -
    {% trans 'password'|capfirst %}
    -
    - - {% trans 'Change password' %} - -
    + {% if object.pk == user.pk %} +
    {% trans 'password'|capfirst %}
    +
    + + {% trans 'Change password' %} + +
    + {% endif %}
    {% trans 'section'|capfirst %}
    {{ object.profile.section }}
    diff --git a/templates/registration/future_profile_detail.html b/templates/registration/future_profile_detail.html index 8c78fb8d..1d2d08c7 100644 --- a/templates/registration/future_profile_detail.html +++ b/templates/registration/future_profile_detail.html @@ -31,13 +31,6 @@ {% endif %} -
    {% trans 'password'|capfirst %}
    -
    - - {% trans 'Change password' %} - -
    -
    {% trans 'section'|capfirst %}
    {{ object.profile.section }}
    diff --git a/templates/wei/weimembership_form.html b/templates/wei/weimembership_form.html index 0cde7fe6..995b6c1a 100644 --- a/templates/wei/weimembership_form.html +++ b/templates/wei/weimembership_form.html @@ -119,8 +119,10 @@
    From 968a44fee665e504ec076d49766c30aa80d067f1 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sat, 2 May 2020 02:49:27 +0200 Subject: [PATCH 87/87] For a random reason, django filters templates are missing. Wtf? --- templates/django_filters/rest_framework/crispy_form.html | 5 +++++ templates/django_filters/rest_framework/form.html | 6 ++++++ templates/django_filters/widgets/multiwidget.html | 1 + 3 files changed, 12 insertions(+) create mode 100644 templates/django_filters/rest_framework/crispy_form.html create mode 100644 templates/django_filters/rest_framework/form.html create mode 100644 templates/django_filters/widgets/multiwidget.html diff --git a/templates/django_filters/rest_framework/crispy_form.html b/templates/django_filters/rest_framework/crispy_form.html new file mode 100644 index 00000000..171767c0 --- /dev/null +++ b/templates/django_filters/rest_framework/crispy_form.html @@ -0,0 +1,5 @@ +{% load crispy_forms_tags %} +{% load i18n %} + +

    {% trans "Field filters" %}

    +{% crispy filter.form %} diff --git a/templates/django_filters/rest_framework/form.html b/templates/django_filters/rest_framework/form.html new file mode 100644 index 00000000..b116e353 --- /dev/null +++ b/templates/django_filters/rest_framework/form.html @@ -0,0 +1,6 @@ +{% load i18n %} +

    {% trans "Field filters" %}

    +
    + {{ filter.form.as_p }} + +
    diff --git a/templates/django_filters/widgets/multiwidget.html b/templates/django_filters/widgets/multiwidget.html new file mode 100644 index 00000000..089ddb20 --- /dev/null +++ b/templates/django_filters/widgets/multiwidget.html @@ -0,0 +1 @@ +{% for widget in widget.subwidgets %}{% include widget.template_name %}{% if forloop.first %}-{% endif %}{% endfor %}