From e8f795ae23b234ad6d7db8dff9dabbfa27a6c548 Mon Sep 17 00:00:00 2001 From: Julian B Date: Mon, 23 Sep 2024 21:07:02 +0200 Subject: [PATCH] add CRUD views --- ephios/plugins/files/forms.py | 39 ++++++++++++++++++ .../plugins/files/migrations/0001_initial.py | 16 ++++++-- ephios/plugins/files/models.py | 6 ++- ephios/plugins/files/signals.py | 41 +++++++++++++++++++ .../templates/files/document_attachement.html | 5 +++ .../files/document_confirm_delete.html | 22 ++++++++++ .../files/templates/files/document_form.html | 19 +++++++++ .../files/templates/files/document_list.html | 33 +++++++++++++++ ephios/plugins/files/urls.py | 24 ++++++++++- ephios/plugins/files/views.py | 34 +++++++++++++++ 10 files changed, 233 insertions(+), 6 deletions(-) create mode 100644 ephios/plugins/files/forms.py create mode 100644 ephios/plugins/files/templates/files/document_attachement.html create mode 100644 ephios/plugins/files/templates/files/document_confirm_delete.html create mode 100644 ephios/plugins/files/templates/files/document_form.html create mode 100644 ephios/plugins/files/templates/files/document_list.html diff --git a/ephios/plugins/files/forms.py b/ephios/plugins/files/forms.py new file mode 100644 index 000000000..57b7daf63 --- /dev/null +++ b/ephios/plugins/files/forms.py @@ -0,0 +1,39 @@ +from django.forms import FileInput, Form, ModelForm, ModelMultipleChoiceField +from django.utils.translation import gettext as _ +from django_select2.forms import Select2MultipleWidget + +from ephios.core.forms.events import BasePluginFormMixin +from ephios.plugins.files.models import Document + + +class DocumentForm(ModelForm): + class Meta: + model = Document + fields = ["title", "file"] + widgets = {"file": FileInput(attrs={"accept": ".pdf"})} + + +class EventAttachedDocumentForm(BasePluginFormMixin, Form): + documents = ModelMultipleChoiceField( + queryset=Document.objects.all(), required=False, widget=Select2MultipleWidget + ) + + def __init__(self, *args, **kwargs): + kwargs.setdefault("prefix", "files") + self.event = kwargs.pop("event") + self.request = kwargs.pop("request") + super().__init__(*args, **kwargs) + self.fields["documents"].initial = ( + self.event.documents.all() if self.event and self.event.pk else [] + ) + + def save(self): + if self.cleaned_data["documents"]: + self.event.documents.set(self.cleaned_data["documents"]) + + @property + def heading(self): + return _("Attach files") + + def is_function_active(self): + return self.event and self.event.documents.exists() diff --git a/ephios/plugins/files/migrations/0001_initial.py b/ephios/plugins/files/migrations/0001_initial.py index cad424828..7fd793eb2 100644 --- a/ephios/plugins/files/migrations/0001_initial.py +++ b/ephios/plugins/files/migrations/0001_initial.py @@ -1,5 +1,6 @@ -# Generated by Django 5.0.8 on 2024-09-21 12:01 +# Generated by Django 5.0.8 on 2024-09-23 19:05 +import django.core.validators from django.db import migrations, models @@ -7,7 +8,9 @@ class Migration(migrations.Migration): initial = True - dependencies = [] + dependencies = [ + ("core", "0029_alter_userprofile_date_of_birth"), + ] operations = [ migrations.CreateModel( @@ -20,7 +23,14 @@ class Migration(migrations.Migration): ), ), ("title", models.CharField(max_length=255)), - ("file", models.FileField(upload_to="documents/")), + ( + "file", + models.FileField( + upload_to="documents/", + validators=[django.core.validators.FileExtensionValidator(["pdf"])], + ), + ), + ("attached_to", models.ManyToManyField(related_name="documents", to="core.event")), ], ), ] diff --git a/ephios/plugins/files/models.py b/ephios/plugins/files/models.py index e57f0aa0e..2e92ee914 100644 --- a/ephios/plugins/files/models.py +++ b/ephios/plugins/files/models.py @@ -1,9 +1,13 @@ +from django.core.validators import FileExtensionValidator from django.db import models +from ephios.core.models import Event + class Document(models.Model): title = models.CharField(max_length=255) - file = models.FileField(upload_to="documents/") + file = models.FileField(upload_to="documents/", validators=[FileExtensionValidator(["pdf"])]) + attached_to = models.ManyToManyField(Event, related_name="documents") def __str__(self): return self.title diff --git a/ephios/plugins/files/signals.py b/ephios/plugins/files/signals.py index e69de29bb..bc6a73746 100644 --- a/ephios/plugins/files/signals.py +++ b/ephios/plugins/files/signals.py @@ -0,0 +1,41 @@ +from django.dispatch import receiver +from django.template.loader import render_to_string +from django.urls import reverse +from django.utils.translation import gettext as _ + +from ephios.core.signals import event_forms, event_info, management_settings_sections +from ephios.plugins.files.forms import EventAttachedDocumentForm + + +@receiver( + management_settings_sections, + dispatch_uid="ephios.plugins.pages.signals.files_settings_section", +) +def files_settings_section(sender, request, **kwargs): + return ( + [ + { + "label": _("Files"), + "url": reverse("files:settings_document_list"), + "active": request.resolver_match.url_name.startswith("settings_document"), + }, + ] + if request.user.has_perm("files.add_document") + else [] + ) + + +@receiver( + event_forms, + dispatch_uid="ephios.plugins.files.signals.files_event_forms", +) +def guests_event_forms(sender, event, request, **kwargs): + return [EventAttachedDocumentForm(request.POST or None, event=event, request=request)] + + +@receiver(event_info, dispatch_uid="ephios.plugins.files.signals.event_info") +def display_event_files(event, request, **kwargs): + if event.documents.exists(): + return render_to_string( + "files/document_attachement.html", {"documents": event.documents.all()}, request + ) diff --git a/ephios/plugins/files/templates/files/document_attachement.html b/ephios/plugins/files/templates/files/document_attachement.html new file mode 100644 index 000000000..e72dcaa7f --- /dev/null +++ b/ephios/plugins/files/templates/files/document_attachement.html @@ -0,0 +1,5 @@ +
+ {% for document in documents %} + {{ document.title }} + {% endfor %} +
diff --git a/ephios/plugins/files/templates/files/document_confirm_delete.html b/ephios/plugins/files/templates/files/document_confirm_delete.html new file mode 100644 index 000000000..41f84a64d --- /dev/null +++ b/ephios/plugins/files/templates/files/document_confirm_delete.html @@ -0,0 +1,22 @@ +{% extends "core/settings/settings_base.html" %} +{% load i18n %} +{% load static %} + +{% block title %} + {% translate "Delete file" %} +{% endblock %} + +{% block settings_content %} + + +
{% csrf_token %} +

