workflows: make it possible to replace odt images with variables (#16259)

This uses the "name" attribute of an image (in LibreOffice, double click
on the picture and go to the options tab to set it).  It must be set to
a Python expression, prefixed with = (e.g. =form_var_image_raw).

The expression should return a wcs-file-like object (must have a
get_content() method).  The replacement image mime type should be the
same as the original one.
This commit is contained in:
Frédéric Péters 2017-05-11 15:11:18 +02:00
parent 330e1788a7
commit 6ec13787dd
3 changed files with 76 additions and 5 deletions

Binary file not shown.

View File

@ -5,10 +5,12 @@ import StringIO
import time
import urllib2
import urlparse
import zipfile
import mock
from quixote import cleanup, get_response
from quixote.http_request import Upload as QuixoteUpload
from wcs.qommon.http_request import HTTPRequest
from qommon.form import *
@ -34,7 +36,7 @@ from wcs.wf.register_comment import RegisterCommenterWorkflowStatusItem
from wcs.wf.remove import RemoveWorkflowStatusItem
from wcs.wf.roles import AddRoleWorkflowStatusItem, RemoveRoleWorkflowStatusItem
from wcs.wf.wscall import WebserviceCallStatusItem
from wcs.wf.export_to_model import transform_to_pdf
from wcs.wf.export_to_model import ExportToModel, transform_to_pdf
from wcs.wf.geolocate import GeolocateWorkflowStatusItem
from wcs.wf.backoffice_fields import SetBackofficeFieldsWorkflowStatusItem
from wcs.wf.redirect_to_url import RedirectToUrlWorkflowStatusItem
@ -1892,6 +1894,42 @@ def test_transform_to_pdf():
assert outstream is not False
assert outstream.read(10).startswith('%PDF-')
def test_export_to_model_image(pub):
formdef = FormDef()
formdef.name = 'baz'
formdef.fields = [
FileField(id='3', label='File', type='file', varname='image'),
]
formdef.store()
upload = PicklableUpload('test.jpeg', 'image/jpeg')
image_data = open(os.path.join(os.path.dirname(__file__), 'image-with-gps-data.jpeg')).read()
upload.receive([image_data])
formdata = formdef.data_class()()
formdata.data = {'3': upload}
formdata.just_created()
formdata.store()
pub.substitutions.feed(formdata)
item = ExportToModel()
item.method = 'non-interactive'
template_filename = os.path.join(os.path.dirname(__file__), 'template-with-image.odt')
template = open(template_filename).read()
upload = QuixoteUpload('/foo/template.odt', content_type='application/octet-stream')
upload.fp = StringIO.StringIO()
upload.fp.write(template)
upload.fp.seek(0)
item.model_file = UploadedFile(pub.app_dir, None, upload)
item.attach_to_history = True
item.perform(formdata)
zfile = zipfile.ZipFile(formdata.evolution[-1].parts[0].filename, mode='r')
zinfo = zfile.getinfo('Pictures/10000000000000320000003276E9D46581B55C88.jpg')
# check the image has been replaced by the one from the formdata
assert zinfo.file_size == len(image_data)
def test_global_timeouts(pub):
FormDef.wipe()
Workflow.wipe()

View File

@ -45,9 +45,15 @@ from wcs.portfolio import has_portfolio, push_document
OO_TEXT_NS = 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'
OO_OFFICE_NS = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'
OO_DRAW_NS = 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'
XLINK_NS = 'http://www.w3.org/1999/xlink'
USER_FIELD_DECL = '{%s}user-field-decl' % OO_TEXT_NS
USER_FIELD_GET = '{%s}user-field-get' % OO_TEXT_NS
STRING_VALUE = '{%s}string-value' % OO_OFFICE_NS
DRAW_FRAME = '{%s}frame' % OO_DRAW_NS
DRAW_NAME = '{%s}name' % OO_DRAW_NS
DRAW_IMAGE = '{%s}image' % OO_DRAW_NS
XLINK_HREF = '{%s}href' % XLINK_NS
NAME = '{%s}name' % OO_TEXT_NS
try:
@ -82,13 +88,26 @@ def transform_opendocument(instream, outstream, process):
'''
zin = zipfile.ZipFile(instream, mode='r')
zout = zipfile.ZipFile(outstream, mode='w')
new_images = {}
assert 'content.xml' in zin.namelist()
for filename in zin.namelist():
# first pass to process meta.xml and content.xml
if filename not in ('meta.xml', 'content.xml'):
continue
content = zin.read(filename)
root = ET.fromstring(content)
process(root, new_images)
content = ET.tostring(root)
zout.writestr(filename, content)
for filename in zin.namelist():
# second pass to copy/replace other files
if filename in ('meta.xml', 'content.xml'):
root = ET.fromstring(content)
process(root)
content = ET.tostring(root)
continue
if filename in new_images:
content = new_images[filename].get_content()
else:
content = zin.read(filename)
zout.writestr(filename, content)
zout.close()
@ -385,7 +404,9 @@ class ExportToModel(WorkflowStatusItem):
'template: %s') % str(e))
def apply_od_template_to_formdata(self, formdata):
def process_root(root):
context_variables = get_publisher().substitutions.get_context_variables()
def process_root(root, new_images):
# cache for keeping computed user-field-decl value around
user_field_values = {}
@ -405,6 +426,18 @@ class ExportToModel(WorkflowStatusItem):
node.attrib[NAME] in user_field_values):
node.text = user_field_values[node.attrib[NAME]]
if node.tag == DRAW_FRAME:
name = node.attrib.get(DRAW_NAME)
if not name.startswith('='):
continue
# variable image
try:
variable_image = self.compute(name)
except:
continue
image = [x for x in node.getchildren() if x.tag == DRAW_IMAGE][0]
new_images[image.attrib.get(XLINK_HREF)] = variable_image
for attr in ('text', 'tail'):
if not getattr(node, attr):
continue