diff --git a/locale/de_DE/LC_MESSAGES/django.po b/locale/de_DE/LC_MESSAGES/django.po index d59e12f6..fbac592e 100644 --- a/locale/de_DE/LC_MESSAGES/django.po +++ b/locale/de_DE/LC_MESSAGES/django.po @@ -4,15 +4,15 @@ msgstr "" "Project-Id-Version: Mafiasi\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-01-18 14:54+0100\n" -"PO-Revision-Date: 2023-01-18 14:58+0100\n" -"Last-Translator: Markus Neblung \n" +"PO-Revision-Date: 2023-07-27 20:41+0200\n" +"Last-Translator: Timon Engelke \n" "Language-Team: \n" "Language: de_DE\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 3.2.2\n" +"X-Generator: Poedit 3.0.1\n" #: mafiasi/base/middleware.py:15 msgid "" @@ -376,6 +376,18 @@ msgstr "PDF hochladen" msgid "Reminder: Memory minutes for \"%(coursename)s\"" msgstr "Erinnerung: Gedächtnisprotokoll für \"%(coursename)s\"" +#: mafiasi/gprot/templates/admin/gprot/gprot/change_form.html:33 +msgid "Block this Gprot" +msgstr "Dieses GProt blockieren" + +#: mafiasi/gprot/templates/admin/gprot/gprot/change_form.html:34 +msgid "" +"When a Gprot is blocked, a hash is saved that ensures that the same file " +"cannot be uploaded again." +msgstr "" +"Wenn das GProt blockiert ist, dann wird ein Hash gespeichert, der " +"verhindert, dass die gleiche Datei erneut hochgeladen wird." + #: mafiasi/gprot/templates/gprot/_action_list.html:6 msgid "View" msgstr "Anzeigen" @@ -987,7 +999,16 @@ msgstr "Es sind nur PDF-Dateien erlaubt." msgid "Please select a file to upload." msgstr "Bitte wähle eine Datei zum Hochladen aus." -#: mafiasi/gprot/views.py:285 +#: mafiasi/gprot/views.py:217 +msgid "" +"This file was blocked because it is an original exam. Please do not try to " +"upload it again because we will get problems with the university." +msgstr "" +"Der Upload wurde blockiert, weil es sich um eine Originalklausur handelt. " +"Bitte versuche nicht, sie nochmal hochzuladen, damit wir keine Probleme mit " +"der Universität bekommen." + +#: mafiasi/gprot/views.py:307 msgid "Only PNG, JPEG and GIF files are allowed." msgstr "Es sind nur Bilder im PNG-, JPEG- oder GIF-Format erlaubt." diff --git a/locale/en_US/LC_MESSAGES/django.po b/locale/en_US/LC_MESSAGES/django.po index 873f7ed5..3bd0860f 100644 --- a/locale/en_US/LC_MESSAGES/django.po +++ b/locale/en_US/LC_MESSAGES/django.po @@ -3,14 +3,14 @@ msgstr "" "Project-Id-Version: Mafiasi\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-01-18 14:47+0100\n" -"PO-Revision-Date: 2022-03-07 16:02+0100\n" -"Last-Translator: Markus Neblung <9neblung@informatik.uni-hamburg.de>\n" +"PO-Revision-Date: 2023-07-27 20:41+0200\n" +"Last-Translator: Timon Engelke \n" "Language-Team: \n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 2.4.2\n" +"X-Generator: Poedit 3.0.1\n" #: mafiasi/base/middleware.py:15 msgid "" @@ -371,6 +371,18 @@ msgstr "Upload PDF" msgid "Reminder: Memory minutes for \"%(coursename)s\"" msgstr "Reminder: Memory minutes for \"%(coursename)s\"" +#: mafiasi/gprot/templates/admin/gprot/gprot/change_form.html:33 +msgid "Block this Gprot" +msgstr "Block this Gprot" + +#: mafiasi/gprot/templates/admin/gprot/gprot/change_form.html:34 +msgid "" +"When a Gprot is blocked, a hash is saved that ensures that the same file " +"cannot be uploaded again." +msgstr "" +"When a Gprot is blocked, a hash is saved that ensures that the same file " +"cannot be uploaded again." + #: mafiasi/gprot/templates/gprot/_action_list.html:6 msgid "View" msgstr "View" @@ -987,7 +999,15 @@ msgstr "Only PDF files are allowed." msgid "Please select a file to upload." msgstr "Please select a file to upload." -#: mafiasi/gprot/views.py:285 +#: mafiasi/gprot/views.py:217 +msgid "" +"This file was blocked because it is an original exam. Please do not try to " +"upload it again because we will get problems with the university." +msgstr "" +"This file was blocked because it is an original exam. Please do not try to " +"upload it again because we will get problems with the university." + +#: mafiasi/gprot/views.py:307 msgid "Only PNG, JPEG and GIF files are allowed." msgstr "Only PNG, JPEG and GIF files are allowed." diff --git a/locale/fr_FR/LC_MESSAGES/django.po b/locale/fr_FR/LC_MESSAGES/django.po index 8bb84bd0..d6d162f8 100644 --- a/locale/fr_FR/LC_MESSAGES/django.po +++ b/locale/fr_FR/LC_MESSAGES/django.po @@ -3,15 +3,15 @@ msgstr "" "Project-Id-Version: Mafiasi\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-01-18 14:47+0100\n" -"PO-Revision-Date: 2022-03-07 16:02+0100\n" -"Last-Translator: Markus Neblung <9neblung@informatik.uni-hamburg.de>\n" +"PO-Revision-Date: 2023-07-27 20:48+0200\n" +"Last-Translator: Timon Engelke \n" "Language-Team: \n" "Language: fr_FR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 2.4.2\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Poedit 3.0.1\n" #: mafiasi/base/middleware.py:15 msgid "" @@ -380,6 +380,18 @@ msgstr "Télécharger le PDF" msgid "Reminder: Memory minutes for \"%(coursename)s\"" msgstr "Rappel: Mémoire minutes pour \"%(coursename)s\"" +#: mafiasi/gprot/templates/admin/gprot/gprot/change_form.html:33 +msgid "Block this Gprot" +msgstr "Bloquer cette mémoire" + +#: mafiasi/gprot/templates/admin/gprot/gprot/change_form.html:34 +msgid "" +"When a Gprot is blocked, a hash is saved that ensures that the same file " +"cannot be uploaded again." +msgstr "" +"Quand une mémoire est bloquée, un hachage est enregistré pour assurer que le " +"même fichier ne peut plus être téléversé." + #: mafiasi/gprot/templates/gprot/_action_list.html:6 msgid "View" msgstr "Vue" @@ -1006,7 +1018,16 @@ msgstr "Seuls les fichiers PDF sont autorisés." msgid "Please select a file to upload." msgstr "S'il vous plaît sélectionner un fichier à télécharger." -#: mafiasi/gprot/views.py:285 +#: mafiasi/gprot/views.py:217 +msgid "" +"This file was blocked because it is an original exam. Please do not try to " +"upload it again because we will get problems with the university." +msgstr "" +"Le fichier a été bloqué parce qu'il s'agit d'un examen original. Merce de ne " +"pas essayer pas de le téléverser à nouveau afin d'éviter des problèmes avec " +"l'université." + +#: mafiasi/gprot/views.py:307 msgid "Only PNG, JPEG and GIF files are allowed." msgstr "Seuls les fichiers PNG, JPEG et GIF sont autorisés." diff --git a/mafiasi/gprot/admin.py b/mafiasi/gprot/admin.py index 803ec89b..c069a5d6 100644 --- a/mafiasi/gprot/admin.py +++ b/mafiasi/gprot/admin.py @@ -1,7 +1,12 @@ -from django.contrib import admin +import hashlib + +from django.contrib import admin, messages +from django.shortcuts import get_object_or_404, redirect +from django.urls import path, reverse from mafiasi.gprot.models import ( Attachment, + BlockedGprots, Favorite, GProt, Label, @@ -18,6 +23,36 @@ class GProtAdmin(admin.ModelAdmin): list_filter = ("published", "is_pdf") search_fields = ("course__name",) + def get_urls(self): + urls = super().get_urls() + custom_urls = [ + path("/block/", self.admin_site.admin_view(self.block_view), name="gprot_block"), + ] + + return custom_urls + urls + + def block_view(self, request, object_id): + to_block = get_object_or_404(GProt, pk=object_id) + if not to_block.is_pdf: + messages.error(request, "Cannot block, not a PDF") + else: + to_block.published = False + to_block.owner = None + to_block.save() + pdf_filefield = to_block.content_pdf + if not pdf_filefield: + messages.error(request, "Cannot block, no file") + else: + pdf_filefield.file.open() + contents = pdf_filefield.file.read() + pdf_filefield.file.close() + pdf_filefield.delete() + hash = hashlib.sha256(contents).hexdigest() + BlockedGprots.objects.create(pdf_hash=hash, gprot_title=str(to_block), blocked_by=request.user) + messages.success(request, "Successfully blocked") + + return redirect(reverse("admin:gprot_gprot_change", args=(object_id,))) + admin.site.register(GProt, GProtAdmin) @@ -25,3 +60,4 @@ class GProtAdmin(admin.ModelAdmin): admin.site.register(Reminder) admin.site.register(Label) admin.site.register(Favorite) +admin.site.register(BlockedGprots) diff --git a/mafiasi/gprot/migrations/0006_blockedgprots.py b/mafiasi/gprot/migrations/0006_blockedgprots.py new file mode 100644 index 00000000..532d2a74 --- /dev/null +++ b/mafiasi/gprot/migrations/0006_blockedgprots.py @@ -0,0 +1,27 @@ +# Generated by Django 4.1 on 2023-07-27 17:43 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("gprot", "0005_alter_favorite_url"), + ] + + operations = [ + migrations.CreateModel( + name="BlockedGprots", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("pdf_hash", models.CharField(max_length=64)), + ("gprot_title", models.CharField(max_length=100)), + ( + "blocked_by", + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + ], + ), + ] diff --git a/mafiasi/gprot/models.py b/mafiasi/gprot/models.py index dcff4eb7..2e4a0b4e 100644 --- a/mafiasi/gprot/models.py +++ b/mafiasi/gprot/models.py @@ -60,6 +60,15 @@ def __str__(self): return "[{0}] {1}: {2}{3}".format(self.pk, self.exam_date, self.course, append) +class BlockedGprots(models.Model): + pdf_hash = models.CharField(max_length=64) + gprot_title = models.CharField(max_length=100) + blocked_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) + + def __str__(self): + return "BlockedGprot: {0}".format(self.gprot_title) + + class Attachment(models.Model): gprot = models.ForeignKey(GProt, on_delete=models.CASCADE) file = models.FileField(upload_to=make_attachment_filename) diff --git a/mafiasi/gprot/templates/admin/gprot/gprot/change_form.html b/mafiasi/gprot/templates/admin/gprot/gprot/change_form.html new file mode 100644 index 00000000..60fb762a --- /dev/null +++ b/mafiasi/gprot/templates/admin/gprot/gprot/change_form.html @@ -0,0 +1,37 @@ +{% extends "admin/change_form.html" %} +{% load i18n %} +{% block extrastyle %}{{ block.super }} + +{% endblock %} +{% block after_related_objects %} +
+{% translate 'Block this Gprot' %} +

