import os from corres2math.tokens import email_validation_token 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 magic import Magic from .forms import CoachRegistrationForm, PhotoAuthorizationForm, SignupForm, StudentRegistrationForm, UserForm from .models import StudentRegistration class SignupView(CreateView): model = User form_class = SignupForm template_name = "registration/signup.html" 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) 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) 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 = {"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 = {"title": _('Email validation email sent')} class UserResendValidationEmailView(LoginRequiredMixin, DetailView): """ Rensend the email validation link. """ model = User extra_context = {"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): def get_redirect_url(self, *args, **kwargs): return reverse_lazy("registration:user_detail", args=(self.request.user.pk,)) class UserDetailView(LoginRequiredMixin, DetailView): model = User context_object_name = "user_object" template_name = "registration/user_detail.html" def dispatch(self, request, *args, **kwargs): user = request.user if not user.registration.is_admin and user.pk != kwargs["pk"]: raise PermissionDenied return super().dispatch(request, *args, **kwargs) class UserUpdateView(LoginRequiredMixin, UpdateView): model = User form_class = UserForm template_name = "registration/update_user.html" def dispatch(self, request, *args, **kwargs): user = request.user if not user.registration.is_admin and user.pk != kwargs["pk"]: raise PermissionDenied return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) user = self.get_object() context["registration_form"] = user.registration.form_class(data=self.request.POST or None, instance=self.object.registration) 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) 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,)) class UserUploadPhotoAuthorizationView(LoginRequiredMixin, UpdateView): model = StudentRegistration form_class = PhotoAuthorizationForm template_name = "registration/upload_photo_authorization.html" def dispatch(self, request, *args, **kwargs): user = request.user if not user.registration.is_admin and user.registration.pk != kwargs["pk"]: raise PermissionDenied return super().dispatch(request, *args, **kwargs) @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,)) class PhotoAuthorizationView(LoginRequiredMixin, View): def get(self, request, *args, **kwargs): filename = kwargs["filename"] path = f"media/authorization/photo/{filename}" if not os.path.exists(path): raise Http404 student = StudentRegistration.objects.get(photo_authorization__endswith=filename) user = request.user if not user.registration.is_admin and user.pk != student.user.pk: raise PermissionDenied mime = Magic(mime=True) mime_type = mime.from_file(path) ext = mime_type.split("/")[1].replace("jpeg", "jpg") 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 UserImpersonateView(LoginRequiredMixin, RedirectView): def dispatch(self, request, *args, **kwargs): """ An administrator can log in through this page as someone else, and act as this other person. """ 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 redirect(request.path) 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", "/"))