From cd62332896bb477178ba1c8068241fa76ca398ee Mon Sep 17 00:00:00 2001 From: Jonathan Weth <git@jonathanweth.de> Date: Thu, 20 May 2021 17:50:06 +0200 Subject: [PATCH] Save HTML file for PDF generation in media storage --- .../migrations/0014_alter_pdffile_file.py | 6 ++-- .../core/migrations/0018_pdffile_html_file.py | 31 +++++++++++++++++++ aleksis/core/models.py | 29 +++-------------- aleksis/core/urls.py | 1 - aleksis/core/util/pdf.py | 7 +++-- aleksis/core/views.py | 12 ------- 6 files changed, 45 insertions(+), 41 deletions(-) create mode 100644 aleksis/core/migrations/0018_pdffile_html_file.py diff --git a/aleksis/core/migrations/0014_alter_pdffile_file.py b/aleksis/core/migrations/0014_alter_pdffile_file.py index 266848894..b2cc3beb5 100644 --- a/aleksis/core/migrations/0014_alter_pdffile_file.py +++ b/aleksis/core/migrations/0014_alter_pdffile_file.py @@ -1,8 +1,10 @@ # Generated by Django 3.2 on 2021-04-17 18:47 -import aleksis.core.models from django.db import migrations, models +def _get_upload_path(instance, filename): # noqa + return f"pdfs/{instance.secret}.pdf" + class Migration(migrations.Migration): @@ -14,6 +16,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='pdffile', name='file', - field=models.FileField(blank=True, null=True, upload_to=aleksis.core.models.PDFFile._get_upload_path, verbose_name='Generated PDF file'), + field=models.FileField(blank=True, null=True, upload_to=_get_upload_path, verbose_name='Generated PDF file'), ), ] diff --git a/aleksis/core/migrations/0018_pdffile_html_file.py b/aleksis/core/migrations/0018_pdffile_html_file.py new file mode 100644 index 000000000..d802526de --- /dev/null +++ b/aleksis/core/migrations/0018_pdffile_html_file.py @@ -0,0 +1,31 @@ +# Generated by Django 3.2.3 on 2021-05-20 14:25 + +from django.db import migrations, models +from django.core.files.base import ContentFile + +def _get_default(): + return ContentFile("") + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0017_dashboardwidget_broken'), + ] + + operations = [ + migrations.RemoveField( + model_name='pdffile', + name='html', + ), + migrations.AddField( + model_name='pdffile', + name='html_file', + field=models.FileField(default=_get_default, upload_to='pdfs/', verbose_name='Generated HTML file'), + preserve_default=False, + ), + migrations.AlterField( + model_name='pdffile', + name='file', + field=models.FileField(blank=True, null=True, upload_to='pdfs/', verbose_name='Generated PDF file'), + ), + ] diff --git a/aleksis/core/models.py b/aleksis/core/models.py index ac862dc64..bd69c5ce0 100644 --- a/aleksis/core/models.py +++ b/aleksis/core/models.py @@ -24,6 +24,7 @@ from django.utils.text import slugify from django.utils.translation import gettext_lazy as _ import jsonstore +from cachalot.api import cachalot_disabled from cache_memoize import cache_memoize from django_celery_results.models import TaskResult from dynamic_preferences.models import PerInstancePreferenceModel @@ -1005,41 +1006,20 @@ class PDFFile(ExtensibleModel): def _get_default_expiration(): # noqa return timezone.now() + timedelta(minutes=get_site_preferences()["general__pdf_expiration"]) - def _get_upload_path(instance, filename): # noqa - return f"pdfs/{instance.secret}.pdf" - person = models.ForeignKey( to=Person, on_delete=models.CASCADE, verbose_name=_("Owner"), related_name="pdf_files" ) expires_at = models.DateTimeField( verbose_name=_("File expires at"), default=_get_default_expiration ) - html = models.TextField(verbose_name=_("Rendered HTML")) + html_file = models.FileField(upload_to="pdfs/", verbose_name=_("Generated HTML file")) file = models.FileField( - upload_to=_get_upload_path, blank=True, null=True, verbose_name=_("Generated PDF file") + upload_to="pdfs/", blank=True, null=True, verbose_name=_("Generated PDF file") ) def __str__(self): return f"{self.person} ({self.pk})" - @property - def secret(self) -> str: - """Get secret needed for accessing the HTML page.""" - return hmac.new( - bytes(settings.SECRET_KEY, "utf-8"), - msg=bytes(self.html + str(self.expires_at), "utf-8"), - digestmod="sha256", - ).hexdigest() - - @property - def html_url(self) -> str: - """Get URL for the HTML page.""" - return ( - urlparse(reverse("html_for_pdf_file", args=[self.pk])) - ._replace(query=f"secret={self.secret}") - .geturl() - ) - class Meta: verbose_name = _("PDF file") verbose_name_plural = _("PDF files") @@ -1057,7 +1037,8 @@ class TaskUserAssignment(ExtensibleModel): def create_for_task_id(cls, task_id: str, user: "User") -> "TaskUserAssignment": # Use get_or_create to ensure the TaskResult exists # django-celery-results will later add the missing information - result, __ = TaskResult.objects.get_or_create(task_id=task_id) + with cachalot_disabled(): + result, __ = TaskResult.objects.get_or_create(task_id=task_id) return cls.objects.create(task_result=result, user=user) class Meta: diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py index a357db0d6..9ce20f117 100644 --- a/aleksis/core/urls.py +++ b/aleksis/core/urls.py @@ -210,7 +210,6 @@ urlpatterns = [ name="edit_default_dashboard", ), path("pdfs/<int:pk>/", views.RedirectToPDFFile.as_view(), name="redirect_to_pdf_file"), - path("pdfs/<int:pk>/html/", views.HTMLForPDFFile.as_view(), name="html_for_pdf_file"), ] # Add URLs for optional features diff --git a/aleksis/core/util/pdf.py b/aleksis/core/util/pdf.py index 0ca5b5a3c..01733d724 100644 --- a/aleksis/core/util/pdf.py +++ b/aleksis/core/util/pdf.py @@ -4,6 +4,7 @@ from tempfile import TemporaryDirectory from typing import Optional from django.core.files import File +from django.core.files.base import ContentFile from django.http.request import HttpRequest from django.http.response import HttpResponse from django.shortcuts import get_object_or_404 @@ -73,8 +74,10 @@ def render_pdf(request: HttpRequest, template_name: str, context: dict = None) - html_template = render_to_string(template_name, context, request) - file_object = PDFFile.objects.create(person=request.user.person, html=html_template) - html_url = request.build_absolute_uri(file_object.html_url) + file_object = PDFFile.objects.create( + person=request.user.person, html_file=ContentFile(html_template, name="source.html") + ) + html_url = request.build_absolute_uri(file_object.html_file.url) result = generate_pdf.delay(file_object.pk, html_url, lang=get_language()) diff --git a/aleksis/core/views.py b/aleksis/core/views.py index d71013051..76abd15f7 100644 --- a/aleksis/core/views.py +++ b/aleksis/core/views.py @@ -1080,18 +1080,6 @@ class RedirectToPDFFile(SingleObjectMixin, View): return redirect(file_object.file.url) -class HTMLForPDFFile(SingleObjectMixin, View): - """Return rendered HTML for generating a PDF file.""" - - model = PDFFile - - def get(self, request, *args, **kwargs): - file_object = self.get_object() - if request.GET.get("secret") != file_object.secret: - raise PermissionDenied() - return HttpResponse(file_object.html) - - class CeleryProgressView(View): """Wrap celery-progress view to check permissions before.""" -- GitLab