summaryrefslogtreecommitdiff
path: root/postgresqleu/digisign/backendviews.py
diff options
context:
space:
mode:
authorMagnus Hagander2023-06-02 15:37:42 +0000
committerMagnus Hagander2023-06-02 15:37:42 +0000
commit01214a785959878f6d7dd835347819dbaa888ac1 (patch)
tree38337993b0bb5f4ad1ecab8587bec1d2af0697b0 /postgresqleu/digisign/backendviews.py
parent5a8a7681352a19fa81a97ff01f0ad87b1c36231d (diff)
Add support for digital signature providers
This adds a new type of provider to the system for handling digital signatures. Initially the only consumer is conference sponsorships, but it could be added for other parts of the system as well in the future. Regular "old-style" sponsorship contracts are still supported, but will gain the feature to auto-fill sponsor name and VAT number if wanted. The sponsor signup workflow is adjusted to support either one or both of the two methods. Initially the only implementation is Signwell, but the system is made pluggable just like e.g. the payment providers, so other suppliers can be added in the future. This should be considered fairly beta at this point, as several parts of it cannot be fully tested until a production account is in place. But the basics are there...
Diffstat (limited to 'postgresqleu/digisign/backendviews.py')
-rw-r--r--postgresqleu/digisign/backendviews.py128
1 files changed, 128 insertions, 0 deletions
diff --git a/postgresqleu/digisign/backendviews.py b/postgresqleu/digisign/backendviews.py
new file mode 100644
index 00000000..c93885d3
--- /dev/null
+++ b/postgresqleu/digisign/backendviews.py
@@ -0,0 +1,128 @@
+from django.core.exceptions import PermissionDenied
+from django.http import HttpResponse
+from django.shortcuts import get_object_or_404, render
+
+import base64
+import io
+import json
+
+from postgresqleu.util.backendviews import backend_list_editor
+from postgresqleu.digisign.models import DigisignProvider, DigisignLog
+from postgresqleu.digisign.backendforms import BackendProviderForm
+from postgresqleu.digisign.util import digisign_providers
+from postgresqleu.digisign.pdfutil import fill_pdf_fields
+
+
+def edit_providers(request, rest):
+ if not request.user.is_superuser:
+ raise PermissionDenied("Access denied")
+
+ def _load_formclass(classname):
+ pieces = classname.split('.')
+ modname = '.'.join(pieces[:-1])
+ classname = pieces[-1]
+ mod = __import__(modname, fromlist=[classname, ])
+ if hasattr(getattr(mod, classname), 'backend_form_class'):
+ return getattr(mod, classname).backend_form_class
+ else:
+ return BackendProviderForm
+
+ u = rest and rest.rstrip('/') or rest
+ if u and u != '' and u.isdigit():
+ p = get_object_or_404(DigisignProvider, pk=u)
+ formclass = _load_formclass(p.classname)
+ elif u == 'new':
+ if '_newformdata' in request.POST or 'classname' in request.POST:
+ c = request.POST['_newformdata' if '_newformdata' in request.POST else 'classname']
+ if c not in digisign_providers:
+ raise PermissionDenied()
+
+ formclass = _load_formclass(c)
+ else:
+ formclass = BackendProviderForm
+ else:
+ formclass = BackendProviderForm
+
+ return backend_list_editor(request,
+ None,
+ formclass,
+ rest,
+ bypass_conference_filter=True,
+ topadmin='Digital signatures',
+ return_url='/admin/',
+ )
+
+
+def view_provider_log(request, providerid):
+ if not request.user.is_superuser:
+ raise PermissionDenied("Access denied")
+
+ provider = get_object_or_404(DigisignProvider, pk=providerid)
+
+ return render(request, 'digisign/digisign_backend_log.html', {
+ 'log': DigisignLog.objects.filter(provider=provider).order_by('-id')[:100],
+ 'breadcrumbs': [
+ ('/admin/digisign/providers/', 'Digital signature providers'),
+ ('/admin/digisign/providers/{}/'.format(provider.id), provider.name),
+ ]
+ })
+
+
+def pdf_field_editor(request, conference, pdf, available_fields, fielddata, savecallback=None, breadcrumbs=[]):
+ import fitz
+
+ if request.method == 'GET' and request.GET.get('current', '0') == '1':
+ return HttpResponse(
+ json.dumps(fielddata),
+ content_type='application/json',
+ status=200,
+ )
+ elif request.method == 'POST' and 'application/json' in request.META['CONTENT_TYPE']:
+ # Postback to save all fields
+ try:
+ postdata = json.loads(request.body.decode())
+ except json.decoder.JSONDecodeError:
+ return HttpResponse("Invalid json", status=400)
+
+ newdata = {
+ 'fields': [],
+ 'fontsize': int(postdata['fontsize']),
+ }
+ fieldnames = [fn for fn, fd in available_fields]
+ for f in postdata['fields']:
+ if f['field'] in fieldnames:
+ newdata['fields'].append({
+ 'field': f['field'],
+ 'page': int(f['page']),
+ 'x': int(f['x']),
+ 'y': int(f['y']),
+ })
+ else:
+ return HttpResponse('Invalid field {}'.format(f['field']), status=400)
+
+ newdata['fields'] = sorted(newdata['fields'], key=lambda f: f['page'])
+ savecallback(fielddata | newdata)
+ return HttpResponse(json.dumps({'status': 'OK'}), content_type="application/json", status=200)
+
+ # Or we render the base page
+
+ # This is inefficient as hell, but we hope not to have huge PDFs :) Turn the PDF into
+ # one PNG for each page.
+ pdf = fitz.open('pdf', bytes(pdf))
+ pages = []
+ pages = [(pagenum, base64.b64encode(page.getPixmap().getPNGData()).decode()) for pagenum, page in enumerate(pdf.pages())]
+
+ return render(request, 'digisign/pdf_field_editor.html', {
+ 'conference': conference,
+ 'breadcrumbs': breadcrumbs,
+ 'pages': pages,
+ 'fields': available_fields,
+ })
+
+
+def pdf_field_preview(request, conference, pdf, available_fields, fielddata):
+ pdf = fill_pdf_fields(pdf, available_fields, fielddata)
+
+ resp = HttpResponse(content_type='application/pdf')
+ resp.write(pdf)
+ return resp