Skip to content
Snippets Groups Projects
views.py 15.5 KiB
Newer Older
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed

ynerant's avatar
ynerant committed
import io
from datetime import datetime
ynerant's avatar
ynerant committed

from PIL import Image
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
ynerant's avatar
ynerant committed
from django.contrib.auth.models import User
ynerant's avatar
ynerant committed
from django.contrib.auth.views import LoginView
ynerant's avatar
ynerant committed
from django.db.models import Q
ynerant's avatar
ynerant committed
from django.forms import HiddenInput
ynerant's avatar
ynerant committed
from django.shortcuts import redirect
ynerant's avatar
ynerant committed
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
ynerant's avatar
ynerant committed
from django.views.generic import CreateView, DetailView, UpdateView, TemplateView
from django.views.generic.edit import FormMixin
from django_tables2.views import SingleTableView
ynerant's avatar
ynerant committed
from rest_framework.authtoken.models import Token
from note.forms import ImageForm
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
from note.models import Alias, NoteUser
from note.models.transactions import Transaction
ynerant's avatar
ynerant committed
from note.tables import HistoryTable, AliasTable
ynerant's avatar
ynerant committed
from permission.backends import PermissionBackend
from permission.views import ProtectQuerysetMixin
ynerant's avatar
ynerant committed

ynerant's avatar
ynerant committed
from .filters import UserFilter, UserFilterFormHelper
from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, CustomAuthenticationForm
ynerant's avatar
ynerant committed
from .models import Club, Membership
ynerant's avatar
ynerant committed
from .tables import ClubTable, UserTable, MembershipTable
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed

ynerant's avatar
ynerant committed
class CustomLoginView(LoginView):
    form_class = CustomAuthenticationForm

    def form_valid(self, form):
        self.request.session['permission_mask'] = form.cleaned_data['permission_mask'].rank
        return super().form_valid(form)


class UserCreateView(CreateView):
Pierre-antoine Comby's avatar
Pierre-antoine Comby committed
    """
    Une vue pour inscrire un utilisateur et lui créer un profile
    """
    form_class = SignUpForm
Pierre-antoine Comby's avatar
Pierre-antoine Comby committed
    success_url = reverse_lazy('login')
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    template_name = 'member/signup.html'
    second_form = ProfileForm
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
Pierre-antoine Comby's avatar
Pierre-antoine Comby committed
        context["profile_form"] = self.second_form()
Pierre-antoine Comby's avatar
Pierre-antoine Comby committed

        return context
Pierre-antoine Comby's avatar
Pierre-antoine Comby committed

    def form_valid(self, form):
        profile_form = ProfileForm(self.request.POST)
        if form.is_valid() and profile_form.is_valid():
            user = form.save(commit=False)
            user.profile = profile_form.save(commit=False)
            user.save()
            user.profile.save()
Pierre-antoine Comby's avatar
Pierre-antoine Comby committed
        return super().form_valid(form)
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed

class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
    model = User
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    fields = ['first_name', 'last_name', 'username', 'email']
    template_name = 'member/profile_update.html'
Pierre-antoine Comby's avatar
Pierre-antoine Comby committed
    context_object_name = 'user_object'
    profile_form = ProfileForm
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['profile_form'] = self.profile_form(instance=context['user_object'].profile)
        context['title'] = _("Update Profile")
        return context

    def get_form(self, form_class=None):
ynerant's avatar
ynerant committed
        form = super().get_form(form_class)
        if 'username' not in form.data:
            return form
        new_username = form.data['username']
ynerant's avatar
ynerant committed
        # Si l'utilisateur cherche à modifier son pseudo, le nouveau pseudo ne doit pas être proche d'un alias existant
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
        note = NoteUser.objects.filter(
            alias__normalized_name=Alias.normalize(new_username))
Pierre-antoine Comby's avatar
Pierre-antoine Comby committed
        if note.exists() and note.get().user != self.object:
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
            form.add_error('username',
                           _("An alias with a similar name already exists."))
    def form_valid(self, form):
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
        profile_form = ProfileForm(
            data=self.request.POST,
Pierre-antoine Comby's avatar
Pierre-antoine Comby committed
            instance=self.object.profile,
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
        )
        if form.is_valid() and profile_form.is_valid():
            new_username = form.data['username']
            alias = Alias.objects.filter(name=new_username)
            # Si le nouveau pseudo n'est pas un de nos alias,
            # on supprime éventuellement un alias similaire pour le remplacer
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
                similar = Alias.objects.filter(
                    normalized_name=Alias.normalize(new_username))
                if similar.exists():
                    similar.delete()
ynerant's avatar
ynerant committed

            user = form.save(commit=False)
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
            profile = profile_form.save(commit=False)
            profile.user = user
            profile.save()
