Skip to content
Snippets Groups Projects
Commit cd623328 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Save HTML file for PDF generation in media storage

parent fb172b9f
No related branches found
No related tags found
1 merge request!617Resolve "Remove token-based filenames for generated PDF files"
Pipeline #12140 failed
# 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'),
),
]
# 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'),
),
]
......@@ -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:
......
......@@ -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
......
......@@ -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())
......
......@@ -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."""
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment