plateforme-tfjm2/apps/registration/views.py

422 lines
15 KiB
Python
Raw Normal View History

2020-12-27 10:49:54 +00:00
# Copyright (C) 2020 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later
import os
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied, ValidationError
from django.db import transaction
from django.http import FileResponse, Http404
from django.shortcuts import redirect, resolve_url
from django.urls import reverse_lazy
from django.utils.http import urlsafe_base64_decode
from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, DetailView, RedirectView, TemplateView, UpdateView, View
from django_tables2 import SingleTableView
from magic import Magic
2021-01-12 16:56:40 +00:00
from participation.models import Solution, Synthesis
2020-12-28 18:19:01 +00:00
from tfjm.tokens import email_validation_token
2020-12-29 15:14:56 +00:00
from tfjm.views import AdminMixin, UserMixin
2020-12-27 10:49:54 +00:00
2020-12-29 15:14:56 +00:00
from .forms import CoachRegistrationForm, HealthSheetForm, ParentalAuthorizationForm, PhotoAuthorizationForm,\
SignupForm, StudentRegistrationForm, UserForm
from .models import Registration, StudentRegistration, ParticipantRegistration
2020-12-27 10:49:54 +00:00
from .tables import RegistrationTable
class SignupView(CreateView):
"""
Signup, as a participant or a coach.
"""
model = User
form_class = SignupForm
template_name = "registration/signup.html"
extra_context = dict(title=_("Sign up"))
def get_context_data(self, **kwargs):
context = super().get_context_data()
context["student_registration_form"] = StudentRegistrationForm(self.request.POST or None)
context["coach_registration_form"] = CoachRegistrationForm(self.request.POST or None)
del context["student_registration_form"].fields["team"]
del context["student_registration_form"].fields["email_confirmed"]
del context["coach_registration_form"].fields["team"]
del context["coach_registration_form"].fields["email_confirmed"]
return context
@transaction.atomic
def form_valid(self, form):
role = form.cleaned_data["role"]
if role == "participant":
registration_form = StudentRegistrationForm(self.request.POST)
else:
registration_form = CoachRegistrationForm(self.request.POST)
del registration_form.fields["team"]
del registration_form.fields["email_confirmed"]
if not registration_form.is_valid():
return self.form_invalid(form)
ret = super().form_valid(form)
registration = registration_form.instance
registration.user = form.instance
registration.save()
return ret
def get_success_url(self):
return reverse_lazy("registration:email_validation_sent")
class UserValidateView(TemplateView):
"""
A view to validate the email address.
"""
title = _("Email validation")
template_name = 'registration/email_validation_complete.html'
extra_context = dict(title=_("Validate email"))
def get(self, *args, **kwargs):
"""
With a given token and user id (in params), validate the email address.
"""
assert 'uidb64' in kwargs and 'token' in kwargs
self.validlink = False
user = self.get_user(kwargs['uidb64'])
token = kwargs['token']
# Validate the token
if user is not None and email_validation_token.check_token(user, token):
self.validlink = True
user.registration.email_confirmed = True
user.registration.save()
return self.render_to_response(self.get_context_data(), status=200 if self.validlink else 400)
def get_user(self, uidb64):
"""
Get user from the base64-encoded string.
"""
try:
# urlsafe_base64_decode() decodes to bytestring
uid = urlsafe_base64_decode(uidb64).decode()
user = User.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, User.DoesNotExist, ValidationError):
user = None
return user
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['user_object'] = self.get_user(self.kwargs["uidb64"])
context['login_url'] = resolve_url(settings.LOGIN_URL)
if self.validlink:
context['validlink'] = True
else:
context.update({
'title': _('Email validation unsuccessful'),
'validlink': False,
})
return context
class UserValidationEmailSentView(TemplateView):
"""
Display the information that the validation link has been sent.
"""
template_name = 'registration/email_validation_email_sent.html'
extra_context = dict(title=_('Email validation email sent'))
class UserResendValidationEmailView(LoginRequiredMixin, DetailView):
"""
Rensend the email validation link.
"""
model = User
extra_context = dict(title=_("Resend email validation link"))
def get(self, request, *args, **kwargs):
user = self.get_object()
user.registration.send_email_validation_link()
return redirect('registration:email_validation_sent')
class MyAccountDetailView(LoginRequiredMixin, RedirectView):
"""
Redirect to our own profile detail page.
"""
def get_redirect_url(self, *args, **kwargs):
return reverse_lazy("registration:user_detail", args=(self.request.user.pk,))
2020-12-29 15:14:56 +00:00
class UserDetailView(UserMixin, DetailView):
2020-12-27 10:49:54 +00:00
"""
Display the detail about a user.
"""
model = User
context_object_name = "user_object"
template_name = "registration/user_detail.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = _("Detail of user {user}").format(user=str(self.object.registration))
return context
class UserListView(AdminMixin, SingleTableView):
"""
Display the list of all registered users.
"""
model = Registration
table_class = RegistrationTable
template_name = "registration/user_list.html"
2020-12-29 15:14:56 +00:00
class UserUpdateView(UserMixin, UpdateView):
2020-12-27 10:49:54 +00:00
"""
Update the detail about a user and its registration.
"""
model = User
form_class = UserForm
template_name = "registration/update_user.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
user = self.get_object()
context["title"] = _("Update user {user}").format(user=str(self.object.registration))
context["registration_form"] = user.registration.form_class(data=self.request.POST or None,
instance=self.object.registration)
2020-12-28 20:42:01 +00:00
if not self.request.user.registration.is_admin:
2020-12-27 10:49:54 +00:00
if "team" in context["registration_form"].fields:
del context["registration_form"].fields["team"]
del context["registration_form"].fields["email_confirmed"]
return context
@transaction.atomic
def form_valid(self, form):
user = form.instance
registration_form = user.registration.form_class(data=self.request.POST or None,
instance=self.object.registration)
2020-12-28 20:42:01 +00:00
if not self.request.user.registration.is_admin:
2020-12-27 10:49:54 +00:00
if "team" in registration_form.fields:
del registration_form.fields["team"]
del registration_form.fields["email_confirmed"]
if not registration_form.is_valid():
return self.form_invalid(form)
registration_form.save()
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("registration:user_detail", args=(self.object.pk,))
2020-12-29 15:14:56 +00:00
class UserUploadPhotoAuthorizationView(UserMixin, UpdateView):
2020-12-27 10:49:54 +00:00
"""
A participant can send its photo authorization.
"""
model = StudentRegistration
form_class = PhotoAuthorizationForm
template_name = "registration/upload_photo_authorization.html"
extra_context = dict(title=_("Upload photo authorization"))
@transaction.atomic
def form_valid(self, form):
old_instance = StudentRegistration.objects.get(pk=self.object.pk)
if old_instance.photo_authorization:
old_instance.photo_authorization.delete()
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))
2020-12-29 15:14:56 +00:00
class UserUploadHealthSheetView(UserMixin, UpdateView):
"""
A participant can send its health sheet.
"""
model = StudentRegistration
form_class = HealthSheetForm
template_name = "registration/upload_health_sheet.html"
extra_context = dict(title=_("Upload health sheet"))
@transaction.atomic
def form_valid(self, form):
old_instance = StudentRegistration.objects.get(pk=self.object.pk)
if old_instance.health_sheet:
old_instance.health_sheet.delete()
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))
class UserUploadParentalAuthorizationView(UserMixin, UpdateView):
"""
A participant can send its parental authorization.
"""
model = StudentRegistration
form_class = ParentalAuthorizationForm
template_name = "registration/upload_parental_authorization.html"
extra_context = dict(title=_("Upload parental authorization"))
@transaction.atomic
def form_valid(self, form):
old_instance = StudentRegistration.objects.get(pk=self.object.pk)
if old_instance.parental_authorization:
old_instance.parental_authorization.delete()
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))
2020-12-27 10:49:54 +00:00
class PhotoAuthorizationView(LoginRequiredMixin, View):
"""
Display the sent photo authorization.
"""
def get(self, request, *args, **kwargs):
filename = kwargs["filename"]
path = f"media/authorization/photo/{filename}"
if not os.path.exists(path):
raise Http404
student = ParticipantRegistration.objects.get(photo_authorization__endswith=filename)
2020-12-27 10:49:54 +00:00
user = request.user
if not user.registration.is_admin and user.pk != student.user.pk:
raise PermissionDenied
# Guess mime type of the file
mime = Magic(mime=True)
mime_type = mime.from_file(path)
ext = mime_type.split("/")[1].replace("jpeg", "jpg")
# Replace file name
true_file_name = _("Photo authorization of {student}.{ext}").format(student=str(student), ext=ext)
return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
class HealthSheetView(LoginRequiredMixin, View):
"""
Display the sent health sheet.
"""
def get(self, request, *args, **kwargs):
filename = kwargs["filename"]
path = f"media/authorization/health/{filename}"
if not os.path.exists(path):
raise Http404
student = ParticipantRegistration.objects.get(health_sheet__endswith=filename)
user = request.user
if not user.registration.is_admin and user.pk != student.user.pk:
raise PermissionDenied
# Guess mime type of the file
mime = Magic(mime=True)
mime_type = mime.from_file(path)
ext = mime_type.split("/")[1].replace("jpeg", "jpg")
# Replace file name
true_file_name = _("Health sheet of {student}.{ext}").format(student=str(student), ext=ext)
return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
class ParentalAuthorizationView(LoginRequiredMixin, View):
"""
Display the sent parental authorization.
"""
def get(self, request, *args, **kwargs):
filename = kwargs["filename"]
path = f"media/authorization/parental/{filename}"
if not os.path.exists(path):
raise Http404
student = StudentRegistration.objects.get(parental_authorization__endswith=filename)
user = request.user
if not user.registration.is_admin and user.pk != student.user.pk:
raise PermissionDenied
# Guess mime type of the file
mime = Magic(mime=True)
mime_type = mime.from_file(path)
ext = mime_type.split("/")[1].replace("jpeg", "jpg")
# Replace file name
true_file_name = _("Parental authorization of {student}.{ext}").format(student=str(student), ext=ext)
return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
2021-01-12 16:56:40 +00:00
class SolutionView(LoginRequiredMixin, View):
"""
Display the sent solution.
"""
def get(self, request, *args, **kwargs):
filename = kwargs["filename"]
path = f"media/solutions/{filename}"
if not os.path.exists(path):
raise Http404
solution = Solution.objects.get(file__endswith=filename)
# user = request.user
# if False:
# FIXME Check ACL
# raise PermissionDenied
# Guess mime type of the file
mime = Magic(mime=True)
mime_type = mime.from_file(path)
ext = mime_type.split("/")[1].replace("jpeg", "jpg")
# Replace file name
true_file_name = str(solution) + f".{ext}"
return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
class SynthesisView(LoginRequiredMixin, View):
"""
Display the sent synthesis.
"""
def get(self, request, *args, **kwargs):
filename = kwargs["filename"]
path = f"media/syhntheses/{filename}"
if not os.path.exists(path):
raise Http404
solution = Synthesis.objects.get(file__endswith=filename)
# user = request.user
# if False:
# FIXME Check ACL
# raise PermissionDenied
# Guess mime type of the file
mime = Magic(mime=True)
mime_type = mime.from_file(path)
ext = mime_type.split("/")[1].replace("jpeg", "jpg")
# Replace file name
true_file_name = str(solution) + f".{ext}"
return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
2020-12-27 10:49:54 +00:00
class UserImpersonateView(LoginRequiredMixin, RedirectView):
"""
An administrator can log in through this page as someone else, and act as this other person.
"""
def dispatch(self, request, *args, **kwargs):
if self.request.user.registration.is_admin:
if not User.objects.filter(pk=kwargs["pk"]).exists():
raise Http404
session = request.session
session["admin"] = request.user.pk
session["_fake_user_id"] = kwargs["pk"]
return super().dispatch(request, *args, **kwargs)
def get_redirect_url(self, *args, **kwargs):
return reverse_lazy("registration:user_detail", args=(kwargs["pk"],))
class ResetAdminView(LoginRequiredMixin, View):
"""
Return to admin view, clear the session field that let an administrator to log in as someone else.
"""
def dispatch(self, request, *args, **kwargs):
user = request.user
if not user.is_authenticated:
return self.handle_no_permission()
if "_fake_user_id" in request.session:
del request.session["_fake_user_id"]
return redirect(request.GET.get("path", reverse_lazy("index")))