From 93b0bff50bff64fedd3f86420a963cec51bcbb83 Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Thu, 30 Aug 2018 12:18:56 +0200 Subject: [PATCH] permit PDF as well as images for invoice uploads for expenses --- src/economy/forms.py | 38 ++++++++++++++++++ .../includes/expense_detail_panel.html | 2 +- src/economy/views.py | 9 ++++- src/static_src/img/pdf.png | Bin 0 -> 22387 bytes 4 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 src/economy/forms.py create mode 100644 src/static_src/img/pdf.png diff --git a/src/economy/forms.py b/src/economy/forms.py new file mode 100644 index 00000000..7ef2dcd9 --- /dev/null +++ b/src/economy/forms.py @@ -0,0 +1,38 @@ +import os, magic, copy + +from django import forms +from .models import Expense + + +class ExpenseCreateForm(forms.ModelForm): + """ + We have to define this form explicitly because we want our ImageField to accept PDF files as well as images, + and we cannot change the clean_* methods with an autogenerated form from inside views.py + """ + class Meta: + model = Expense + fields = ['description', 'amount', 'invoice', 'paid_by_bornhack', 'responsible_team'] + + invoice = forms.FileField() + + def clean_invoice(self): + # get the uploaded file from cleaned_data + uploaded_file = self.cleaned_data['invoice'] + # is this a valid image? + try: + # create an ImageField instance + im = forms.ImageField() + # now check if the file is a valid image + im.to_python(uploaded_file) + except forms.ValidationError: + # file is not a valid image, so check if it's a pdf + # do a deep copy so we dont mess with the file object we might be passing on + testfile = copy.deepcopy(uploaded_file) + # read the uploaded file into memory (the webserver limits uploads to a reasonable max size so this should be safe) + mimetype = magic.from_buffer(testfile.open().read(), mime=True) + if mimetype != "application/pdf": + raise forms.ValidationError("Only images and PDF files allowed") + + # this is either a valid image, or has mimetype application/pdf, all good + return uploaded_file + diff --git a/src/economy/templates/includes/expense_detail_panel.html b/src/economy/templates/includes/expense_detail_panel.html index 0c4f8d5e..08c42deb 100644 --- a/src/economy/templates/includes/expense_detail_panel.html +++ b/src/economy/templates/includes/expense_detail_panel.html @@ -31,7 +31,7 @@ Invoice -
+
Filename: {{ expense.invoice_filename }} diff --git a/src/economy/views.py b/src/economy/views.py index 6ee75494..affdec6d 100644 --- a/src/economy/views.py +++ b/src/economy/views.py @@ -1,4 +1,4 @@ -import magic +import os, magic from django.shortcuts import render from django.conf import settings @@ -15,6 +15,7 @@ from utils.mixins import RaisePermissionRequiredMixin from teams.models import Team from .models import Expense, Reimbursement from .mixins import ExpensePermissionMixin, ReimbursementPermissionMixin +from .forms import ExpenseCreateForm class ExpenseListView(LoginRequiredMixin, CampViewMixin, ListView): @@ -41,9 +42,9 @@ class ExpenseDetailView(CampViewMixin, ExpensePermissionMixin, DetailView): class ExpenseCreateView(CampViewMixin, RaisePermissionRequiredMixin, CreateView): model = Expense - fields = ['description', 'amount', 'invoice', 'paid_by_bornhack', 'responsible_team'] template_name = 'expense_form.html' permission_required = ("camps.expense_create_permission") + form_class = ExpenseCreateForm def get_context_data(self, **kwargs): """ @@ -92,6 +93,10 @@ class ExpenseInvoiceView(CampViewMixin, ExpensePermissionMixin, DetailView): invoicedata = expense.invoice.read() # find mimetype mimetype = magic.from_buffer(invoicedata, mime=True) + # check if we have a PDF, no preview if so, load a pdf icon instead + if mimetype=="application/pdf" and 'preview' in request.GET: + invoicedata = open(os.path.join(settings.DJANGO_BASE_PATH, "static_src/img/pdf.png"), "rb").read() + mimetype = magic.from_buffer(invoicedata, mime=True) # put the response together and return it response = HttpResponse(content_type=mimetype) response.write(invoicedata) diff --git a/src/static_src/img/pdf.png b/src/static_src/img/pdf.png new file mode 100644 index 0000000000000000000000000000000000000000..0744dd077d89e055da6ef72f5eb4d655c1bc191a GIT binary patch literal 22387 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 OI~oApXVt22!~YBIt4r+w literal 0 HcmV?d00001