{% blocktranslate %}When a Gprot is blocked, a hash is saved that ensures that the same file cannot be uploaded again.{% endblocktranslate %}

+
+
+{% endblock %} diff --git a/mafiasi/gprot/views.py b/mafiasi/gprot/views.py index 7ca9e705..be6217fe 100644 --- a/mafiasi/gprot/views.py +++ b/mafiasi/gprot/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import hashlib import json import time from datetime import date @@ -24,7 +25,14 @@ from raven.contrib.django.raven_compat.models import client from mafiasi.gprot.forms import GProtBasicForm, GProtCreateForm, GProtSearchForm -from mafiasi.gprot.models import Attachment, Favorite, GProt, Notification, Reminder +from mafiasi.gprot.models import ( + Attachment, + BlockedGprots, + Favorite, + GProt, + Notification, + Reminder, +) from mafiasi.gprot.sanitize import clean_html from mafiasi.teaching.forms import CourseForm, TeacherForm from mafiasi.teaching.models import Course, Teacher, insert_autocomplete_courses @@ -195,6 +203,7 @@ def edit_gprot(request, gprot_pk): error = _("Only files up to {0} MB are allowed.").format(settings.GPROT_PDF_MAX_SIZE) if magic.from_buffer(upload.read(1024), mime=True) != "application/pdf": error = _("Only PDF files are allowed.") + else: error = _("Please select a file to upload.") @@ -204,6 +213,16 @@ def edit_gprot(request, gprot_pk): gprot.content_pdf = upload gprot.save() _clean_pdf_metadata(gprot) + # we have to calculate the hash after cleaning the file + gprot.content_pdf.file.open() + pdf_content = gprot.content_pdf.file.read() + gprot.content_pdf.file.close() + hash = hashlib.sha256(pdf_content).hexdigest() + if BlockedGprots.objects.filter(pdf_hash=hash).exists(): + error = _( + "This file was blocked because it is an original exam. Please do not try to upload it again because we will get problems with the university." + ) + gprot.content_pdf.delete() else: content = request.POST.get("content", "")