From efc749c695193d9704216e3113156902555a79cd Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 17 Feb 2022 23:30:55 +1100 Subject: [PATCH 1/3] When uploading a report template, keep the existing filename (if it is the same report!) --- InvenTree/report/models.py | 45 ++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/InvenTree/report/models.py b/InvenTree/report/models.py index be8d803edf..c2766ec959 100644 --- a/InvenTree/report/models.py +++ b/InvenTree/report/models.py @@ -43,32 +43,12 @@ except OSError as err: # pragma: no cover logger = logging.getLogger("inventree") -class ReportFileUpload(FileSystemStorage): - """ - Custom implementation of FileSystemStorage class. - - When uploading a report (or a snippet / asset / etc), - it is often important to ensure the filename is not arbitrarily *changed*, - if the name of the uploaded file is identical to the currently stored file. - - For example, a snippet or asset file is referenced in a template by filename, - and we do not want that filename to change when we upload a new *version* - of the snippet or asset file. - - This uploader class performs the following pseudo-code function: - - - If the model is *new*, proceed as normal - - If the model is being updated: - a) If the new filename is *different* from the existing filename, proceed as normal - b) If the new filename is *identical* to the existing filename, we want to overwrite the existing file - """ - - def get_available_name(self, name, max_length=None): - - return super().get_available_name(name, max_length) - - def rename_template(instance, filename): + """ + Helper function for 'renaming' uploaded report files. + Pass responsibility back to the calling class, + to ensure that files are uploaded to the correct directory. + """ return instance.rename_file(filename) @@ -155,7 +135,20 @@ class ReportBase(models.Model): filename = os.path.basename(filename) - return os.path.join('report', 'report_template', self.getSubdir(), filename) + path = os.path.join('report', 'report_template', self.getSubdir(), filename) + + # If the report file is the *same* filename as the one being uploaded, + # remove the original one from the media directory + if str(filename) == str(self.template): + + fullpath = os.path.join(settings.MEDIA_ROOT, path) + fullpath = os.path.abspath(fullpath) + + if os.path.exists(fullpath): + logger.info(f"Deleting existing report template: '{filename}'") + os.remove(fullpath) + + return path @property def extension(self): From 6aec40b9ba647ea1352113132e97d82d4f42073c Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 17 Feb 2022 23:46:43 +1100 Subject: [PATCH 2/3] Improved error messages when report templates (or snippets) are missing! --- InvenTree/report/api.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/InvenTree/report/api.py b/InvenTree/report/api.py index af88f4799f..710f0562b1 100644 --- a/InvenTree/report/api.py +++ b/InvenTree/report/api.py @@ -225,13 +225,15 @@ class ReportPrintMixin: outputs.append(report.render_as_string(request)) else: outputs.append(report.render(request)) - except TemplateDoesNotExist: - - filename = report.template - + except TemplateDoesNotExist as e: + + template = str(e) + if not template: + template = report.template + return Response( { - 'error': _(f"Template file '{filename}' is missing or does not exist"), + 'error': _(f"Template file '{template}' is missing or does not exist"), }, status=400, ) @@ -269,13 +271,16 @@ class ReportPrintMixin: else: pdf = outputs[0].get_document().write_pdf() - except TemplateDoesNotExist: + except TemplateDoesNotExist as e: - filename = report.template + template = str(e) + + if not template: + template = report.template return Response( { - 'error': _(f"Template file '{filename}' is missing or does not exist"), + 'error': _(f"Template file '{template}' is missing or does not exist"), }, status=400, ) From 7c64cb950c4f0bec5623f72552654f2b9d34baea Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 18 Feb 2022 00:01:49 +1100 Subject: [PATCH 3/3] Delete template files from cache as they are uploaded --- InvenTree/report/api.py | 3 +-- InvenTree/report/models.py | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/InvenTree/report/api.py b/InvenTree/report/api.py index 710f0562b1..c7d2b15e4d 100644 --- a/InvenTree/report/api.py +++ b/InvenTree/report/api.py @@ -226,11 +226,10 @@ class ReportPrintMixin: else: outputs.append(report.render(request)) except TemplateDoesNotExist as e: - template = str(e) if not template: template = report.template - + return Response( { 'error': _(f"Template file '{template}' is missing or does not exist"), diff --git a/InvenTree/report/models.py b/InvenTree/report/models.py index c2766ec959..3ee19bd5e6 100644 --- a/InvenTree/report/models.py +++ b/InvenTree/report/models.py @@ -14,12 +14,12 @@ import datetime from django.urls import reverse from django.db import models from django.conf import settings +from django.core.cache import cache from django.core.exceptions import ValidationError, FieldError from django.template.loader import render_to_string from django.template import Template, Context -from django.core.files.storage import FileSystemStorage from django.core.validators import FileExtensionValidator import build.models @@ -137,17 +137,20 @@ class ReportBase(models.Model): path = os.path.join('report', 'report_template', self.getSubdir(), filename) + fullpath = os.path.join(settings.MEDIA_ROOT, path) + fullpath = os.path.abspath(fullpath) + # If the report file is the *same* filename as the one being uploaded, # remove the original one from the media directory if str(filename) == str(self.template): - fullpath = os.path.join(settings.MEDIA_ROOT, path) - fullpath = os.path.abspath(fullpath) - if os.path.exists(fullpath): logger.info(f"Deleting existing report template: '{filename}'") os.remove(fullpath) + # Ensure that the cache is cleared for this template! + cache.delete(fullpath) + return path @property @@ -515,16 +518,20 @@ def rename_snippet(instance, filename): path = os.path.join('report', 'snippets', filename) + fullpath = os.path.join(settings.MEDIA_ROOT, path) + fullpath = os.path.abspath(fullpath) + # If the snippet file is the *same* filename as the one being uploaded, # delete the original one from the media directory if str(filename) == str(instance.snippet): - fullpath = os.path.join(settings.MEDIA_ROOT, path) - fullpath = os.path.abspath(fullpath) if os.path.exists(fullpath): logger.info(f"Deleting existing snippet file: '{filename}'") os.remove(fullpath) + # Ensure that the cache is deleted for this snippet + cache.delete(fullpath) + return path