Project

General

Profile

0003-wf-export_to_model-add-opendocument-support-4292.patch

Benjamin Dauvergne, 14 October 2015 01:23 PM

Download (30.8 KB)

View differences:

Subject: [PATCH 3/3] wf/export_to_model: add opendocument support (#4292)

 tests/template-out.odt    | Bin 0 -> 31328 bytes
 tests/template.odt        | Bin 0 -> 9790 bytes
 tests/test_form_pages.py  |  90 ++++++++++++++++++++++++++++++++++++++++++++--
 wcs/wf/export_to_model.py |  85 +++++++++++++++++++++++++++++++++++++++----
 4 files changed, 165 insertions(+), 10 deletions(-)
 create mode 100644 tests/template-out.odt
 create mode 100644 tests/template.odt
tests/test_form_pages.py
3 3
import os
4 4
import re
5 5
import StringIO
6
import zipfile
6 7
from webtest import Upload
7 8

  
8 9
from quixote.http_request import Upload as QuixoteUpload
......
23 24

  
24 25
from utilities import get_app, login, create_temporary_pub, clean_temporary_pub, emails
25 26

  
27

  
28
def assert_equal_zip(stream1, stream2):
29
    z1 = zipfile.ZipFile(stream1)
30
    z2 = zipfile.ZipFile(stream2)
31
    assert set(z1.namelist()) == set(z2.namelist())
32
    for name in z1.namelist():
33
        assert z1.read(name) == z2.read(name)
34

  
35

  
26 36
def pytest_generate_tests(metafunc):
27 37
    if 'pub' in metafunc.fixturenames:
28 38
        metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True)
......
1135 1145
    st1 = wf.add_status('Status1', 'st1')
1136 1146
    export_to = ExportToModel()
1137 1147
    export_to.label = 'create doc'
1138
    upload = QuixoteUpload('/foo/test.rtf', content_type='text/rtf')
1148
    upload = QuixoteUpload('/foo/test.rtf', content_type='application/rtf')
1139 1149
    upload.fp = StringIO.StringIO()
1140 1150
    upload.fp.write('HELLO WORLD')
1141 1151
    upload.fp.seek(0)
......
1178 1188
    resp = resp.click('test.rtf')
1179 1189
    assert resp.location.endswith('/test.rtf')
1180 1190
    resp = resp.follow()
1181
    assert resp.content_type == 'text/rtf'
1191
    assert resp.content_type == 'application/rtf'
1182 1192
    assert resp.body == 'HELLO WORLD'
1183 1193

  
1184 1194
    # change file content, same name
1185
    upload = QuixoteUpload('/foo/test.rtf', content_type='text/rtf')
1195
    upload = QuixoteUpload('/foo/test.rtf', content_type='application/rtf')
1186 1196
    upload.fp = StringIO.StringIO()
1187 1197
    upload.fp.write('HELLO NEW WORLD')
1188 1198
    upload.fp.seek(0)
......
1197 1207
    assert resp.click('test.rtf', index=0).follow().body == 'HELLO WORLD'
1198 1208
    assert resp.click('test.rtf', index=1).follow().body == 'HELLO NEW WORLD'
1199 1209

  
1210
def test_formdata_generated_document_odt_download(pub):
1211
    create_user(pub)
1212
    wf = Workflow(name='status')
1213
    st1 = wf.add_status('Status1', 'st1')
1214
    export_to = ExportToModel()
1215
    export_to.label = 'create doc'
1216
    template_filename = os.path.join(os.path.dirname(__file__), 'template.odt')
1217
    template = open(template_filename).read()
1218
    upload = QuixoteUpload('/foo/template.odt', content_type='application/octet-stream')
1219
    upload.fp = StringIO.StringIO()
1220
    upload.fp.write(template)
1221
    upload.fp.seek(0)
1222
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
1223
    export_to.id = '_export_to'
1224
    export_to.by = ['_submitter']
1225
    st1.items.append(export_to)
1226
    export_to.parent = st1
1227
    wf.store()
1228

  
1229
    formdef = create_formdef()
1230
    formdef.workflow_id = wf.id
1231
    formdef.fields = []
1232
    formdef.store()
1233
    formdef.data_class().wipe()
1234

  
1235
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
1236
    resp = resp.forms[0].submit('submit')
1237
    assert 'Check values then click submit.' in resp.body
1238
    resp = resp.forms[0].submit('submit')
1239
    assert resp.status_int == 302
1240
    form_location = resp.location
1241
    resp = resp.follow()
1242
    assert 'The form has been recorded' in resp.body
1243

  
1244
    resp = resp.form.submit('button_export_to')
1245

  
1246
    resp = resp.follow() # $form/$id/create_doc
1247
    resp = resp.follow() # $form/$id/create_doc/
1248
    with open(os.path.join(os.path.dirname(__file__), 'template-out.odt')) as f:
1249
        assert_equal_zip(StringIO.StringIO(resp.body), f)
1250

  
1251
    export_to.attach_to_history = True
1252
    wf.store()
1253

  
1254
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
1255
    resp = resp.form.submit('button_export_to')
1256
    assert resp.location == form_location
1257
    resp = resp.follow() # back to form page
1258

  
1259
    resp = resp.click('template.odt')
1260
    assert resp.location.endswith('/template.odt')
1261
    resp = resp.follow()
1262
    assert resp.content_type == 'application/octet-stream'
1263
    with open(os.path.join(os.path.dirname(__file__), 'template-out.odt')) as f:
1264
        assert_equal_zip(StringIO.StringIO(resp.body), f)
1265

  
1266
    # change file content, same name
1267
    upload = QuixoteUpload('/foo/test.rtf', content_type='application/rtf')
