diff --git a/apps/member/urls.py b/apps/member/urls.py index bc536f60..0b705bfd 100644 --- a/apps/member/urls.py +++ b/apps/member/urls.py @@ -14,12 +14,12 @@ urlpatterns = [ path('club/create/', views.ClubCreateView.as_view(), name="club_create"), path('club//update', views.ClubUpdateView.as_view(), name="club_update"), path('club//update_pic', views.ClubPictureUpdateView.as_view(), name="club_update_pic"), + path('club//aliases', views.ClubAliasView.as_view(), name="club_alias"), path('user/', views.UserListView.as_view(), name="user_list"), path('user/', views.UserDetailView.as_view(), name="user_detail"), path('user//update', views.UserUpdateView.as_view(), name="user_update_profile"), path('user//update_pic', views.ProfilePictureUpdateView.as_view(), name="user_update_pic"), - path('user//aliases', views.AliasView.as_view(), name="user_alias"), - path('user/aliases/delete/', views.DeleteAliasView.as_view(), name="user_alias_delete"), + path('user//aliases', views.ProfileAliasView.as_view(), name="user_alias"), path('manage-auth-token/', views.ManageAuthTokens.as_view(), name='auth_token'), # API for the user autocompleter path('user/user-autocomplete', views.UserAutocomplete.as_view(), name="user_autocomplete"), diff --git a/apps/member/views.py b/apps/member/views.py index 7d3ed748..2992f76e 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -20,7 +20,8 @@ from django.views.generic import CreateView, DetailView, UpdateView, TemplateVie from django.views.generic.edit import FormMixin from django_tables2.views import SingleTableView from rest_framework.authtoken.models import Token -from note.forms import AliasForm, ImageForm +from note.forms import ImageForm +#from note.forms import AliasForm, ImageForm from note.models import Alias, NoteUser from note.models.transactions import Transaction from note.tables import HistoryTable, AliasTable @@ -143,10 +144,6 @@ class UserDetailView(LoginRequiredMixin, DetailView): club_list = \ Membership.objects.all().filter(user=user).only("club") context['club_list'] = ClubTable(club_list) - context['title'] = _("Account #%(id)s: %(username)s") % { - 'id': user.pk, - 'username': user.username, - } return context @@ -171,57 +168,18 @@ class UserListView(LoginRequiredMixin, SingleTableView): context["filter"] = self.filter return context - -class AliasView(LoginRequiredMixin, FormMixin, DetailView): + +class ProfileAliasView(LoginRequiredMixin, DetailView): model = User template_name = 'member/profile_alias.html' context_object_name = 'user_object' - form_class = AliasForm - + def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - note = context['user_object'].note + note = context['object'].note context["aliases"] = AliasTable(note.alias_set.all()) return context - def get_success_url(self): - return reverse_lazy('member:user_alias', kwargs={'pk': self.object.id}) - - def post(self, request, *args, **kwargs): - self.object = self.get_object() - form = self.get_form() - if form.is_valid(): - return self.form_valid(form) - else: - return self.form_invalid(form) - - def form_valid(self, form): - alias = form.save(commit=False) - alias.note = self.object.note - alias.save() - return super().form_valid(form) - - -class DeleteAliasView(LoginRequiredMixin, DeleteView): - model = Alias - - def delete(self, request, *args, **kwargs): - try: - self.object = self.get_object() - self.object.delete() - except ValidationError as e: - # TODO: pass message to redirected view. - messages.error(self.request, str(e)) - else: - messages.success(self.request, _("Alias successfully deleted")) - return HttpResponseRedirect(self.get_success_url()) - - def get_success_url(self): - return reverse_lazy('member:user_alias', kwargs={'pk': self.object.note.user.pk}) - - def get(self, request, *args, **kwargs): - return self.post(request, *args, **kwargs) - class PictureUpdateView(LoginRequiredMixin, FormMixin, DetailView): form_class = ImageForm @@ -368,6 +326,17 @@ class ClubDetailView(LoginRequiredMixin, DetailView): context['member_list'] = club_member return context +class ClubAliasView(LoginRequiredMixin, DetailView): + model = Club + template_name = 'member/club_alias.html' + context_object_name = 'club' + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + note = context['object'].note + context["aliases"] = AliasTable(note.alias_set.all()) + return context + class ClubUpdateView(LoginRequiredMixin, UpdateView): model = Club @@ -395,12 +364,12 @@ class ClubAddMemberView(LoginRequiredMixin, CreateView): return super().get_queryset().filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view") | PermissionBackend.filter_queryset(self.request.user, Membership, "change")) - def get_context_data(self, **kwargs): + club = Club.objects.get(pk=self.kwargs["pk"]) context = super().get_context_data(**kwargs) context['formset'] = MemberFormSet() context['helper'] = FormSetHelper() - + context['club'] = club context['no_cache'] = True return context diff --git a/apps/note/api/serializers.py b/apps/note/api/serializers.py index a51b4263..05c35aa5 100644 --- a/apps/note/api/serializers.py +++ b/apps/note/api/serializers.py @@ -78,7 +78,11 @@ class AliasSerializer(serializers.ModelSerializer): class Meta: model = Alias fields = '__all__' - read_only_fields = ('note', ) + + def validate(self, attrs): + instance = Alias(**attrs) + instance.clean() + return attrs class NotePolymorphicSerializer(PolymorphicSerializer): diff --git a/apps/note/api/views.py b/apps/note/api/views.py index fc4a0e8f..e70eb49e 100644 --- a/apps/note/api/views.py +++ b/apps/note/api/views.py @@ -2,10 +2,14 @@ # SPDX-License-Identifier: GPL-3.0-or-later from django.db.models import Q +from django.core.exceptions import ValidationError from django_filters.rest_framework import DjangoFilterBackend from rest_framework.filters import OrderingFilter, SearchFilter -from api.viewsets import ReadProtectedModelViewSet, ReadOnlyProtectedModelViewSet from rest_framework import viewsets +from rest_framework.response import Response +from rest_framework import status + +from api.viewsets import ReadProtectedModelViewSet, ReadOnlyProtectedModelViewSet from .serializers import NotePolymorphicSerializer, AliasSerializer, TemplateCategorySerializer, \ TransactionTemplateSerializer, TransactionPolymorphicSerializer @@ -53,6 +57,22 @@ class AliasViewSet(ReadProtectedModelViewSet): search_fields = ['$normalized_name', '$name', '$note__polymorphic_ctype__model', ] ordering_fields = ['name', 'normalized_name'] + def get_serializer_class(self): + serializer_class = self.serializer_class + if self.request.method in ['PUT', 'PATCH']: + #alias owner cannot be change once establish + setattr(serializer_class.Meta, 'read_only_fields', ('note',)) + return serializer_class + + def destroy(self, request, *args, **kwargs): + instance = self.get_object() + try: + self.perform_destroy(instance) + except ValidationError as e: + print(e) + return Response({e.code:e.message},status.HTTP_400_BAD_REQUEST) + return Response(status=status.HTTP_204_NO_CONTENT) + def get_queryset(self): """ Parse query and apply filters. diff --git a/apps/note/fixtures/button.json b/apps/note/fixtures/button.json new file mode 100644 index 00000000..39f880b5 --- /dev/null +++ b/apps/note/fixtures/button.json @@ -0,0 +1,58 @@ +[ + { + "model": "note.templatecategory", + "pk": 1, + "fields": { + "name": "Soft" + } + }, + { + "model": "note.templatecategory", + "pk": 2, + "fields": { + "name": "Pulls" + } + }, + { + "model": "note.templatecategory", + "pk": 3, + "fields": { + "name": "Gala" + } + }, + { + "model": "note.templatecategory", + "pk": 4, + "fields": { + "name": "Clubs" + } + }, + { + "model": "note.templatecategory", + "pk": 5, + "fields": { + "name": "Bouffe" + } + }, + { + "model": "note.templatecategory", + "pk": 6, + "fields": { + "name": "BDA" + } + }, + { + "model": "note.templatecategory", + "pk": 7, + "fields": { + "name": "Autre" + } + }, + { + "model": "note.templatecategory", + "pk": 8, + "fields": { + "name": "Alcool" + } + } +] diff --git a/apps/note/fixtures/initial.json b/apps/note/fixtures/initial.json index efe37afa..72853eb7 100644 --- a/apps/note/fixtures/initial.json +++ b/apps/note/fixtures/initial.json @@ -184,61 +184,5 @@ "normalized_name": "kfet", "note": 6 } - }, - { - "model": "note.templatecategory", - "pk": 1, - "fields": { - "name": "Soft" - } - }, - { - "model": "note.templatecategory", - "pk": 2, - "fields": { - "name": "Pulls" - } - }, - { - "model": "note.templatecategory", - "pk": 3, - "fields": { - "name": "Gala" - } - }, - { - "model": "note.templatecategory", - "pk": 4, - "fields": { - "name": "Clubs" - } - }, - { - "model": "note.templatecategory", - "pk": 5, - "fields": { - "name": "Bouffe" - } - }, - { - "model": "note.templatecategory", - "pk": 6, - "fields": { - "name": "BDA" - } - }, - { - "model": "note.templatecategory", - "pk": 7, - "fields": { - "name": "Autre" - } - }, - { - "model": "note.templatecategory", - "pk": 8, - "fields": { - "name": "Alcool" - } } ] \ No newline at end of file diff --git a/apps/note/forms.py b/apps/note/forms.py index ac6adaaf..60252ad5 100644 --- a/apps/note/forms.py +++ b/apps/note/forms.py @@ -9,17 +9,6 @@ from .models import Alias from .models import TransactionTemplate -class AliasForm(forms.ModelForm): - class Meta: - model = Alias - fields = ("name",) - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.fields["name"].label = False - self.fields["name"].widget.attrs = {"placeholder": _('New Alias')} - - class ImageForm(forms.Form): image = forms.ImageField(required=False, label=_('select an image'), diff --git a/apps/note/models/notes.py b/apps/note/models/notes.py index b6b00aa8..43faabfe 100644 --- a/apps/note/models/notes.py +++ b/apps/note/models/notes.py @@ -228,7 +228,7 @@ class Alias(models.Model): for cat in {'M', 'P', 'Z', 'C'})).casefold() def clean(self): - normalized_name = Alias.normalize(self.name) + normalized_name = self.normalize(self.name) if len(normalized_name) >= 255: raise ValidationError(_('Alias is too long.'), code='alias_too_long') @@ -242,8 +242,12 @@ class Alias(models.Model): pass self.normalized_name = normalized_name + def save(self,*args,**kwargs): + self.normalized_name = self.normalize(self.name) + super().save(*args,**kwargs) + def delete(self, using=None, keep_parents=False): if self.name == str(self.note): raise ValidationError(_("You can't delete your main alias."), - code="cant_delete_main_alias") + code="main_alias") return super().delete(using, keep_parents) diff --git a/apps/note/tables.py b/apps/note/tables.py index 4b57cfa2..201b6c43 100644 --- a/apps/note/tables.py +++ b/apps/note/tables.py @@ -99,7 +99,7 @@ class HistoryTable(tables.Table): # function delete_button(id) provided in template file DELETE_TEMPLATE = """ - + """ @@ -107,7 +107,8 @@ class AliasTable(tables.Table): class Meta: attrs = { 'class': - 'table table condensed table-striped table-hover' + 'table table condensed table-striped table-hover', + 'id':"alias_table" } model = Alias fields = ('name',) @@ -115,15 +116,11 @@ class AliasTable(tables.Table): show_header = False name = tables.Column(attrs={'td': {'class': 'text-center'}}) - # delete = tables.TemplateColumn(template_code=delete_template, - # attrs={'td':{'class': 'col-sm-1'}}) - delete = tables.LinkColumn('member:user_alias_delete', - args=[A('pk')], - attrs={ - 'td': {'class': 'col-sm-2'}, - 'a': {'class': 'btn btn-danger'}}, - text='delete', accessor='pk') + delete_col = tables.TemplateColumn(template_code=DELETE_TEMPLATE, + extra_context={"delete_trans": _('delete')}, + attrs={'td': {'class': 'col-sm-1'}}) + class ButtonTable(tables.Table): @@ -143,11 +140,11 @@ class ButtonTable(tables.Table): edit = tables.LinkColumn('note:template_update', args=[A('pk')], attrs={'td': {'class': 'col-sm-1'}, - 'a': {'class': 'btn btn-primary'}}, + 'a': {'class': 'btn btn-sm btn-primary'}}, text=_('edit'), accessor='pk') - delete = tables.TemplateColumn(template_code=DELETE_TEMPLATE, + delete_col = tables.TemplateColumn(template_code=DELETE_TEMPLATE, extra_context={"delete_trans": _('delete')}, attrs={'td': {'class': 'col-sm-1'}}) diff --git a/static/js/alias.js b/static/js/alias.js new file mode 100644 index 00000000..267410da --- /dev/null +++ b/static/js/alias.js @@ -0,0 +1,37 @@ + + $("#alias_input").on('keypress',function(e) { + if(e.which == 13) { + $("#alias_submit").click(); + } + }); + + function create_alias(note_id){ + $.post("/api/note/alias/", + { + "csrfmiddlewaretoken": CSRF_TOKEN, + "name": $("#alias_input").val(), + "note": note_id + } + ).done(function(){ + $("#alias_table").load(location.href+ " #alias_table"); + addMsg("Alias ajouté","success"); + }) + .fail(function(xhr, textStatus, error){ + errMsg(xhr.responseJSON); + }); +} + // on click of button "delete" , call the API + function delete_button(button_id){ + $.ajax({ + url:"/api/note/alias/"+button_id+"/", + method:"DELETE", + headers: {"X-CSRFTOKEN": CSRF_TOKEN} + }) + .done(function(){ + addMsg('Alias supprimé','success'); + $("#alias_table").load(location.href + " #alias_table"); + }) + .fail(function(xhr,textStatus, error){ + errMsg(xhr.responseJSON); + }); + } diff --git a/static/js/base.js b/static/js/base.js index 422c2b6f..d21bd433 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -28,7 +28,15 @@ function addMsg(msg, alert_type) { + msg + "\n"; msgDiv.html(html); } - +/** + * add Muliple error message from err_obj + * @param err_obj {error_code:erro_message} + */ +function errMsg(errs_obj){ + for (const err_msg of Object.values(errs_obj)) { + addMsg(err_msg,'danger'); + } +} /** * Reload the balance of the user on the right top corner */ diff --git a/static/js/consos.js b/static/js/consos.js index 8ac2b7eb..20859933 100644 --- a/static/js/consos.js +++ b/static/js/consos.js @@ -221,7 +221,7 @@ function consume(source, source_alias, dest, quantity, amount, reason, type, cat addMsg("La transaction n'a pas pu être validée pour cause de solde insuffisant.", "danger"); }).fail(function () { reset(); - addMsg("Une erreur est survenue lors de la transaction : " + e.responseText, "danger"); + errMsg(e.responseJSON); }); }); } diff --git a/templates/base.html b/templates/base.html index 62fc9c58..6a688fc9 100644 --- a/templates/base.html +++ b/templates/base.html @@ -79,9 +79,11 @@ SPDX-License-Identifier: GPL-3.0-or-later {% trans 'Consumptions' %} {% endif %} + {% if "note.transaction"|not_empty_model_list %} + {% endif %} {% if "member.club"|not_empty_model_list %}