diff --git a/Dockerfile b/Dockerfile index 0cdc29a..712c69a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ ENV PYTHONUNBUFFERED 1 ENV DJANGO_ALLOW_ASYNC_UNSAFE 1 # Install LaTeX requirements -RUN apk add --no-cache gettext texlive nginx gcc libc-dev libffi-dev postgresql-dev +RUN apk add --no-cache gettext texlive nginx gcc libc-dev libffi-dev postgresql-dev libmagic RUN apk add --no-cache bash diff --git a/apps/registration/forms.py b/apps/registration/forms.py index 3282cd7..75a25e3 100644 --- a/apps/registration/forms.py +++ b/apps/registration/forms.py @@ -2,6 +2,7 @@ from django import forms from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.models import User from django.core.exceptions import ValidationError +from django.forms import FileInput from django.utils.translation import gettext_lazy as _ from .models import AdminRegistration, CoachRegistration, StudentRegistration @@ -52,6 +53,10 @@ class PhotoAuthorizationForm(forms.ModelForm): raise ValidationError(_("The uploaded file must be a PDF, PNG of JPEG file.")) return self.cleaned_data["photo_authorization"] + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields["photo_authorization"].widget = FileInput() + class Meta: model = StudentRegistration fields = ('photo_authorization',) diff --git a/apps/registration/models.py b/apps/registration/models.py index fa584df..e5fa3da 100644 --- a/apps/registration/models.py +++ b/apps/registration/models.py @@ -1,3 +1,5 @@ +from django.core.exceptions import ValidationError + from corres2math.tokens import email_validation_token from django.contrib.sites.models import Site from django.db import models @@ -68,7 +70,7 @@ class Registration(PolymorphicModel): def get_random_filename(instance, filename): - return get_random_string(64) + return "authorization/photo/" + get_random_string(64) class StudentRegistration(Registration): diff --git a/apps/registration/views.py b/apps/registration/views.py index 103c9c1..4877427 100644 --- a/apps/registration/views.py +++ b/apps/registration/views.py @@ -1,3 +1,10 @@ +import mimetypes +import os + +from django.http import Http404, HttpResponse, FileResponse +from django.views.generic.base import View +from magic import Magic + from corres2math.tokens import email_validation_token from django.conf import settings from django.contrib.auth.mixins import LoginRequiredMixin @@ -164,5 +171,26 @@ class UserUploadPhotoAuthorizationView(LoginRequiredMixin, UpdateView): form_class = PhotoAuthorizationForm template_name = "registration/upload_photo_authorization.html" + @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) + 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) diff --git a/corres2math/urls.py b/corres2math/urls.py index 0aca2ec..63efe76 100644 --- a/corres2math/urls.py +++ b/corres2math/urls.py @@ -18,8 +18,7 @@ from django.contrib import admin from django.urls import path, include from django.views.defaults import bad_request, permission_denied, page_not_found, server_error from django.views.generic import TemplateView - -from corres2math import settings +from registration.views import PhotoAuthorizationView urlpatterns = [ path('', TemplateView.as_view(template_name="index.html"), name='index'), @@ -32,12 +31,11 @@ urlpatterns = [ path('participation/', include('participation.urls')), path('registration/', include('registration.urls')), + path('media/authorization/photo//', PhotoAuthorizationView.as_view(), name='photo_authorization'), + path('', include('eastereggs.urls')), ] -# FIXME Protect files -urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) - handler400 = bad_request handler403 = permission_denied handler404 = page_not_found diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 6e404c8..959680f 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Corres2math\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-09-27 11:18+0200\n" +"POT-Creation-Date: 2020-09-27 12:35+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Yohann D'ANELLO \n" "Language-Team: LANGUAGE \n" @@ -38,7 +38,7 @@ msgstr "Fermer" msgid "Logs" msgstr "Logs" -#: apps/logs/models.py:22 apps/registration/models.py:16 +#: apps/logs/models.py:22 apps/registration/models.py:18 msgid "user" msgstr "utilisateur" @@ -141,7 +141,7 @@ msgid "Team {name} ({trigram})" msgstr "Équipe {name} ({trigram})" #: apps/participation/models.py:45 apps/participation/models.py:56 -#: apps/registration/models.py:81 apps/registration/models.py:127 +#: apps/registration/models.py:83 apps/registration/models.py:129 msgid "team" msgstr "équipe" @@ -302,104 +302,104 @@ msgstr "Vous n'êtes pas dans une équipe." msgid "You don't participate, so you don't have any team." msgstr "Vous ne participez pas, vous n'avez donc pas d'équipe." -#: apps/registration/forms.py:12 +#: apps/registration/forms.py:13 msgid "role" msgstr "rôle" -#: apps/registration/forms.py:14 +#: apps/registration/forms.py:15 msgid "participant" msgstr "participant" -#: apps/registration/forms.py:15 apps/registration/models.py:136 +#: apps/registration/forms.py:16 apps/registration/models.py:138 msgid "coach" msgstr "encadrant" -#: apps/registration/forms.py:52 +#: apps/registration/forms.py:53 msgid "The uploaded file must be a PDF, PNG of JPEG file." msgstr "Le fichier envoyé doit être au format PDF, PNG ou JPEG." -#: apps/registration/models.py:21 +#: apps/registration/models.py:23 msgid "Grant Animath to contact me in the future about other actions" msgstr "" "Autoriser Animath à me recontacter à l'avenir à propos d'autres actions" -#: apps/registration/models.py:26 +#: apps/registration/models.py:28 msgid "email confirmed" msgstr "email confirmé" -#: apps/registration/models.py:30 +#: apps/registration/models.py:32 msgid "Activate your Correspondances account" msgstr "Activez votre compte des Correspondances" -#: apps/registration/models.py:66 +#: apps/registration/models.py:68 msgid "registration" msgstr "inscription" -#: apps/registration/models.py:67 +#: apps/registration/models.py:69 msgid "registrations" msgstr "inscriptions" -#: apps/registration/models.py:86 +#: apps/registration/models.py:88 msgid "12th grade" msgstr "Terminale" -#: apps/registration/models.py:87 +#: apps/registration/models.py:89 msgid "11th grade" msgstr "Première" -#: apps/registration/models.py:88 +#: apps/registration/models.py:90 msgid "10th grade or lower" msgstr "Seconde ou inférieur" -#: apps/registration/models.py:90 +#: apps/registration/models.py:92 msgid "student class" msgstr "classe" -#: apps/registration/models.py:95 +#: apps/registration/models.py:97 msgid "school" msgstr "école" -#: apps/registration/models.py:100 +#: apps/registration/models.py:102 msgid "photo authorization" msgstr "autorisation de droit à l'image" -#: apps/registration/models.py:108 +#: apps/registration/models.py:110 msgid "student" msgstr "étudiant" -#: apps/registration/models.py:116 +#: apps/registration/models.py:118 msgid "student registration" msgstr "inscription d'élève" -#: apps/registration/models.py:117 +#: apps/registration/models.py:119 msgid "student registrations" msgstr "inscriptions d'élève" -#: apps/registration/models.py:131 +#: apps/registration/models.py:133 msgid "professional activity" msgstr "activité professionnelle" -#: apps/registration/models.py:144 +#: apps/registration/models.py:146 msgid "coach registration" msgstr "inscription d'encadrant" -#: apps/registration/models.py:145 +#: apps/registration/models.py:147 msgid "coach registrations" msgstr "inscriptions d'encadrants" -#: apps/registration/models.py:150 +#: apps/registration/models.py:152 msgid "role of the administrator" msgstr "rôle de l'administrateur" -#: apps/registration/models.py:155 +#: apps/registration/models.py:157 msgid "admin" msgstr "admin" -#: apps/registration/models.py:163 +#: apps/registration/models.py:165 msgid "admin registration" msgstr "inscription d'administrateur" -#: apps/registration/models.py:164 +#: apps/registration/models.py:166 msgid "admin registrations" msgstr "inscriptions d'administrateur" @@ -635,26 +635,31 @@ msgstr "Modifier l'utilisateur" msgid "Upload photo authorization" msgstr "Téléverser l'autorisation de droit à l'image" -#: apps/registration/views.py:56 +#: apps/registration/views.py:63 msgid "Email validation" msgstr "Validation de l'adresse mail" -#: apps/registration/views.py:58 +#: apps/registration/views.py:65 msgid "Validate email" msgstr "Valider l'adresse mail" -#: apps/registration/views.py:97 +#: apps/registration/views.py:104 msgid "Email validation unsuccessful" msgstr "Échec de la validation de l'adresse mail" -#: apps/registration/views.py:108 +#: apps/registration/views.py:115 msgid "Email validation email sent" msgstr "Mail de confirmation de l'adresse mail envoyé" -#: apps/registration/views.py:116 +#: apps/registration/views.py:123 msgid "Resend email validation link" msgstr "Renvoyé le lien de validation de l'adresse mail" +#: apps/registration/views.py:195 +#, python-brace-format +msgid "Photo authorization of {student}.{ext}" +msgstr "Autorisation de droit à l'image de {student}.{ext}" + #: corres2math/settings.py:150 msgid "English" msgstr "Anglais" diff --git a/requirements.txt b/requirements.txt index adbb301..8e2482a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,4 +9,5 @@ django-tables2 djangorestframework django-rest-polymorphic ptpython +python-magic gunicorn \ No newline at end of file