mirror of
https://gitlab.crans.org/bde/nk20
synced 2024-11-26 18:37:12 +00:00
Merge branch 'fix-ci' into 'master'
Rendre Erdnaxe heureux See merge request bde/nk20!42
This commit is contained in:
commit
7f91f27753
@ -11,7 +11,7 @@ class ActivityAdmin(admin.ModelAdmin):
|
|||||||
Admin customisation for Activity
|
Admin customisation for Activity
|
||||||
"""
|
"""
|
||||||
list_display = ('name', 'activity_type', 'organizer')
|
list_display = ('name', 'activity_type', 'organizer')
|
||||||
list_filter = ('activity_type', )
|
list_filter = ('activity_type',)
|
||||||
search_fields = ['name', 'organizer__name']
|
search_fields = ['name', 'organizer__name']
|
||||||
|
|
||||||
# Organize activities by start date
|
# Organize activities by start date
|
||||||
|
@ -11,6 +11,7 @@ class ActivityTypeSerializer(serializers.ModelSerializer):
|
|||||||
REST API Serializer for Activity types.
|
REST API Serializer for Activity types.
|
||||||
The djangorestframework plugin will analyse the model `ActivityType` and parse all fields in the API.
|
The djangorestframework plugin will analyse the model `ActivityType` and parse all fields in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ActivityType
|
model = ActivityType
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
@ -21,6 +22,7 @@ class ActivitySerializer(serializers.ModelSerializer):
|
|||||||
REST API Serializer for Activities.
|
REST API Serializer for Activities.
|
||||||
The djangorestframework plugin will analyse the model `Activity` and parse all fields in the API.
|
The djangorestframework plugin will analyse the model `Activity` and parse all fields in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Activity
|
model = Activity
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
@ -31,6 +33,7 @@ class GuestSerializer(serializers.ModelSerializer):
|
|||||||
REST API Serializer for Guests.
|
REST API Serializer for Guests.
|
||||||
The djangorestframework plugin will analyse the model `Guest` and parse all fields in the API.
|
The djangorestframework plugin will analyse the model `Guest` and parse all fields in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Guest
|
model = Guest
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
|
|
||||||
from ..models import ActivityType, Activity, Guest
|
|
||||||
from .serializers import ActivityTypeSerializer, ActivitySerializer, GuestSerializer
|
from .serializers import ActivityTypeSerializer, ActivitySerializer, GuestSerializer
|
||||||
|
from ..models import ActivityType, Activity, Guest
|
||||||
|
|
||||||
|
|
||||||
class ActivityTypeViewSet(viewsets.ModelViewSet):
|
class ActivityTypeViewSet(viewsets.ModelViewSet):
|
||||||
|
@ -14,6 +14,7 @@ class UserSerializer(serializers.ModelSerializer):
|
|||||||
REST API Serializer for Users.
|
REST API Serializer for Users.
|
||||||
The djangorestframework plugin will analyse the model `User` and parse all fields in the API.
|
The djangorestframework plugin will analyse the model `User` and parse all fields in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
exclude = (
|
exclude = (
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
class Changelog(models.Model):
|
class Changelog(models.Model):
|
||||||
|
@ -7,6 +7,7 @@ from django.contrib.contenttypes.models import ContentType
|
|||||||
from django.core import serializers
|
from django.core import serializers
|
||||||
from django.db.models.signals import pre_save, post_save, post_delete
|
from django.db.models.signals import pre_save, post_save, post_delete
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
from .models import Changelog
|
from .models import Changelog
|
||||||
|
|
||||||
|
|
||||||
@ -43,20 +44,20 @@ def get_user_and_ip(sender):
|
|||||||
|
|
||||||
|
|
||||||
EXCLUDED = [
|
EXCLUDED = [
|
||||||
'admin.logentry',
|
'admin.logentry',
|
||||||
'authtoken.token',
|
'authtoken.token',
|
||||||
'cas_server.user',
|
'cas_server.user',
|
||||||
'cas_server.userattributes',
|
'cas_server.userattributes',
|
||||||
'contenttypes.contenttype',
|
'contenttypes.contenttype',
|
||||||
'logs.changelog',
|
'logs.changelog',
|
||||||
'migrations.migration',
|
'migrations.migration',
|
||||||
'note.noteuser',
|
'note.noteuser',
|
||||||
'note.noteclub',
|
'note.noteclub',
|
||||||
'note.notespecial',
|
'note.notespecial',
|
||||||
'sessions.session',
|
'sessions.session',
|
||||||
'reversion.revision',
|
'reversion.revision',
|
||||||
'reversion.version',
|
'reversion.version',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@receiver(pre_save)
|
@receiver(pre_save)
|
||||||
|
@ -18,9 +18,9 @@ class ProfileInline(admin.StackedInline):
|
|||||||
|
|
||||||
|
|
||||||
class CustomUserAdmin(UserAdmin):
|
class CustomUserAdmin(UserAdmin):
|
||||||
inlines = (ProfileInline, )
|
inlines = (ProfileInline,)
|
||||||
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
|
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
|
||||||
list_select_related = ('profile', )
|
list_select_related = ('profile',)
|
||||||
form = ProfileForm
|
form = ProfileForm
|
||||||
|
|
||||||
def get_inline_instances(self, request, obj=None):
|
def get_inline_instances(self, request, obj=None):
|
||||||
|
@ -11,6 +11,7 @@ class ProfileSerializer(serializers.ModelSerializer):
|
|||||||
REST API Serializer for Profiles.
|
REST API Serializer for Profiles.
|
||||||
The djangorestframework plugin will analyse the model `Profile` and parse all fields in the API.
|
The djangorestframework plugin will analyse the model `Profile` and parse all fields in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Profile
|
model = Profile
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
@ -21,6 +22,7 @@ class ClubSerializer(serializers.ModelSerializer):
|
|||||||
REST API Serializer for Clubs.
|
REST API Serializer for Clubs.
|
||||||
The djangorestframework plugin will analyse the model `Club` and parse all fields in the API.
|
The djangorestframework plugin will analyse the model `Club` and parse all fields in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Club
|
model = Club
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
@ -31,6 +33,7 @@ class RoleSerializer(serializers.ModelSerializer):
|
|||||||
REST API Serializer for Roles.
|
REST API Serializer for Roles.
|
||||||
The djangorestframework plugin will analyse the model `Role` and parse all fields in the API.
|
The djangorestframework plugin will analyse the model `Role` and parse all fields in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Role
|
model = Role
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
@ -41,6 +44,7 @@ class MembershipSerializer(serializers.ModelSerializer):
|
|||||||
REST API Serializer for Memberships.
|
REST API Serializer for Memberships.
|
||||||
The djangorestframework plugin will analyse the model `Memberships` and parse all fields in the API.
|
The djangorestframework plugin will analyse the model `Memberships` and parse all fields in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Membership
|
model = Membership
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
|
|
||||||
from ..models import Profile, Club, Role, Membership
|
|
||||||
from .serializers import ProfileSerializer, ClubSerializer, RoleSerializer, MembershipSerializer
|
from .serializers import ProfileSerializer, ClubSerializer, RoleSerializer, MembershipSerializer
|
||||||
|
from ..models import Profile, Club, Role, Membership
|
||||||
|
|
||||||
|
|
||||||
class ProfileViewSet(viewsets.ModelViewSet):
|
class ProfileViewSet(viewsets.ModelViewSet):
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from django_filters import FilterSet, CharFilter
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.db.models import CharField
|
|
||||||
from crispy_forms.helper import FormHelper
|
from crispy_forms.helper import FormHelper
|
||||||
from crispy_forms.layout import Layout, Submit
|
from crispy_forms.layout import Layout, Submit
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.db.models import CharField
|
||||||
|
from django_filters import FilterSet, CharFilter
|
||||||
|
|
||||||
|
|
||||||
class UserFilter(FilterSet):
|
class UserFilter(FilterSet):
|
||||||
|
@ -1,23 +1,22 @@
|
|||||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from crispy_forms.bootstrap import Div
|
||||||
|
from crispy_forms.helper import FormHelper
|
||||||
|
from crispy_forms.layout import Layout
|
||||||
from dal import autocomplete
|
from dal import autocomplete
|
||||||
|
from django import forms
|
||||||
from django.contrib.auth.forms import UserCreationForm
|
from django.contrib.auth.forms import UserCreationForm
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django import forms
|
|
||||||
|
|
||||||
from .models import Profile, Club, Membership
|
from .models import Profile, Club, Membership
|
||||||
|
|
||||||
from crispy_forms.helper import FormHelper
|
|
||||||
from crispy_forms.bootstrap import Div
|
|
||||||
from crispy_forms.layout import Layout
|
|
||||||
|
|
||||||
|
|
||||||
class SignUpForm(UserCreationForm):
|
class SignUpForm(UserCreationForm):
|
||||||
def __init__(self,*args,**kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args,**kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.fields['username'].widget.attrs.pop("autofocus", None)
|
self.fields['username'].widget.attrs.pop("autofocus", None)
|
||||||
self.fields['first_name'].widget.attrs.update({"autofocus":"autofocus"})
|
self.fields['first_name'].widget.attrs.update({"autofocus": "autofocus"})
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
@ -28,6 +27,7 @@ class ProfileForm(forms.ModelForm):
|
|||||||
"""
|
"""
|
||||||
A form for the extras field provided by the :model:`member.Profile` model.
|
A form for the extras field provided by the :model:`member.Profile` model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Profile
|
model = Profile
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
@ -42,7 +42,7 @@ class ClubForm(forms.ModelForm):
|
|||||||
|
|
||||||
class AddMembersForm(forms.Form):
|
class AddMembersForm(forms.Form):
|
||||||
class Meta:
|
class Meta:
|
||||||
fields = ('', )
|
fields = ('',)
|
||||||
|
|
||||||
|
|
||||||
class MembershipForm(forms.ModelForm):
|
class MembershipForm(forms.ModelForm):
|
||||||
@ -54,13 +54,13 @@ class MembershipForm(forms.ModelForm):
|
|||||||
# et récupère les noms d'utilisateur valides
|
# et récupère les noms d'utilisateur valides
|
||||||
widgets = {
|
widgets = {
|
||||||
'user':
|
'user':
|
||||||
autocomplete.ModelSelect2(
|
autocomplete.ModelSelect2(
|
||||||
url='member:user_autocomplete',
|
url='member:user_autocomplete',
|
||||||
attrs={
|
attrs={
|
||||||
'data-placeholder': 'Nom ...',
|
'data-placeholder': 'Nom ...',
|
||||||
'data-minimum-input-length': 1,
|
'data-minimum-input-length': 1,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
class Profile(models.Model):
|
class Profile(models.Model):
|
||||||
@ -48,7 +48,7 @@ class Profile(models.Model):
|
|||||||
verbose_name_plural = _('user profile')
|
verbose_name_plural = _('user profile')
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('user_detail', args=(self.pk, ))
|
return reverse('user_detail', args=(self.pk,))
|
||||||
|
|
||||||
|
|
||||||
class Club(models.Model):
|
class Club(models.Model):
|
||||||
@ -97,7 +97,7 @@ class Club(models.Model):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse_lazy('member:club_detail', args=(self.pk, ))
|
return reverse_lazy('member:club_detail', args=(self.pk,))
|
||||||
|
|
||||||
|
|
||||||
class Role(models.Model):
|
class Role(models.Model):
|
||||||
@ -153,7 +153,6 @@ class Membership(models.Model):
|
|||||||
verbose_name = _('membership')
|
verbose_name = _('membership')
|
||||||
verbose_name_plural = _('memberships')
|
verbose_name_plural = _('memberships')
|
||||||
|
|
||||||
|
|
||||||
# @receiver(post_save, sender=settings.AUTH_USER_MODEL)
|
# @receiver(post_save, sender=settings.AUTH_USER_MODEL)
|
||||||
# def save_user_profile(instance, created, **_kwargs):
|
# def save_user_profile(instance, created, **_kwargs):
|
||||||
# """
|
# """
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
|
||||||
def save_user_profile(instance, created, raw, **_kwargs):
|
def save_user_profile(instance, created, raw, **_kwargs):
|
||||||
"""
|
"""
|
||||||
Hook to create and save a profile when an user is updated if it is not registered with the signup form
|
Hook to create and save a profile when an user is updated if it is not registered with the signup form
|
||||||
|
@ -1,33 +1,33 @@
|
|||||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
||||||
from django.shortcuts import redirect
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.views.generic import CreateView, DetailView, UpdateView, TemplateView,DeleteView
|
|
||||||
from django.views.generic.edit import FormMixin
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.contrib import messages
|
|
||||||
from django.urls import reverse_lazy
|
|
||||||
from django.http import HttpResponseRedirect
|
|
||||||
from django.db.models import Q
|
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
from django.conf import settings
|
|
||||||
from django_tables2.views import SingleTableView
|
|
||||||
from rest_framework.authtoken.models import Token
|
|
||||||
from dal import autocomplete
|
|
||||||
from PIL import Image
|
|
||||||
import io
|
import io
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
from dal import autocomplete
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.db.models import Q
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
from django.urls import reverse_lazy
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views.generic import CreateView, DetailView, UpdateView, TemplateView, DeleteView
|
||||||
|
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.models import Alias, NoteUser
|
from note.models import Alias, NoteUser
|
||||||
from note.models.transactions import Transaction
|
from note.models.transactions import Transaction
|
||||||
from note.tables import HistoryTable, AliasTable
|
from note.tables import HistoryTable, AliasTable
|
||||||
from note.forms import AliasForm, ImageForm
|
|
||||||
|
|
||||||
from .models import Profile, Club, Membership
|
|
||||||
from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, MemberFormSet, FormSetHelper
|
|
||||||
from .tables import ClubTable, UserTable
|
|
||||||
from .filters import UserFilter, UserFilterFormHelper
|
from .filters import UserFilter, UserFilterFormHelper
|
||||||
|
from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, MemberFormSet, FormSetHelper
|
||||||
|
from .models import Club, Membership
|
||||||
|
from .tables import ClubTable, UserTable
|
||||||
|
|
||||||
|
|
||||||
class UserCreateView(CreateView):
|
class UserCreateView(CreateView):
|
||||||
@ -109,7 +109,7 @@ class UserUpdateView(LoginRequiredMixin, UpdateView):
|
|||||||
return reverse_lazy('member:user_detail',
|
return reverse_lazy('member:user_detail',
|
||||||
kwargs={'pk': kwargs['id']})
|
kwargs={'pk': kwargs['id']})
|
||||||
else:
|
else:
|
||||||
return reverse_lazy('member:user_detail', args=(self.object.id, ))
|
return reverse_lazy('member:user_detail', args=(self.object.id,))
|
||||||
|
|
||||||
|
|
||||||
class UserDetailView(LoginRequiredMixin, DetailView):
|
class UserDetailView(LoginRequiredMixin, DetailView):
|
||||||
@ -157,13 +157,14 @@ class UserListView(LoginRequiredMixin, SingleTableView):
|
|||||||
context["filter"] = self.filter
|
context["filter"] = self.filter
|
||||||
return context
|
return context
|
||||||
|
|
||||||
class AliasView(LoginRequiredMixin,FormMixin,DetailView):
|
|
||||||
|
class AliasView(LoginRequiredMixin, FormMixin, DetailView):
|
||||||
model = User
|
model = User
|
||||||
template_name = 'member/profile_alias.html'
|
template_name = 'member/profile_alias.html'
|
||||||
context_object_name = 'user_object'
|
context_object_name = 'user_object'
|
||||||
form_class = AliasForm
|
form_class = AliasForm
|
||||||
|
|
||||||
def get_context_data(self,**kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
note = context['user_object'].note
|
note = context['user_object'].note
|
||||||
context["aliases"] = AliasTable(note.alias_set.all())
|
context["aliases"] = AliasTable(note.alias_set.all())
|
||||||
@ -172,7 +173,7 @@ class AliasView(LoginRequiredMixin,FormMixin,DetailView):
|
|||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse_lazy('member:user_alias', kwargs={'pk': self.object.id})
|
return reverse_lazy('member:user_alias', kwargs={'pk': self.object.id})
|
||||||
|
|
||||||
def post(self,request,*args,**kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
form = self.get_form()
|
form = self.get_form()
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
@ -186,42 +187,45 @@ class AliasView(LoginRequiredMixin,FormMixin,DetailView):
|
|||||||
alias.save()
|
alias.save()
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class DeleteAliasView(LoginRequiredMixin, DeleteView):
|
class DeleteAliasView(LoginRequiredMixin, DeleteView):
|
||||||
model = Alias
|
model = Alias
|
||||||
|
|
||||||
def delete(self,request,*args,**kwargs):
|
def delete(self, request, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
self.object.delete()
|
self.object.delete()
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
# TODO: pass message to redirected view.
|
# TODO: pass message to redirected view.
|
||||||
messages.error(self.request,str(e))
|
messages.error(self.request, str(e))
|
||||||
else:
|
else:
|
||||||
messages.success(self.request,_("Alias successfully deleted"))
|
messages.success(self.request, _("Alias successfully deleted"))
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
print(self.request)
|
print(self.request)
|
||||||
return reverse_lazy('member:user_alias',kwargs={'pk':self.object.note.user.pk})
|
return reverse_lazy('member:user_alias', kwargs={'pk': self.object.note.user.pk})
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
return self.post(request, *args, **kwargs)
|
return self.post(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ProfilePictureUpdateView(LoginRequiredMixin, FormMixin, DetailView):
|
class ProfilePictureUpdateView(LoginRequiredMixin, FormMixin, DetailView):
|
||||||
model = User
|
model = User
|
||||||
template_name = 'member/profile_picture_update.html'
|
template_name = 'member/profile_picture_update.html'
|
||||||
context_object_name = 'user_object'
|
context_object_name = 'user_object'
|
||||||
form_class = ImageForm
|
form_class = ImageForm
|
||||||
def get_context_data(self,*args,**kwargs):
|
|
||||||
context = super().get_context_data(*args,**kwargs)
|
def get_context_data(self, *args, **kwargs):
|
||||||
context['form'] = self.form_class(self.request.POST,self.request.FILES)
|
context = super().get_context_data(*args, **kwargs)
|
||||||
|
context['form'] = self.form_class(self.request.POST, self.request.FILES)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse_lazy('member:user_detail', kwargs={'pk': self.object.id})
|
return reverse_lazy('member:user_detail', kwargs={'pk': self.object.id})
|
||||||
|
|
||||||
def post(self,request,*args,**kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
form = self.get_form()
|
form = self.get_form()
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
return self.form_valid(form)
|
return self.form_valid(form)
|
||||||
@ -230,7 +234,7 @@ class ProfilePictureUpdateView(LoginRequiredMixin, FormMixin, DetailView):
|
|||||||
print(form)
|
print(form)
|
||||||
return self.form_invalid(form)
|
return self.form_invalid(form)
|
||||||
|
|
||||||
def form_valid(self,form):
|
def form_valid(self, form):
|
||||||
image_field = form.cleaned_data['image']
|
image_field = form.cleaned_data['image']
|
||||||
x = form.cleaned_data['x']
|
x = form.cleaned_data['x']
|
||||||
y = form.cleaned_data['y']
|
y = form.cleaned_data['y']
|
||||||
@ -238,15 +242,15 @@ class ProfilePictureUpdateView(LoginRequiredMixin, FormMixin, DetailView):
|
|||||||
h = form.cleaned_data['height']
|
h = form.cleaned_data['height']
|
||||||
# image crop and resize
|
# image crop and resize
|
||||||
image_file = io.BytesIO(image_field.read())
|
image_file = io.BytesIO(image_field.read())
|
||||||
ext = image_field.name.split('.')[-1].lower()
|
# ext = image_field.name.split('.')[-1].lower()
|
||||||
#TODO: support GIF format
|
# TODO: support GIF format
|
||||||
image = Image.open(image_file)
|
image = Image.open(image_file)
|
||||||
image = image.crop((x, y, x+w, y+h))
|
image = image.crop((x, y, x + w, y + h))
|
||||||
image_clean = image.resize((settings.PIC_WIDTH,
|
image_clean = image.resize((settings.PIC_WIDTH,
|
||||||
settings.PIC_RATIO*settings.PIC_WIDTH),
|
settings.PIC_RATIO * settings.PIC_WIDTH),
|
||||||
Image.ANTIALIAS)
|
Image.ANTIALIAS)
|
||||||
image_file = io.BytesIO()
|
image_file = io.BytesIO()
|
||||||
image_clean.save(image_file,"PNG")
|
image_clean.save(image_file, "PNG")
|
||||||
image_field.file = image_file
|
image_field.file = image_file
|
||||||
# renaming
|
# renaming
|
||||||
filename = "{}_pic.png".format(self.object.note.pk)
|
filename = "{}_pic.png".format(self.object.note.pk)
|
||||||
@ -255,7 +259,7 @@ class ProfilePictureUpdateView(LoginRequiredMixin, FormMixin, DetailView):
|
|||||||
self.object.note.save()
|
self.object.note.save()
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class ManageAuthTokens(LoginRequiredMixin, TemplateView):
|
class ManageAuthTokens(LoginRequiredMixin, TemplateView):
|
||||||
"""
|
"""
|
||||||
Affiche le jeton d'authentification, et permet de le regénérer
|
Affiche le jeton d'authentification, et permet de le regénérer
|
||||||
@ -283,6 +287,7 @@ class UserAutocomplete(autocomplete.Select2QuerySetView):
|
|||||||
"""
|
"""
|
||||||
Auto complete users by usernames
|
Auto complete users by usernames
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""
|
"""
|
||||||
Quand une personne cherche un utilisateur par pseudo, une requête est envoyée sur l'API dédiée à l'auto-complétion.
|
Quand une personne cherche un utilisateur par pseudo, une requête est envoyée sur l'API dédiée à l'auto-complétion.
|
||||||
@ -331,7 +336,7 @@ class ClubDetailView(LoginRequiredMixin, DetailView):
|
|||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
club = context["club"]
|
club = context["club"]
|
||||||
club_transactions = \
|
club_transactions = \
|
||||||
Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note))
|
Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note))
|
||||||
context['history_list'] = HistoryTable(club_transactions)
|
context['history_list'] = HistoryTable(club_transactions)
|
||||||
club_member = \
|
club_member = \
|
||||||
|
@ -47,11 +47,11 @@ class NoteClubAdmin(PolymorphicChildModelAdmin):
|
|||||||
"""
|
"""
|
||||||
Child for a club note, see NoteAdmin
|
Child for a club note, see NoteAdmin
|
||||||
"""
|
"""
|
||||||
inlines = (AliasInlines, )
|
inlines = (AliasInlines,)
|
||||||
|
|
||||||
# We can't change club after creation or the balance
|
# We can't change club after creation or the balance
|
||||||
readonly_fields = ('club', 'balance')
|
readonly_fields = ('club', 'balance')
|
||||||
search_fields = ('club', )
|
search_fields = ('club',)
|
||||||
|
|
||||||
def has_add_permission(self, request):
|
def has_add_permission(self, request):
|
||||||
"""
|
"""
|
||||||
@ -71,7 +71,7 @@ class NoteSpecialAdmin(PolymorphicChildModelAdmin):
|
|||||||
"""
|
"""
|
||||||
Child for a special note, see NoteAdmin
|
Child for a special note, see NoteAdmin
|
||||||
"""
|
"""
|
||||||
readonly_fields = ('balance', )
|
readonly_fields = ('balance',)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(NoteUser)
|
@admin.register(NoteUser)
|
||||||
@ -79,7 +79,7 @@ class NoteUserAdmin(PolymorphicChildModelAdmin):
|
|||||||
"""
|
"""
|
||||||
Child for an user note, see NoteAdmin
|
Child for an user note, see NoteAdmin
|
||||||
"""
|
"""
|
||||||
inlines = (AliasInlines, )
|
inlines = (AliasInlines,)
|
||||||
|
|
||||||
# We can't change user after creation or the balance
|
# We can't change user after creation or the balance
|
||||||
readonly_fields = ('user', 'balance')
|
readonly_fields = ('user', 'balance')
|
||||||
@ -133,7 +133,7 @@ class TransactionAdmin(PolymorphicParentModelAdmin):
|
|||||||
Else the amount of money would not be transferred
|
Else the amount of money would not be transferred
|
||||||
"""
|
"""
|
||||||
if obj: # user is editing an existing object
|
if obj: # user is editing an existing object
|
||||||
return 'created_at', 'source', 'destination', 'quantity',\
|
return 'created_at', 'source', 'destination', 'quantity', \
|
||||||
'amount'
|
'amount'
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@ -143,9 +143,9 @@ class TransactionTemplateAdmin(admin.ModelAdmin):
|
|||||||
"""
|
"""
|
||||||
Admin customisation for TransactionTemplate
|
Admin customisation for TransactionTemplate
|
||||||
"""
|
"""
|
||||||
list_display = ('name', 'poly_destination', 'amount', 'category', 'display', )
|
list_display = ('name', 'poly_destination', 'amount', 'category', 'display',)
|
||||||
list_filter = ('category', 'display')
|
list_filter = ('category', 'display')
|
||||||
autocomplete_fields = ('destination', )
|
autocomplete_fields = ('destination',)
|
||||||
|
|
||||||
def poly_destination(self, obj):
|
def poly_destination(self, obj):
|
||||||
"""
|
"""
|
||||||
@ -161,5 +161,5 @@ class TemplateCategoryAdmin(admin.ModelAdmin):
|
|||||||
"""
|
"""
|
||||||
Admin customisation for TransactionTemplate
|
Admin customisation for TransactionTemplate
|
||||||
"""
|
"""
|
||||||
list_display = ('name', )
|
list_display = ('name',)
|
||||||
list_filter = ('name', )
|
list_filter = ('name',)
|
||||||
|
@ -13,6 +13,7 @@ class NoteSerializer(serializers.ModelSerializer):
|
|||||||
REST API Serializer for Notes.
|
REST API Serializer for Notes.
|
||||||
The djangorestframework plugin will analyse the model `Note` and parse all fields in the API.
|
The djangorestframework plugin will analyse the model `Note` and parse all fields in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Note
|
model = Note
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
@ -29,6 +30,7 @@ class NoteClubSerializer(serializers.ModelSerializer):
|
|||||||
REST API Serializer for Club's notes.
|
REST API Serializer for Club's notes.
|
||||||
The djangorestframework plugin will analyse the model `NoteClub` and parse all fields in the API.
|
The djangorestframework plugin will analyse the model `NoteClub` and parse all fields in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = NoteClub
|
model = NoteClub
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
@ -39,6 +41,7 @@ class NoteSpecialSerializer(serializers.ModelSerializer):
|
|||||||
REST API Serializer for special notes.
|
REST API Serializer for special notes.
|
||||||
The djangorestframework plugin will analyse the model `NoteSpecial` and parse all fields in the API.
|
The djangorestframework plugin will analyse the model `NoteSpecial` and parse all fields in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = NoteSpecial
|
model = NoteSpecial
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
@ -49,6 +52,7 @@ class NoteUserSerializer(serializers.ModelSerializer):
|
|||||||
REST API Serializer for User's notes.
|
REST API Serializer for User's notes.
|
||||||
The djangorestframework plugin will analyse the model `NoteUser` and parse all fields in the API.
|
The djangorestframework plugin will analyse the model `NoteUser` and parse all fields in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = NoteUser
|
model = NoteUser
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
@ -59,6 +63,7 @@ class AliasSerializer(serializers.ModelSerializer):
|
|||||||
REST API Serializer for Aliases.
|
REST API Serializer for Aliases.
|
||||||
The djangorestframework plugin will analyse the model `Alias` and parse all fields in the API.
|
The djangorestframework plugin will analyse the model `Alias` and parse all fields in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Alias
|
model = Alias
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
@ -78,6 +83,7 @@ class TransactionTemplateSerializer(serializers.ModelSerializer):
|
|||||||
REST API Serializer for Transaction templates.
|
REST API Serializer for Transaction templates.
|
||||||
The djangorestframework plugin will analyse the model `TransactionTemplate` and parse all fields in the API.
|
The djangorestframework plugin will analyse the model `TransactionTemplate` and parse all fields in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = TransactionTemplate
|
model = TransactionTemplate
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
@ -88,6 +94,7 @@ class TransactionSerializer(serializers.ModelSerializer):
|
|||||||
REST API Serializer for Transactions.
|
REST API Serializer for Transactions.
|
||||||
The djangorestframework plugin will analyse the model `Transaction` and parse all fields in the API.
|
The djangorestframework plugin will analyse the model `Transaction` and parse all fields in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Transaction
|
model = Transaction
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
@ -98,6 +105,7 @@ class MembershipTransactionSerializer(serializers.ModelSerializer):
|
|||||||
REST API Serializer for Membership transactions.
|
REST API Serializer for Membership transactions.
|
||||||
The djangorestframework plugin will analyse the model `MembershipTransaction` and parse all fields in the API.
|
The djangorestframework plugin will analyse the model `MembershipTransaction` and parse all fields in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = MembershipTransaction
|
model = MembershipTransaction
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
@ -4,11 +4,11 @@
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
|
|
||||||
from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
|
|
||||||
from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction
|
|
||||||
from .serializers import NoteSerializer, NotePolymorphicSerializer, NoteClubSerializer, NoteSpecialSerializer, \
|
from .serializers import NoteSerializer, NotePolymorphicSerializer, NoteClubSerializer, NoteSpecialSerializer, \
|
||||||
NoteUserSerializer, AliasSerializer, \
|
NoteUserSerializer, AliasSerializer, \
|
||||||
TransactionTemplateSerializer, TransactionSerializer, MembershipTransactionSerializer
|
TransactionTemplateSerializer, TransactionSerializer, MembershipTransactionSerializer
|
||||||
|
from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
|
||||||
|
from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction
|
||||||
|
|
||||||
|
|
||||||
class NoteViewSet(viewsets.ModelViewSet):
|
class NoteViewSet(viewsets.ModelViewSet):
|
||||||
|
@ -3,31 +3,25 @@
|
|||||||
|
|
||||||
from dal import autocomplete
|
from dal import autocomplete
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
import os
|
from .models import Alias
|
||||||
|
|
||||||
from crispy_forms.helper import FormHelper
|
|
||||||
from crispy_forms.bootstrap import Div
|
|
||||||
from crispy_forms.layout import Layout, HTML
|
|
||||||
|
|
||||||
from .models import Transaction, TransactionTemplate, TemplateTransaction
|
from .models import Transaction, TransactionTemplate, TemplateTransaction
|
||||||
from .models import Note, Alias
|
|
||||||
|
|
||||||
class AliasForm(forms.ModelForm):
|
class AliasForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Alias
|
model = Alias
|
||||||
fields = ("name",)
|
fields = ("name",)
|
||||||
|
|
||||||
def __init__(self,*args,**kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args,**kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.fields["name"].label = False
|
self.fields["name"].label = False
|
||||||
self.fields["name"].widget.attrs={"placeholder":_('New Alias')}
|
self.fields["name"].widget.attrs = {"placeholder": _('New Alias')}
|
||||||
|
|
||||||
|
|
||||||
class ImageForm(forms.Form):
|
class ImageForm(forms.Form):
|
||||||
image = forms.ImageField(required = False,
|
image = forms.ImageField(required=False,
|
||||||
label=_('select an image'),
|
label=_('select an image'),
|
||||||
help_text=_('Maximal size: 2MB'))
|
help_text=_('Maximal size: 2MB'))
|
||||||
x = forms.FloatField(widget=forms.HiddenInput())
|
x = forms.FloatField(widget=forms.HiddenInput())
|
||||||
@ -35,7 +29,7 @@ class ImageForm(forms.Form):
|
|||||||
width = forms.FloatField(widget=forms.HiddenInput())
|
width = forms.FloatField(widget=forms.HiddenInput())
|
||||||
height = forms.FloatField(widget=forms.HiddenInput())
|
height = forms.FloatField(widget=forms.HiddenInput())
|
||||||
|
|
||||||
|
|
||||||
class TransactionTemplateForm(forms.ModelForm):
|
class TransactionTemplateForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = TransactionTemplate
|
model = TransactionTemplate
|
||||||
@ -48,13 +42,13 @@ class TransactionTemplateForm(forms.ModelForm):
|
|||||||
# forward=(forward.Const('TYPE', 'note_type') où TYPE est dans {user, club, special}
|
# forward=(forward.Const('TYPE', 'note_type') où TYPE est dans {user, club, special}
|
||||||
widgets = {
|
widgets = {
|
||||||
'destination':
|
'destination':
|
||||||
autocomplete.ModelSelect2(
|
autocomplete.ModelSelect2(
|
||||||
url='note:note_autocomplete',
|
url='note:note_autocomplete',
|
||||||
attrs={
|
attrs={
|
||||||
'data-placeholder': 'Note ...',
|
'data-placeholder': 'Note ...',
|
||||||
'data-minimum-input-length': 1,
|
'data-minimum-input-length': 1,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -62,7 +56,6 @@ class TransactionForm(forms.ModelForm):
|
|||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
super().save(commit)
|
super().save(commit)
|
||||||
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
"""
|
"""
|
||||||
If the user has no right to transfer funds, then it will be the source of the transfer by default.
|
If the user has no right to transfer funds, then it will be the source of the transfer by default.
|
||||||
@ -70,7 +63,7 @@ class TransactionForm(forms.ModelForm):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
cleaned_data = super().clean()
|
cleaned_data = super().clean()
|
||||||
if not "source" in cleaned_data: # TODO Replace it with "if %user has no right to transfer funds"
|
if "source" not in cleaned_data: # TODO Replace it with "if %user has no right to transfer funds"
|
||||||
cleaned_data["source"] = self.user.note
|
cleaned_data["source"] = self.user.note
|
||||||
|
|
||||||
if cleaned_data["source"].pk == cleaned_data["destination"].pk:
|
if cleaned_data["source"].pk == cleaned_data["destination"].pk:
|
||||||
@ -78,7 +71,6 @@ class TransactionForm(forms.ModelForm):
|
|||||||
|
|
||||||
return cleaned_data
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Transaction
|
model = Transaction
|
||||||
fields = (
|
fields = (
|
||||||
@ -91,21 +83,21 @@ class TransactionForm(forms.ModelForm):
|
|||||||
# Voir ci-dessus
|
# Voir ci-dessus
|
||||||
widgets = {
|
widgets = {
|
||||||
'source':
|
'source':
|
||||||
autocomplete.ModelSelect2(
|
autocomplete.ModelSelect2(
|
||||||
url='note:note_autocomplete',
|
url='note:note_autocomplete',
|
||||||
attrs={
|
attrs={
|
||||||
'data-placeholder': 'Note ...',
|
'data-placeholder': 'Note ...',
|
||||||
'data-minimum-input-length': 1,
|
'data-minimum-input-length': 1,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
'destination':
|
'destination':
|
||||||
autocomplete.ModelSelect2(
|
autocomplete.ModelSelect2(
|
||||||
url='note:note_autocomplete',
|
url='note:note_autocomplete',
|
||||||
attrs={
|
attrs={
|
||||||
'data-placeholder': 'Note ...',
|
'data-placeholder': 'Note ...',
|
||||||
'data-minimum-input-length': 1,
|
'data-minimum-input-length': 1,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -122,18 +114,18 @@ class ConsoForm(forms.ModelForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = TemplateTransaction
|
model = TemplateTransaction
|
||||||
fields = ('source', )
|
fields = ('source',)
|
||||||
|
|
||||||
# Le champ d'utilisateur est remplacé par un champ d'auto-complétion.
|
# Le champ d'utilisateur 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
|
# 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 de note valides
|
# et récupère les aliases de note valides
|
||||||
widgets = {
|
widgets = {
|
||||||
'source':
|
'source':
|
||||||
autocomplete.ModelSelect2(
|
autocomplete.ModelSelect2(
|
||||||
url='note:note_autocomplete',
|
url='note:note_autocomplete',
|
||||||
attrs={
|
attrs={
|
||||||
'data-placeholder': 'Note ...',
|
'data-placeholder': 'Note ...',
|
||||||
'data-minimum-input-length': 1,
|
'data-minimum-input-length': 1,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ from django.core.validators import RegexValidator
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from polymorphic.models import PolymorphicModel
|
from polymorphic.models import PolymorphicModel
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Defines each note types
|
Defines each note types
|
||||||
"""
|
"""
|
||||||
@ -27,7 +28,7 @@ class Note(PolymorphicModel):
|
|||||||
help_text=_('in centimes, money credited for this instance'),
|
help_text=_('in centimes, money credited for this instance'),
|
||||||
default=0,
|
default=0,
|
||||||
)
|
)
|
||||||
last_negative= models.DateTimeField(
|
last_negative = models.DateTimeField(
|
||||||
verbose_name=_('last negative date'),
|
verbose_name=_('last negative date'),
|
||||||
help_text=_('last time the balance was negative'),
|
help_text=_('last time the balance was negative'),
|
||||||
null=True,
|
null=True,
|
||||||
@ -98,7 +99,7 @@ class Note(PolymorphicModel):
|
|||||||
# Alias exists, so check if it is linked to this note
|
# Alias exists, so check if it is linked to this note
|
||||||
if aliases.first().note != self:
|
if aliases.first().note != self:
|
||||||
raise ValidationError(_('This alias is already taken.'),
|
raise ValidationError(_('This alias is already taken.'),
|
||||||
code="same_alias",)
|
code="same_alias", )
|
||||||
else:
|
else:
|
||||||
# Alias does not exist yet, so check if it can exist
|
# Alias does not exist yet, so check if it can exist
|
||||||
a = Alias(name=str(self))
|
a = Alias(name=str(self))
|
||||||
@ -231,12 +232,12 @@ class Alias(models.Model):
|
|||||||
sim_alias = Alias.objects.get(normalized_name=normalized_name)
|
sim_alias = Alias.objects.get(normalized_name=normalized_name)
|
||||||
if self != sim_alias:
|
if self != sim_alias:
|
||||||
raise ValidationError(_('An alias with a similar name already exists: {} '.format(sim_alias)),
|
raise ValidationError(_('An alias with a similar name already exists: {} '.format(sim_alias)),
|
||||||
code="same_alias"
|
code="same_alias"
|
||||||
)
|
)
|
||||||
except Alias.DoesNotExist:
|
except Alias.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
self.normalized_name = normalized_name
|
self.normalized_name = normalized_name
|
||||||
|
|
||||||
def delete(self, using=None, keep_parents=False):
|
def delete(self, using=None, keep_parents=False):
|
||||||
if self.name == str(self.note):
|
if self.name == str(self.note):
|
||||||
raise ValidationError(_("You can't delete your main alias."),
|
raise ValidationError(_("You can't delete your main alias."),
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.urls import reverse
|
|
||||||
from polymorphic.models import PolymorphicModel
|
from polymorphic.models import PolymorphicModel
|
||||||
|
|
||||||
from .notes import Note, NoteClub
|
from .notes import Note, NoteClub
|
||||||
@ -44,7 +44,7 @@ class TransactionTemplate(models.Model):
|
|||||||
verbose_name=_('name'),
|
verbose_name=_('name'),
|
||||||
max_length=255,
|
max_length=255,
|
||||||
unique=True,
|
unique=True,
|
||||||
error_messages={'unique':_("A template with this name already exist")},
|
error_messages={'unique': _("A template with this name already exist")},
|
||||||
)
|
)
|
||||||
destination = models.ForeignKey(
|
destination = models.ForeignKey(
|
||||||
NoteClub,
|
NoteClub,
|
||||||
@ -63,7 +63,7 @@ class TransactionTemplate(models.Model):
|
|||||||
max_length=31,
|
max_length=31,
|
||||||
)
|
)
|
||||||
display = models.BooleanField(
|
display = models.BooleanField(
|
||||||
default = True,
|
default=True,
|
||||||
)
|
)
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
verbose_name=_('description'),
|
verbose_name=_('description'),
|
||||||
@ -75,7 +75,7 @@ class TransactionTemplate(models.Model):
|
|||||||
verbose_name_plural = _("transaction templates")
|
verbose_name_plural = _("transaction templates")
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('note:template_update', args=(self.pk, ))
|
return reverse('note:template_update', args=(self.pk,))
|
||||||
|
|
||||||
|
|
||||||
class Transaction(PolymorphicModel):
|
class Transaction(PolymorphicModel):
|
||||||
@ -168,6 +168,7 @@ class TemplateTransaction(Transaction):
|
|||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class MembershipTransaction(Transaction):
|
class MembershipTransaction(Transaction):
|
||||||
"""
|
"""
|
||||||
Special type of :model:`note.Transaction` associated to a :model:`member.Membership`.
|
Special type of :model:`note.Transaction` associated to a :model:`member.Membership`.
|
||||||
|
@ -4,14 +4,16 @@
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
from django_tables2.utils import A
|
from django_tables2.utils import A
|
||||||
from .models.transactions import Transaction
|
|
||||||
from .models.notes import Alias
|
from .models.notes import Alias
|
||||||
|
from .models.transactions import Transaction
|
||||||
|
|
||||||
|
|
||||||
class HistoryTable(tables.Table):
|
class HistoryTable(tables.Table):
|
||||||
class Meta:
|
class Meta:
|
||||||
attrs = {
|
attrs = {
|
||||||
'class':
|
'class':
|
||||||
'table table-condensed table-striped table-hover'
|
'table table-condensed table-striped table-hover'
|
||||||
}
|
}
|
||||||
model = Transaction
|
model = Transaction
|
||||||
template_name = 'django_tables2/bootstrap4.html'
|
template_name = 'django_tables2/bootstrap4.html'
|
||||||
@ -25,21 +27,22 @@ class HistoryTable(tables.Table):
|
|||||||
.order_by(('-' if is_descending else '') + 'total')
|
.order_by(('-' if is_descending else '') + 'total')
|
||||||
return (queryset, True)
|
return (queryset, True)
|
||||||
|
|
||||||
|
|
||||||
class AliasTable(tables.Table):
|
class AliasTable(tables.Table):
|
||||||
class Meta:
|
class Meta:
|
||||||
attrs = {
|
attrs = {
|
||||||
'class':
|
'class':
|
||||||
'table table condensed table-striped table-hover'
|
'table table condensed table-striped table-hover'
|
||||||
}
|
}
|
||||||
model = Alias
|
model = Alias
|
||||||
fields =('name',)
|
fields = ('name',)
|
||||||
template_name = 'django_tables2/bootstrap4.html'
|
template_name = 'django_tables2/bootstrap4.html'
|
||||||
|
|
||||||
show_header = False
|
show_header = False
|
||||||
name = tables.Column(attrs={'td':{'class':'text-center'}})
|
name = tables.Column(attrs={'td': {'class': 'text-center'}})
|
||||||
delete = tables.LinkColumn('member:user_alias_delete',
|
delete = tables.LinkColumn('member:user_alias_delete',
|
||||||
args=[A('pk')],
|
args=[A('pk')],
|
||||||
attrs={
|
attrs={
|
||||||
'td': {'class':'col-sm-2'},
|
'td': {'class': 'col-sm-2'},
|
||||||
'a': {'class': 'btn btn-danger'} },
|
'a': {'class': 'btn btn-danger'}},
|
||||||
text='delete',accessor='pk')
|
text='delete', accessor='pk')
|
||||||
|
@ -8,8 +8,8 @@ from django.urls import reverse
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.generic import CreateView, ListView, UpdateView
|
from django.views.generic import CreateView, ListView, UpdateView
|
||||||
|
|
||||||
from .models import Transaction, TransactionTemplate, Alias, TemplateTransaction
|
|
||||||
from .forms import TransactionForm, TransactionTemplateForm, ConsoForm
|
from .forms import TransactionForm, TransactionTemplateForm, ConsoForm
|
||||||
|
from .models import Transaction, TransactionTemplate, Alias, TemplateTransaction
|
||||||
|
|
||||||
|
|
||||||
class TransactionCreate(LoginRequiredMixin, CreateView):
|
class TransactionCreate(LoginRequiredMixin, CreateView):
|
||||||
@ -53,6 +53,7 @@ class NoteAutocomplete(autocomplete.Select2QuerySetView):
|
|||||||
"""
|
"""
|
||||||
Auto complete note by aliases
|
Auto complete note by aliases
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""
|
"""
|
||||||
Quand une personne cherche un alias, une requête est envoyée sur l'API dédiée à l'auto-complétion.
|
Quand une personne cherche un alias, une requête est envoyée sur l'API dédiée à l'auto-complétion.
|
||||||
@ -66,7 +67,7 @@ class NoteAutocomplete(autocomplete.Select2QuerySetView):
|
|||||||
|
|
||||||
# self.q est le paramètre de la recherche
|
# self.q est le paramètre de la recherche
|
||||||
if self.q:
|
if self.q:
|
||||||
qs = qs.filter(Q(name__regex=self.q) | Q(normalized_name__regex=Alias.normalize(self.q)))\
|
qs = qs.filter(Q(name__regex=self.q) | Q(normalized_name__regex=Alias.normalize(self.q))) \
|
||||||
.order_by('normalized_name').distinct()
|
.order_by('normalized_name').distinct()
|
||||||
|
|
||||||
# Filtrage par type de note (user, club, special)
|
# Filtrage par type de note (user, club, special)
|
||||||
@ -147,4 +148,3 @@ class ConsoView(LoginRequiredMixin, CreateView):
|
|||||||
When clicking a button, reload the same page
|
When clicking a button, reload the same page
|
||||||
"""
|
"""
|
||||||
return reverse('note:consos')
|
return reverse('note:consos')
|
||||||
|
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from django.http import HttpResponseRedirect
|
|
||||||
|
|
||||||
from urllib.parse import urlencode, parse_qs, urlsplit, urlunsplit
|
|
||||||
|
|
||||||
|
|
||||||
class TurbolinksMiddleware(object):
|
class TurbolinksMiddleware(object):
|
||||||
"""
|
"""
|
||||||
@ -35,4 +31,3 @@ class TurbolinksMiddleware(object):
|
|||||||
location = request.session.pop('_turbolinks_redirect_to')
|
location = request.session.pop('_turbolinks_redirect_to')
|
||||||
response['Turbolinks-Location'] = location
|
response['Turbolinks-Location'] = location
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import os
|
|
||||||
import re
|
|
||||||
|
|
||||||
from .base import *
|
from .base import *
|
||||||
|
|
||||||
|
|
||||||
def read_env():
|
def read_env():
|
||||||
"""Pulled from Honcho code with minor updates, reads local default
|
"""Pulled from Honcho code with minor updates, reads local default
|
||||||
environment variables from a .env file located in the project root
|
environment variables from a .env file located in the project root
|
||||||
@ -25,14 +23,16 @@ def read_env():
|
|||||||
val = re.sub(r'\\(.)', r'\1', m3.group(1))
|
val = re.sub(r'\\(.)', r'\1', m3.group(1))
|
||||||
os.environ.setdefault(key, val)
|
os.environ.setdefault(key, val)
|
||||||
|
|
||||||
|
|
||||||
read_env()
|
read_env()
|
||||||
|
|
||||||
app_stage = os.environ.get('DJANGO_APP_STAGE', 'dev')
|
app_stage = os.environ.get('DJANGO_APP_STAGE', 'dev')
|
||||||
if app_stage == 'prod':
|
if app_stage == 'prod':
|
||||||
from .production import *
|
from .production import *
|
||||||
DATABASES["default"]["PASSWORD"] = os.environ.get('DJANGO_DB_PASSWORD','CHANGE_ME_IN_ENV_SETTINGS')
|
|
||||||
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY','CHANGE_ME_IN_ENV_SETTINGS')
|
DATABASES["default"]["PASSWORD"] = os.environ.get('DJANGO_DB_PASSWORD', 'CHANGE_ME_IN_ENV_SETTINGS')
|
||||||
ALLOWED_HOSTS.append(os.environ.get('ALLOWED_HOSTS','localhost'))
|
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'CHANGE_ME_IN_ENV_SETTINGS')
|
||||||
|
ALLOWED_HOSTS.append(os.environ.get('ALLOWED_HOSTS', 'localhost'))
|
||||||
else:
|
else:
|
||||||
from .development import *
|
from .development import *
|
||||||
|
|
||||||
@ -43,4 +43,3 @@ except ImportError:
|
|||||||
|
|
||||||
# env variables set at the of in /env/bin/activate
|
# env variables set at the of in /env/bin/activate
|
||||||
# don't forget to unset in deactivate !
|
# don't forget to unset in deactivate !
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ TEMPLATES = [
|
|||||||
'django.contrib.auth.context_processors.auth',
|
'django.contrib.auth.context_processors.auth',
|
||||||
'django.contrib.messages.context_processors.messages',
|
'django.contrib.messages.context_processors.messages',
|
||||||
'django.template.context_processors.request',
|
'django.template.context_processors.request',
|
||||||
# 'django.template.context_processors.media',
|
# 'django.template.context_processors.media',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -183,7 +183,7 @@ FIXTURE_DIRS = [os.path.join(BASE_DIR, "note_kfet/fixtures")]
|
|||||||
# Don't put anything in this directory yourself; store your static files
|
# Don't put anything in this directory yourself; store your static files
|
||||||
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
|
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
|
||||||
# Example: "/var/www/example.com/static/"
|
# Example: "/var/www/example.com/static/"
|
||||||
STATIC_ROOT = os.path.join(BASE_DIR,"static/")
|
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
|
||||||
# STATICFILES_DIRS = [
|
# STATICFILES_DIRS = [
|
||||||
# os.path.join(BASE_DIR, 'static')]
|
# os.path.join(BASE_DIR, 'static')]
|
||||||
STATICFILES_DIRS = []
|
STATICFILES_DIRS = []
|
||||||
@ -195,8 +195,8 @@ STATIC_URL = '/static/'
|
|||||||
|
|
||||||
ALIAS_VALIDATOR_REGEX = r''
|
ALIAS_VALIDATOR_REGEX = r''
|
||||||
|
|
||||||
MEDIA_ROOT=os.path.join(BASE_DIR,"media")
|
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
|
||||||
MEDIA_URL='/media/'
|
MEDIA_URL = '/media/'
|
||||||
|
|
||||||
# Profile Picture Settings
|
# Profile Picture Settings
|
||||||
PIC_WIDTH = 200
|
PIC_WIDTH = 200
|
||||||
@ -211,7 +211,7 @@ CAS_SHOW_POWERED = False
|
|||||||
CAS_REDIRECT_TO_LOGIN_AFTER_LOGOUT = False
|
CAS_REDIRECT_TO_LOGIN_AFTER_LOGOUT = False
|
||||||
CAS_INFO_MESSAGES = {
|
CAS_INFO_MESSAGES = {
|
||||||
"cas_explained": {
|
"cas_explained": {
|
||||||
"message":_(
|
"message": _(
|
||||||
u"The Central Authentication Service grants you access to most of our websites by "
|
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"authenticating only once, so you don't need to type your credentials again unless "
|
||||||
u"your session expires or you logout."
|
u"your session expires or you logout."
|
||||||
@ -224,4 +224,3 @@ CAS_INFO_MESSAGES = {
|
|||||||
CAS_INFO_MESSAGES_ORDER = [
|
CAS_INFO_MESSAGES_ORDER = [
|
||||||
'cas_explained',
|
'cas_explained',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -11,10 +11,11 @@
|
|||||||
# - and more ...
|
# - and more ...
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
|
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
|
||||||
from . import *
|
from . import *
|
||||||
import os
|
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
@ -53,8 +54,6 @@ SESSION_COOKIE_AGE = 60 * 60 * 3
|
|||||||
# Can be modified in secrets.py
|
# Can be modified in secrets.py
|
||||||
CAS_SERVER_URL = "http://localhost:8000/cas/"
|
CAS_SERVER_URL = "http://localhost:8000/cas/"
|
||||||
|
|
||||||
|
STATIC_ROOT = '' # not needed in development settings
|
||||||
STATIC_ROOT = '' # not needed in development settings
|
|
||||||
STATICFILES_DIRS = [
|
STATICFILES_DIRS = [
|
||||||
os.path.join(BASE_DIR, 'static')]
|
os.path.join(BASE_DIR, 'static')]
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ DATABASES = {
|
|||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
# Mandatory !
|
# Mandatory !
|
||||||
ALLOWED_HOSTS = ['127.0.0.1','note.comby.xyz']
|
ALLOWED_HOSTS = ['127.0.0.1', 'note.comby.xyz']
|
||||||
|
|
||||||
# Emails
|
# Emails
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from cas import views as cas_views
|
||||||
|
from django.conf import settings
|
||||||
|
from django.conf.urls.static import static
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
from django.views.generic import RedirectView
|
from django.views.generic import RedirectView
|
||||||
from django.conf.urls.static import static
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
from cas import views as cas_views
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# Dev so redirect to something random
|
# Dev so redirect to something random
|
||||||
@ -36,5 +35,5 @@ urlpatterns = [
|
|||||||
path('logs/', include('logs.urls')),
|
path('logs/', include('logs.urls')),
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
|
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
urlpatterns += static(settings.STATIC_URL,document_root=settings.STATIC_ROOT)
|
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||||
<SOAP-ENV:Header />
|
<SOAP-ENV:Header />
|
||||||
<SOAP-ENV:Body>
|
<SOAP-ENV:Body>
|
||||||
<Response xmlns="urn:oasis:names:tc:SAML:1.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
|
<Response xmlns="urn:oasis:names:tc:SAML:1.0:protocol"
|
||||||
xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" IssueInstant="{{IssueInstant}}"
|
IssueInstant="{{ IssueInstant }}"
|
||||||
MajorVersion="1" MinorVersion="1" Recipient="{{Recipient}}"
|
MajorVersion="1" MinorVersion="1" Recipient="{{ Recipient }}"
|
||||||
ResponseID="{{ResponseID}}">
|
ResponseID="{{ ResponseID }}">
|
||||||
<Status>
|
<Status>
|
||||||
<StatusCode Value="samlp:Success">
|
<StatusCode Value="samlp:Success">
|
||||||
</StatusCode>
|
</StatusCode>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||||
<SOAP-ENV:Header />
|
<SOAP-ENV:Header />
|
||||||
<SOAP-ENV:Body>
|
<SOAP-ENV:Body>
|
||||||
<Response xmlns="urn:oasis:names:tc:SAML:1.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
|
<Response xmlns="urn:oasis:names:tc:SAML:1.0:protocol"
|
||||||
xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" IssueInstant="{{IssueInstant}}"
|
IssueInstant="{{ IssueInstant }}"
|
||||||
MajorVersion="1" MinorVersion="1" Recipient="{{Recipient}}"
|
MajorVersion="1" MinorVersion="1" Recipient="{{ Recipient }}"
|
||||||
ResponseID="{{ResponseID}}">
|
ResponseID="{{ ResponseID }}">
|
||||||
<Status>
|
<Status>
|
||||||
<StatusCode Value="samlp:{{code}}">{{msg}}</StatusCode>
|
<StatusCode Value="samlp:{{code}}">{{msg}}</StatusCode>
|
||||||
</Status>
|
</Status>
|
||||||
|
Loading…
Reference in New Issue
Block a user