1268
    upload.fp = StringIO.StringIO()
1269
    upload.fp.write('HELLO NEW WORLD')
1270
    upload.fp.seek(0)
1271
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
1272
    wf.store()
1273

  
1274
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
1275
    resp = resp.form.submit('button_export_to')
1276
    assert resp.location == form_location
1277
    resp = resp.follow() # back to form page
1278

  
1279
    with open(os.path.join(os.path.dirname(__file__), 'template-out.odt')) as f:
1280
        body = resp.click('template.odt', index=0).follow().body
1281
        assert_equal_zip(StringIO.StringIO(body), f)
1282
    assert resp.click('test.rtf', index=0).follow().body == 'HELLO NEW WORLD'
1283

  
1200 1284
def test_formdata_form_file_download(pub):
1201 1285
    create_user(pub)
1202 1286
    wf = Workflow(name='status')
wcs/wf/export_to_model.py
1 1
from StringIO import StringIO
2 2
from xml.etree import ElementTree as ET
3
import zipfile
3 4

  
4 5
from quixote import get_response, get_request, get_publisher
5 6
from quixote.directory import Directory
......
17 18
                           template_on_formdata, register_item_class)
18 19

  
19 20

  
21
def transform_opendocument(instream, outstream, process):
22
    '''Take a file-like object containing an ODT, ODS, or any open-office
23
       format, parse context.xml with element tree and apply process to its root
24
       node.
25
    '''
26
    zin = zipfile.ZipFile(instream, mode='r')
27
    zout = zipfile.ZipFile(outstream, mode='w')
28
    assert 'content.xml' in zin.namelist()
29
    for filename in zin.namelist():
30
        content = zin.read(filename)
31
        if filename == 'content.xml':
32
            root = ET.fromstring(content)
33
            process(root)
34
            content = ET.tostring(root)
35
        zout.writestr(filename, content)
36
    zout.close()
37

  
38

  
39
def is_opendocument(stream):
40
    try:
41
        with zipfile.ZipFile(stream) as z:
42
            return 'content.xml' in z.namelist()
43
    except zipfile.BadZipfile:
44
        return False
45

  
46

  
20 47
class TemplatingError(PublishError):
21 48
    def __init__(self, description):
22 49
        self.title = _('Templating Error')
......
135 162
            return base_url + self.get_directory_name()
136 163

  
137 164
    def model_file_validation(self, upload):
165
        if hasattr(upload, 'fp'):
166
            fp = upload.fp
167
        elif hasattr(upload, 'get_file'):
168
            fp = upload.get_file()
169
        else:
170
            raise Exception('unknown upload %r' % upload)
138 171
        if upload.content_type and upload.content_type == 'application/rtf':
139
            return True, ''
172
            return 'rtf', ''
140 173
        if (upload.content_type and upload.content_type == 'application/octet-stream') or \
141 174
                upload.content_type is None:
142 175
            if upload.base_filename and upload.base_filename.endswith('.rtf'):
143
                return True, ''
144
        upload.fp.seek(0)
145
        if upload.read(10).startswith('{\\rtf'):
146
            upload.fp.seek(0)
147
            return True, ''
148
        return False, _('Only RTF files can be used')
176
                return 'rtf', ''
177
        fp.seek(0)
178
        if fp.read(10).startswith('{\\rtf'):
179
            fp.seek(0)
180
            return 'rtf', ''
181
        if upload.content_type and upload.content_type.startswith('application/vnd.oasis.opendocument.'):
182
            return 'od', ''
183
        if (upload.content_type and upload.content_type == 'application/octet-stream') or \
184
                upload.content_type is None:
185
            if upload.base_filename and upload.base_filename.rsplit('.', 1) in ('odt', 'ods', 'odc', 'odb'):
186
                return 'od', ''
187
        fp.seek(0)
188
        if is_opendocument(fp):
189
            return 'od', ''
190
        return False, _('Only RTF and OpenDocument files can be used')
149 191

  
150 192
    def add_parameters_widgets(self, form, parameters, prefix='',
151 193
                               formdef=None):
......
206 248
    directory_name = property(get_directory_name)
207 249

  
208 250
    def apply_template_to_formdata(self, formdata):
251
        assert self.model_file
252
        kind, msg = self.model_file_validation(self.model_file)
253
        if kind == 'rtf':
254
            return self.apply_rtf_template_to_formdata(formdata)
255
        elif kind == 'od':
256
            return self.apply_od_template_to_formdata(formdata)
257
        else:
258
            raise Exception('unsupported model kind %r' % kind)
259

  
260
    def apply_rtf_template_to_formdata(self, formdata):
209 261
        process = None
210 262
        if self.model_file.base_filename.endswith('.rtf'):
211 263
            process = rtf_process
......
226 278
            raise TemplatingError(_('Unknown error in the '
227 279
                                    'template: %s') % str(e))
228 280

  
281

  
282
    def apply_od_template_to_formdata(self, formdata):
283
        def process_root(root):
284
            def process_text(t):
285
                if isinstance(t, unicode):
286
                    t = t.encode(get_publisher().site_charset)
287
                t = template_on_formdata(formdata, t)
288
                return unicode(t, get_publisher().site_charset)
289
            for node in root.iter():
290
                if node.text:
291
                    node.text = process_text(node.text)
292
                if node.tail:
293
                    node.tail = process_text(node.tail)
294
        outstream = StringIO()
295
        transform_opendocument(self.model_file.get_file(), outstream,
296
                               process_root)
297
        return outstream.getvalue()
298

  
299

  
229 300
    def get_parameters(self):
230 301
        return ('by', 'label', 'model_file', 'attach_to_history',
231 302
                'backoffice_info_text')
232
-