Compare commits
1 Commits
main
...
wip/74797-
Author | SHA1 | Date |
---|---|---|
Thomas NOËL | 8d53545b72 |
|
@ -0,0 +1,28 @@
|
|||
# Generated by Django 2.2.26 on 2023-02-23 15:02
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
import passerelle.utils.models
|
||||
import passerelle.utils.templates
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pdf', '0002_resource_fill_form_file'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='resource',
|
||||
name='xfdf_template',
|
||||
field=models.FileField(
|
||||
blank=True,
|
||||
help_text='Django template, used to create a XFDF for fill-form, rendered with payload',
|
||||
null=True,
|
||||
upload_to=passerelle.utils.models.resource_file_upload_to,
|
||||
validators=[passerelle.utils.templates.validate_template],
|
||||
verbose_name='XFDF Template',
|
||||
),
|
||||
),
|
||||
]
|
|
@ -25,12 +25,14 @@ from django.conf import settings
|
|||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django.http.response import HttpResponse
|
||||
from django.template.base import VariableDoesNotExist
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from passerelle.base.models import BaseResource
|
||||
from passerelle.utils.api import endpoint
|
||||
from passerelle.utils.jsonresponse import APIError
|
||||
from passerelle.utils.models import resource_file_upload_to
|
||||
from passerelle.utils.templates import render_to_string, validate_template
|
||||
|
||||
PDF_FILE_OBJECT = {
|
||||
'type': 'object',
|
||||
|
@ -85,7 +87,7 @@ FILL_FORM_SCHEMA = {
|
|||
'title': '',
|
||||
'description': '',
|
||||
'type': 'object',
|
||||
'required': ['filename', 'fields'],
|
||||
'required': ['filename'],
|
||||
'unflatten': True,
|
||||
'properties': OrderedDict(
|
||||
{
|
||||
|
@ -94,7 +96,7 @@ FILL_FORM_SCHEMA = {
|
|||
'type': 'string',
|
||||
},
|
||||
'input-form': PDF_FILE_OBJECT,
|
||||
'fields': {
|
||||
'xfdf': {
|
||||
'description': _('hierarchical dictionary of fields'),
|
||||
'type': 'object',
|
||||
},
|
||||
|
@ -123,6 +125,14 @@ class Resource(BaseResource):
|
|||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
xfdf_template = models.FileField(
|
||||
_('XFDF Template'),
|
||||
upload_to=resource_file_upload_to,
|
||||
help_text=_('Django template, used to create a XFDF for fill-form, rendered with payload'),
|
||||
validators=[validate_template],
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('PDF')
|
||||
|
@ -191,17 +201,23 @@ class Resource(BaseResource):
|
|||
'request_body': {'schema': {'application/json': FILL_FORM_SCHEMA}},
|
||||
'input_example': {
|
||||
'filename': 'filled.pdf',
|
||||
'fields/Page1[0]/FirstName[0]': 'John',
|
||||
'fields/Page1[0]/LastName[0]': 'Doe',
|
||||
'fields/Page2[0]/Checkbox[0]': '0',
|
||||
'fields/Page2[0]/Checkbox[1]': '1',
|
||||
'xfdf/Page1[0]/FirstName[0]': 'John',
|
||||
'xfdf/Page1[0]/LastName[0]': 'Doe',
|
||||
'xfdf/Page2[0]/Checkbox[0]': '0',
|
||||
'xfdf/Page2[0]/Checkbox[1]': '1',
|
||||
},
|
||||
},
|
||||
)
|
||||
def fill_form(self, request, post_data):
|
||||
filename = post_data.pop('filename')
|
||||
fields = post_data.pop('fields')
|
||||
filename = post_data['filename']
|
||||
if 'xfdf' in post_data:
|
||||
fields = post_data.pop('xfdf')
|
||||
elif self.xfdf_template:
|
||||
fields = None
|
||||
else:
|
||||
raise APIError("missing 'xfdf' property (no XFDF template)", http_status=400)
|
||||
|
||||
if fields is not None:
|
||||
xfdf_root = ET.Element('xfdf')
|
||||
xfdf_root.attrib['xmlns'] = 'http://ns.adobe.com/xfdf/'
|
||||
xfdf_root.attrib['xml:space'] = 'preserve'
|
||||
|
@ -228,13 +244,25 @@ class Resource(BaseResource):
|
|||
elif self.fill_form_file:
|
||||
input_filename = self.fill_form_file.path
|
||||
else:
|
||||
raise APIError("missing or bad 'input-form' property", http_status=400)
|
||||
raise APIError(
|
||||
"missing or bad 'input-form' property (no default input file)", http_status=400
|
||||
)
|
||||
# create xfdf
|
||||
xfdf_filename = os.path.join(tmpdir, 'fields.xfdf')
|
||||
if fields is not None:
|
||||
xfdf_f.attrib['href'] = input_filename
|
||||
with open(xfdf_filename, mode='wb') as fd:
|
||||
ET.indent(xfdf_root)
|
||||
ET.ElementTree(xfdf_root).write(fd, encoding='UTF-8', xml_declaration=True)
|
||||
else:
|
||||
self.xfdf_template.seek(0)
|
||||
xfdf_template = self.xfdf_template.read().decode()
|
||||
try:
|
||||
xfdf_content = render_to_string(xfdf_template, post_data)
|
||||
except VariableDoesNotExist as exc:
|
||||
raise APIError("cannot render XFDF template: %s" % exc, http_status=400)
|
||||
with open(xfdf_filename, mode='w') as fd:
|
||||
fd.write(xfdf_content)
|
||||
|
||||
# call pdftk fill_form
|
||||
pdf_content = self.run_pdftk(args=[input_filename, 'fill_form', xfdf_filename])
|
||||
|
@ -254,5 +282,5 @@ class Resource(BaseResource):
|
|||
for line in dump.splitlines():
|
||||
unflatten_separated += '<br>%s' % line
|
||||
if line.startswith('FieldName: '):
|
||||
unflatten_separated += ' → <b>fields/%s</b>' % line[11:].replace('.', '/')
|
||||
unflatten_separated += ' → <b>xfdf/%s</b>' % line[11:].replace('.', '/')
|
||||
return unflatten_separated
|
||||
|
|
|
@ -154,7 +154,7 @@ def test_pdf_fill_form(mocked_check_output, app, pdf):
|
|||
|
||||
payload = {
|
||||
'filename': 'foo.pdf',
|
||||
'fields/fname': 'John',
|
||||
'xfdf/fname': 'John',
|
||||
'input-form': {'content': acroform_b64content},
|
||||
}
|
||||
mocked_check_output.side_effect = check_xml
|
||||
|
@ -176,7 +176,7 @@ def test_pdf_fill_form(mocked_check_output, app, pdf):
|
|||
pdf.save()
|
||||
payload = {
|
||||
'filename': 'bar.pdf',
|
||||
'fields/fname': 'John',
|
||||
'xfdf/fname': 'John',
|
||||
}
|
||||
mocked_check_output.reset_mock()
|
||||
resp = app.post_json(endpoint, params=payload, status=200)
|
||||
|
@ -196,7 +196,7 @@ def test_pdf_fill_form(mocked_check_output, app, pdf):
|
|||
# pdftk errors (faked)
|
||||
payload = {
|
||||
'filename': 'foo.pdf',
|
||||
'fields/fname': 'Bill',
|
||||
'xfdf/fname': 'Bill',
|
||||
'input-form': {'content': acroform_b64content},
|
||||
}
|
||||
mocked_check_output.reset_mock()
|
||||
|
@ -228,22 +228,22 @@ def test_pdf_fill_form(mocked_check_output, app, pdf):
|
|||
payload = {'filename': 'out.pdf'}
|
||||
resp = app.post_json(endpoint, params=payload, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_desc'] == "'fields' is a required property"
|
||||
assert resp.json['err_desc'] == "missing 'xfdf' property (no XFDF template)"
|
||||
|
||||
payload = {'filename': 'out.pdf', 'fields': 'not-a-dict'}
|
||||
payload = {'filename': 'out.pdf', 'xfdf': 'not-a-dict'}
|
||||
resp = app.post_json(endpoint, params=payload, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_desc'] == "fields: 'not-a-dict' is not of type 'object'"
|
||||
assert resp.json['err_desc'] == "xfdf: 'not-a-dict' is not of type 'object'"
|
||||
|
||||
pdf.fill_form_file = None # no default PDF form
|
||||
pdf.save()
|
||||
payload = {
|
||||
'filename': 'bar.pdf',
|
||||
'fields/fname': 'Alice',
|
||||
'xfdf/fname': 'Alice',
|
||||
}
|
||||
resp = app.post_json(endpoint, params=payload, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_desc'] == "missing or bad 'input-form' property"
|
||||
assert resp.json['err_desc'] == "missing or bad 'input-form' property (no default input file)"
|
||||
|
||||
resp = app.get(endpoint, status=405)
|
||||
|
||||
|
@ -255,7 +255,7 @@ def test_pdf_real_pdftk_fillform(admin_user, app, pdf, settings):
|
|||
endpoint = generic_endpoint_url('pdf', 'fill-form', slug=pdf.slug)
|
||||
payload = {
|
||||
'filename': 'filled.pdf',
|
||||
'fields/fname': 'ThisIsMyFirstName',
|
||||
'xfdf/fname': 'ThisIsMyFirstName',
|
||||
'input-form': {'content': acroform_b64content},
|
||||
}
|
||||
resp = app.post_json(endpoint, params=payload, status=200)
|
||||
|
@ -271,11 +271,11 @@ def test_pdf_real_pdftk_fillform(admin_user, app, pdf, settings):
|
|||
manage_url = reverse('view-connector', kwargs={'connector': 'pdf', 'slug': pdf.slug})
|
||||
resp = app.get(manage_url)
|
||||
assert 'panel-dumpfields' not in resp.text
|
||||
assert '<b>fields/fname</b>' not in resp.text
|
||||
assert '<b>xfdf/fname</b>' not in resp.text
|
||||
app = login(app)
|
||||
resp = app.get(manage_url)
|
||||
assert 'panel-dumpfields' in resp.text
|
||||
assert '<b>fields/fname</b>' in resp.text
|
||||
assert '<b>xfdf/fname</b>' in resp.text
|
||||
|
||||
|
||||
def test_pdf_validator(pdf):
|
||||
|
|
Loading…
Reference in New Issue