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