ynerant's avatar
ynerant committed
            user.save()
        return super().form_valid(form)

    def get_success_url(self, **kwargs):
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
        if kwargs:
            return reverse_lazy('member:user_detail',
                                kwargs={'pk': kwargs['id']})
ynerant's avatar
ynerant committed
            return reverse_lazy('member:user_detail', args=(self.object.id,))
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed

class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    Affiche les informations sur un utilisateur, sa note, ses clubs...
Pierre-antoine Comby's avatar
Pierre-antoine Comby committed
    model = User
    context_object_name = "user_object"
    template_name = "member/profile_detail.html"
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
Pierre-antoine Comby's avatar
Pierre-antoine Comby committed
        user = context['user_object']
            Transaction.objects.all().filter(Q(source=user.note) | Q(destination=user.note)).order_by("-id")\
            .filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view"))
        context['history_list'] = HistoryTable(history_list)
        club_list = Membership.objects.all().filter(user=user)\
            .filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view")).only("club")
ynerant's avatar
ynerant committed
        context['club_list'] = MembershipTable(data=club_list)
        return context
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed

class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
    """
    Affiche la liste des utilisateurs, avec une fonction de recherche statique
    """
    model = User
    table_class = UserTable
    template_name = 'member/user_list.html'
    filter_class = UserFilter
    formhelper_class = UserFilterFormHelper
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    def get_queryset(self, **kwargs):
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
        self.filter = self.filter_class(self.request.GET, queryset=qs)
        self.filter.form.helper = self.formhelper_class()
        return self.filter.qs

me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["filter"] = self.filter
        return context

ynerant's avatar
ynerant committed

class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
    model = User
    template_name = 'member/profile_alias.html'
    context_object_name = 'user_object'
ynerant's avatar
ynerant committed

ynerant's avatar
ynerant committed
    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

ynerant's avatar
ynerant committed

class PictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, DetailView):
    form_class = ImageForm
ynerant's avatar
ynerant committed

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
ynerant's avatar
ynerant committed
        context['form'] = self.form_class(self.request.POST, self.request.FILES)
ynerant's avatar
ynerant committed

    def get_success_url(self):
        return reverse_lazy('member:user_detail', kwargs={'pk': self.object.id})

ynerant's avatar
ynerant committed
    def post(self, request, *args, **kwargs):
        form = self.get_form()
        self.object = self.get_object()
        if form.is_valid():
            return self.form_valid(form)
        else:
            print('is_invalid')
            print(form)
            return self.form_invalid(form)

ynerant's avatar
ynerant committed
    def form_valid(self, form):
        image_field = form.cleaned_data['image']
        x = form.cleaned_data['x']
        y = form.cleaned_data['y']
        w = form.cleaned_data['width']
        h = form.cleaned_data['height']
        # image crop and resize
        image_file = io.BytesIO(image_field.read())
ynerant's avatar
ynerant committed
        # ext = image_field.name.split('.')[-1].lower()
        # TODO: support GIF format
        image = Image.open(image_file)
ynerant's avatar
ynerant committed
        image = image.crop((x, y, x + w, y + h))
        image_clean = image.resize((settings.PIC_WIDTH,
ynerant's avatar
ynerant committed
                                    settings.PIC_RATIO * settings.PIC_WIDTH),
                                   Image.ANTIALIAS)
        image_file = io.BytesIO()
ynerant's avatar
ynerant committed
        image_clean.save(image_file, "PNG")
        image_field.file = image_file
        # renaming
        filename = "{}_pic.png".format(self.object.note.pk)
        image_field.name = filename
        self.object.note.display_image = image_field
        self.object.note.save()
        return super().form_valid(form)

class ProfilePictureUpdateView(PictureUpdateView):
    model = User
    template_name = 'member/profile_picture_update.html'
    context_object_name = 'user_object'
ynerant's avatar
ynerant committed

ynerant's avatar
ynerant committed
class ManageAuthTokens(LoginRequiredMixin, TemplateView):
ynerant's avatar
ynerant committed
    """
ynerant's avatar
ynerant committed
    Affiche le jeton d'authentification, et permet de le regénérer
ynerant's avatar
ynerant committed
    """
ynerant's avatar
ynerant committed
    model = Token
    template_name = "member/manage_auth_tokens.html"
ynerant's avatar
ynerant committed

ynerant's avatar
ynerant committed
    def get(self, request, *args, **kwargs):
        if 'regenerate' in request.GET and Token.objects.filter(user=request.user).exists():
ynerant's avatar
ynerant committed
            Token.objects.get(user=self.request.user).delete()
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
            return redirect(reverse_lazy('member:auth_token') + "?show",
                            permanent=True)
ynerant's avatar
ynerant committed

ynerant's avatar
ynerant committed
        return super().get(request, *args, **kwargs)
ynerant's avatar
ynerant committed