{% blocktranslate trimmed with title=document.title %} + Are you sure you want to delete the file "{{ title }}"? + {% endblocktranslate %}

+ {% translate "Back" %} + +
+ +{% endblock %} diff --git a/ephios/plugins/files/templates/files/document_form.html b/ephios/plugins/files/templates/files/document_form.html new file mode 100644 index 000000000..8a2faaf00 --- /dev/null +++ b/ephios/plugins/files/templates/files/document_form.html @@ -0,0 +1,19 @@ +{% extends "core/settings/settings_base.html" %} +{% load crispy_forms_filters %} +{% load i18n %} + +{% block settings_content %} + + +
+ {% csrf_token %} + {{ form|crispy }} + +
+{% endblock %} diff --git a/ephios/plugins/files/templates/files/document_list.html b/ephios/plugins/files/templates/files/document_list.html new file mode 100644 index 000000000..5a11ecd5c --- /dev/null +++ b/ephios/plugins/files/templates/files/document_list.html @@ -0,0 +1,33 @@ +{% extends "core/settings/settings_base.html" %} +{% load i18n %} + +{% block settings_content %} + {% translate "Add file" %} + + + + + + + + + {% for document in document_list %} + + + + + {% endfor %} + +
{% translate "Title" %}{% translate "Action" %}
{{ document.title }} + {% translate "Download" %} + {% translate "Edit" %} + {% translate "Delete" %} +
+{% endblock %} diff --git a/ephios/plugins/files/urls.py b/ephios/plugins/files/urls.py index c1e4ba375..3e961bdff 100644 --- a/ephios/plugins/files/urls.py +++ b/ephios/plugins/files/urls.py @@ -1,8 +1,28 @@ from django.urls import path -from ephios.plugins.files.views import DocumentView +from ephios.plugins.files.views import ( + DocumentCreateView, + DocumentDeleteView, + DocumentListView, + DocumentUpdateView, + DocumentView, +) app_name = "files" urlpatterns = [ - path("documents//", DocumentView.as_view(), name="document"), + path("document//", DocumentView.as_view(), name="document"), + path("settings/documents/", DocumentListView.as_view(), name="settings_document_list"), + path( + "settings/documents/create/", DocumentCreateView.as_view(), name="settings_document_create" + ), + path( + "settings/documents//edit/", + DocumentUpdateView.as_view(), + name="settings_document_edit", + ), + path( + "settings/documents//delete/", + DocumentDeleteView.as_view(), + name="settings_document_delete", + ), ] diff --git a/ephios/plugins/files/views.py b/ephios/plugins/files/views.py index 7aa3b41f1..b710dce9d 100644 --- a/ephios/plugins/files/views.py +++ b/ephios/plugins/files/views.py @@ -2,11 +2,17 @@ from urllib.parse import urlsplit from django.conf import settings +from django.contrib.messages.views import SuccessMessageMixin from django.http import FileResponse, HttpResponse from django.shortcuts import get_object_or_404, redirect +from django.urls import reverse_lazy +from django.utils.translation import gettext_lazy as _ from django.views import View +from django.views.generic import CreateView, DeleteView, ListView, UpdateView from guardian.mixins import LoginRequiredMixin +from ephios.extra.mixins import CustomPermissionRequiredMixin +from ephios.plugins.files.forms import DocumentForm from ephios.plugins.files.models import Document @@ -24,3 +30,31 @@ def get(self, request, *args, **kwargs): "attachment; filename=" + os.path.split(document.file.name)[1] ) return response + + +class DocumentListView(CustomPermissionRequiredMixin, ListView): + model = Document + permission_required = "files.add_document" + + +class DocumentCreateView(CustomPermissionRequiredMixin, SuccessMessageMixin, CreateView): + model = Document + permission_required = "files.add_document" + form_class = DocumentForm + success_url = reverse_lazy("files:settings_document_list") + success_message = _("File saved successfully.") + + +class DocumentUpdateView(CustomPermissionRequiredMixin, SuccessMessageMixin, UpdateView): + model = Document + permission_required = "files.change_document" + form_class = DocumentForm + success_url = reverse_lazy("files:settings_document_list") + success_message = _("File saved successfully.") + + +class DocumentDeleteView(CustomPermissionRequiredMixin, SuccessMessageMixin, DeleteView): + model = Document + permission_required = "files.delete_document" + success_url = reverse_lazy("files:settings_document_list") + success_message = _("File deleted successfully.")