ynerant's avatar
ynerant committed
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['token'] = Token.objects.get_or_create(user=self.request.user)[0]
ynerant's avatar
ynerant committed
        return context

me5na7qbjqbrp's avatar
me5na7qbjqbrp committed

me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
# ******************************* #
#              CLUB               #
# ******************************* #
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed

class ClubCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
    """
    Create Club
    """
    model = Club
    form_class = ClubForm
    success_url = reverse_lazy('member:club_list')
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    def form_valid(self, form):
        return super().form_valid(form)
ynerant's avatar
ynerant committed

me5na7qbjqbrp's avatar
me5na7qbjqbrp committed

class ClubListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
    """
    model = Club
    table_class = ClubTable
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed

class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
    model = Club
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    context_object_name = "club"
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        club = context["club"]
        if PermissionBackend().has_perm(self.request.user, "member.change_club_membership_start", club):
            club.update_membership_dates()

        club_transactions = Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note))\
            .filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view")).order_by('-id')
        context['history_list'] = HistoryTable(club_transactions)
        club_member = Membership.objects.filter(
            club=club,
            date_start__lte=datetime.now().date(),
            date_end__gte=datetime.now().date(),
        ).filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view")).all()
ynerant's avatar
ynerant committed

        context['member_list'] = MembershipTable(data=club_member)

        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()\
            .has_perm(self.request.user, "member.add_membership", empty_membership)

ynerant's avatar
ynerant committed

class ClubAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
    model = Club
    template_name = 'member/club_alias.html'
    context_object_name = 'club'
ynerant's avatar
ynerant committed

    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(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
    model = Club
    context_object_name = "club"
    form_class = ClubForm
    template_name = "member/club_form.html"

    def get_success_url(self):
        return reverse_lazy("member:club_detail", kwargs={"pk": self.object.pk})


class ClubPictureUpdateView(PictureUpdateView):
    model = Club
    template_name = 'member/club_picture_update.html'
    context_object_name = 'club'

    def get_success_url(self):
        return reverse_lazy('member:club_detail', kwargs={'pk': self.object.id})

me5na7qbjqbrp's avatar
me5na7qbjqbrp committed

class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
    model = Membership
    form_class = MembershipForm
    template_name = 'member/add_members.html'
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed

    def get_context_data(self, **kwargs):
        club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\
            .get(pk=self.kwargs["pk"])
        context = super().get_context_data(**kwargs)
        context['club'] = club
    def form_valid(self, form):
        club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\
            .get(pk=self.kwargs["pk"])
        form.instance.club = club
ynerant's avatar
ynerant committed

        if club.parent_club is not None:
            if not Membership.objects.filter(user=form.instance.user, club=club.parent_club).exists():
                form.add_error('user', _('User is not a member of the parent club') + ' ' + club.parent_club.name)
                return super().form_invalid(form)

        if Membership.objects.filter(
                user=form.instance.user,
                club=club,
                date_start__lte=datetime.now().date(),
                date_end__gte=datetime.now().date(),
        ).exists():
            form.add_error('user', _('User is already a member of the club'))
            return super().form_invalid(form)

        if form.instance.club.membership_start and form.instance.date_start < form.instance.club.membership_start:
ynerant's avatar
ynerant committed
            form.add_error('user', _("The membership must start after {:%m-%d-%Y}.")
                           .format(form.instance.club.membership_start))
            return super().form_invalid(form)

        if form.instance.club.membership_end and form.instance.date_start > form.instance.club.membership_end:
            form.add_error('user', _("The membership must begin before {:%m-%d-%Y}.")
ynerant's avatar
ynerant committed
                           .format(form.instance.club.membership_start))
            return super().form_invalid(form)

        return super().form_valid(form)

    def get_success_url(self):
        return reverse_lazy('member:club_detail', kwargs={'pk': self.object.club.id})


class ClubManageRolesView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
    model = Membership
    form_class = MembershipForm
    template_name = 'member/add_members.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        club = self.object.club
        context['club'] = club
        form = context['form']
        form.fields['user'].disabled = True
        form.fields['date_start'].widget = HiddenInput()

        return context

    def form_valid(self, form):
        if form.instance.club.membership_start and form.instance.date_start < form.instance.club.membership_start:
ynerant's avatar
ynerant committed
            form.add_error('user', _("The membership must start after {:%m-%d-%Y}.")
                           .format(form.instance.club.membership_start))
            return super().form_invalid(form)

        if form.instance.club.membership_end and form.instance.date_start > form.instance.club.membership_end:
            form.add_error('user', _("The membership must begin before {:%m-%d-%Y}.")
ynerant's avatar
ynerant committed
                           .format(form.instance.club.membership_start))
            return super().form_invalid(form)

        return super().form_valid(form)

    def get_success_url(self):
        return reverse_lazy('member:club_detail', kwargs={'pk': self.object.club.id})