Projet

Général

Profil

0001-misc-split-form_pages-tests.patch

Lauréline Guérin, 17 novembre 2020 14:26

Télécharger (102 ko)

Voir les différences:

Subject: [PATCH 1/2] misc: split form_pages tests

 tests/form_pages/test_all.py      | 1241 --------------------------
 tests/form_pages/test_block.py    |   17 +-
 tests/form_pages/test_formdata.py | 1358 +++++++++++++++++++++++++++++
 3 files changed, 1359 insertions(+), 1257 deletions(-)
 create mode 100644 tests/form_pages/test_formdata.py
tests/form_pages/test_all.py
3 3
import json
4 4
import pytest
5 5
import hashlib
6
import locale
7 6
import os
8 7
import re
9 8
import time
10 9
import zipfile
11
import base64
12 10
from webtest import Upload, Hidden
13 11
import mock
14 12
import xml.etree.ElementTree as ET
......
21 19
from django.utils.six import StringIO, BytesIO
22 20
from django.utils.six.moves.urllib import parse as urlparse
23 21

  
24
from quixote.http_request import Upload as QuixoteUpload
25 22
from django.utils.encoding import force_bytes, force_text
26 23

  
27 24
from wcs.qommon import force_str
28 25
from wcs.qommon.emails import docutils
29
from wcs.qommon.form import UploadedFile
30 26
from wcs.qommon.ident.password_accounts import PasswordAccount
31 27
from wcs.carddef import CardDef
32 28
from wcs.formdef import FormDef
......
36 32
    ChoiceWorkflowStatusItem, JumpOnSubmitWorkflowStatusItem,
37 33
    CommentableWorkflowStatusItem, WorkflowVariablesFieldsFormDef)
38 34
from wcs.wf.backoffice_fields import SetBackofficeFieldsWorkflowStatusItem
39
from wcs.wf.export_to_model import ExportToModel, transform_to_pdf
40 35
from wcs.wf.jump import JumpWorkflowStatusItem
41
from wcs.wf.attachment import AddAttachmentWorkflowStatusItem
42 36
from wcs.wf.form import FormWorkflowStatusItem, WorkflowFormFieldsFormDef
43 37
from wcs.wf.register_comment import RegisterCommenterWorkflowStatusItem
44 38
from wcs.wf.resubmit import ResubmitWorkflowStatusItem
......
48 42
from wcs.roles import Role, logged_users_role
49 43
from wcs.tracking_code import TrackingCode
50 44
from wcs.data_sources import NamedDataSource
51
from wcs.wscalls import NamedWsCall
52 45
from wcs import fields
53 46
from wcs.logged_errors import LoggedError
54 47
from wcs.forms.root import PublicFormStatusPage
......
3524 3517
    assert 'There were errors processing the form' in resp
3525 3518

  
3526 3519

  
3527
def test_formdata_attachment_download(pub):
3528
    create_user(pub)
3529
    wf = Workflow(name='status')
3530
    st1 = wf.add_status('Status1', 'st1')
3531
    attach = AddAttachmentWorkflowStatusItem()
3532
    attach.id = '_attach'
3533
    attach.by = ['_submitter']
3534
    st1.items.append(attach)
3535
    attach.parent = st1
3536
    wf.store()
3537

  
3538
    formdef = create_formdef()
3539
    formdef.workflow_id = wf.id
3540
    formdef.fields = []
3541
    formdef.store()
3542
    formdef.data_class().wipe()
3543

  
3544
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
3545
    resp = resp.forms[0].submit('submit')
3546
    assert 'Check values then click submit.' in resp.text
3547
    resp = resp.forms[0].submit('submit')
3548
    assert resp.status_int == 302
3549
    resp = resp.follow()
3550
    assert 'The form has been recorded' in resp.text
3551

  
3552
    resp.forms[0]['attachment_attach$file'] = Upload('test.txt', b'foobar', 'text/plain')
3553
    resp = resp.forms[0].submit('button_attach')
3554

  
3555
    assert formdef.data_class().count() == 1
3556
    formdata = formdef.data_class().select()[0]
3557
    assert formdata.evolution[-1].parts[0].__class__.__name__ == 'AttachmentEvolutionPart'
3558
    attachment = formdata.evolution[-1].parts[0]
3559
    assert attachment.content_type == 'text/plain'
3560
    assert attachment.orig_filename == 'test.txt'
3561

  
3562
    resp = resp.follow() # back to form page
3563
    resp = resp.click('test.txt')
3564
    assert resp.location.endswith('/test.txt')
3565
    resp = resp.follow()
3566
    assert resp.content_type == 'text/plain'
3567
    assert resp.text == 'foobar'
3568

  
3569

  
3570
def test_formdata_attachment_download_with_substitution_variable(pub):
3571
    create_user_and_admin(pub)
3572
    wf = Workflow(name='status')
3573
    st1 = wf.add_status('Status1', 'st1')
3574
    attach = AddAttachmentWorkflowStatusItem()
3575
    attach.varname = 'attached_doc'
3576
    attach.id = '_attach'
3577
    attach.by = ['_submitter']
3578
    st1.items.append(attach)
3579
    attach.parent = st1
3580
    wf.store()
3581

  
3582
    formdef = create_formdef()
3583
    formdef.workflow_id = wf.id
3584
    formdef.fields = []
3585
    formdef.store()
3586
    formdef.data_class().wipe()
3587

  
3588
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
3589
    resp = resp.forms[0].submit('submit')
3590
    assert 'Check values then click submit.' in resp.text
3591
    resp = resp.forms[0].submit('submit')
3592
    assert resp.status_int == 302
3593
    resp = resp.follow()
3594
    assert 'The form has been recorded' in resp.text
3595

  
3596
    resp.forms[0]['attachment_attach$file'] = Upload('test.txt', b'foobar', 'text/plain')
3597
    resp = resp.forms[0].submit('button_attach')
3598

  
3599
    assert formdef.data_class().count() == 1
3600
    formdata = formdef.data_class().select()[0]
3601
    assert formdata.evolution[-1].parts[0].__class__.__name__ == 'AttachmentEvolutionPart'
3602
    attachment = formdata.evolution[-1].parts[0]
3603
    assert attachment.content_type == 'text/plain'
3604
    assert attachment.orig_filename == 'test.txt'
3605

  
3606
    resp = resp.follow() # back to form page
3607
    resp = resp.click('test.txt')
3608
    assert resp.location.endswith('/test.txt')
3609
    resp = resp.follow()
3610
    assert resp.content_type == 'text/plain'
3611
    assert resp.text == 'foobar'
3612

  
3613
    variables = formdef.data_class().select()[0].get_substitution_variables()
3614
    assert 'attachments' in variables
3615
    attachments = variables['attachments']
3616
    assert attachments is not None
3617
    attachment_variable = attachments.attached_doc
3618

  
3619
    resp = login(get_app(pub), username='admin', password='admin').get(
3620
        attachment_variable.url).follow()
3621
    assert attachment_variable.content == resp.body
3622
    assert attachment_variable.b64_content == base64.b64encode(resp.body)
3623
    assert attachment_variable.content_type == resp._headers['content-type'].split(';')[0]
3624
    content_disposition = resp._headers['content-disposition']
3625
    assert len(content_disposition.split(';')) == 2
3626
    assert content_disposition.split(';')[0] == 'attachment'
3627
    assert resp.request.environ['PATH_INFO'].endswith(attachment_variable.filename)
3628

  
3629

  
3630
def test_formdata_attachment_download_to_backoffice_file_field(pub):
3631
    create_user(pub)
3632
    wf = Workflow(name='status')
3633
    wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf)
3634
    wf.backoffice_fields_formdef.fields = [
3635
        fields.FileField(id='bo1', label='bo field 1', type='file'),
3636
    ]
3637
    st1 = wf.add_status('Status1', 'st1')
3638
    attach = AddAttachmentWorkflowStatusItem()
3639
    attach.id = '_attach'
3640
    attach.by = ['_submitter']
3641
    attach.backoffice_filefield_id = 'bo1'
3642
    st1.items.append(attach)
3643
    attach.parent = st1
3644
    wf.store()
3645

  
3646
    assert attach.get_backoffice_filefield_options() == [('bo1', 'bo field 1', 'bo1')]
3647

  
3648
    formdef = create_formdef()
3649
    formdef.workflow_id = wf.id
3650
    formdef.fields = []
3651
    formdef.store()
3652
    formdef.data_class().wipe()
3653

  
3654
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
3655
    resp = resp.forms[0].submit('submit')
3656
    assert 'Check values then click submit.' in resp.text
3657
    resp = resp.forms[0].submit('submit')
3658
    assert resp.status_int == 302
3659
    resp = resp.follow()
3660
    assert 'The form has been recorded' in resp.text
3661

  
3662
    resp.forms[0]['attachment_attach$file'] = Upload('test.txt', b'foobar', 'text/plain')
3663
    resp = resp.forms[0].submit('button_attach')
3664

  
3665
    # backoffice file field is set
3666
    assert formdef.data_class().count() == 1
3667
    formdata = formdef.data_class().select()[0]
3668
    assert 'bo1' in formdata.data
3669
    bo1 = formdata.data['bo1']
3670
    assert bo1.base_filename == 'test.txt'
3671
    assert bo1.content_type == 'text/plain'
3672
    assert bo1.get_content() == b'foobar'
3673

  
3674
    # and file is in history, too
3675
    assert formdata.evolution[-1].parts[0].__class__.__name__ == 'AttachmentEvolutionPart'
3676
    attachment = formdata.evolution[-1].parts[0]
3677
    assert attachment.content_type == 'text/plain'
3678
    assert attachment.orig_filename == 'test.txt'
3679

  
3680

  
3681
def test_formdata_attachment_download_to_backoffice_file_field_only(pub):
3682
    create_user(pub)
3683
    wf = Workflow(name='status')
3684
    wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf)
3685
    wf.backoffice_fields_formdef.fields = [
3686
        fields.FileField(id='bo1', label='bo field 1', type='file'),
3687
    ]
3688
    st1 = wf.add_status('Status1', 'st1')
3689
    attach = AddAttachmentWorkflowStatusItem()
3690
    attach.id = '_attach'
3691
    attach.by = ['_submitter']
3692
    attach.backoffice_filefield_id = 'bo1'
3693
    attach.attach_to_history = False  # store only in backoffice field
3694
    st1.items.append(attach)
3695
    attach.parent = st1
3696
    wf.store()
3697

  
3698
    assert attach.get_backoffice_filefield_options() == [('bo1', 'bo field 1', 'bo1')]
3699

  
3700
    formdef = create_formdef()
3701
    formdef.workflow_id = wf.id
3702
    formdef.fields = []
3703
    formdef.store()
3704
    formdef.data_class().wipe()
3705

  
3706
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
3707
    resp = resp.forms[0].submit('submit')
3708
    assert 'Check values then click submit.' in resp.text
3709
    resp = resp.forms[0].submit('submit')
3710
    assert resp.status_int == 302
3711
    resp = resp.follow()
3712
    assert 'The form has been recorded' in resp.text
3713

  
3714
    resp.forms[0]['attachment_attach$file'] = Upload('test.txt', b'foobar', 'text/plain')
3715
    resp = resp.forms[0].submit('button_attach')
3716

  
3717
    # backoffice file field is set
3718
    assert formdef.data_class().count() == 1
3719
    formdata = formdef.data_class().select()[0]
3720
    assert 'bo1' in formdata.data
3721
    bo1 = formdata.data['bo1']
3722
    assert bo1.base_filename == 'test.txt'
3723
    assert bo1.content_type == 'text/plain'
3724
    assert bo1.get_content() == b'foobar'
3725

  
3726
    # but nothing in history
3727
    for evo in formdata.evolution:
3728
        assert not evo.parts
3729

  
3730

  
3731
def test_formdata_attachment_file_options(pub):
3732
    create_user(pub)
3733
    wf = Workflow(name='status')
3734
    st1 = wf.add_status('Status1', 'st1')
3735
    attach = AddAttachmentWorkflowStatusItem()
3736
    attach.id = '_attach'
3737
    attach.by = ['_submitter']
3738
    attach.document_type = {'label': 'Fichiers vidéo', 'mimetypes': ['video/*'], 'id': '_video'}
3739
    attach.max_file_size = '3Mo'
3740

  
3741
    st1.items.append(attach)
3742
    attach.parent = st1
3743
    wf.store()
3744

  
3745
    formdef = create_formdef()
3746
    formdef.workflow_id = wf.id
3747
    formdef.fields = []
3748
    formdef.store()
3749
    formdef.data_class().wipe()
3750

  
3751
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
3752
    resp = resp.forms[0].submit('submit')
3753
    assert 'Check values then click submit.' in resp.text
3754
    resp = resp.forms[0].submit('submit')
3755
    assert resp.status_int == 302
3756
    resp = resp.follow()
3757
    assert 'The form has been recorded' in resp.text
3758

  
3759
    file_input = resp.forms[0]['attachment_attach$file']
3760
    assert file_input.attrs['accept'] == 'video/*'
3761
    assert file_input.attrs['data-max-file-size'] == '3000000'
3762

  
3763

  
3764
def test_formdata_generated_document_download(pub):
3765
    create_user(pub)
3766
    wf = Workflow(name='status')
3767
    st1 = wf.add_status('Status1', 'st1')
3768
    export_to = ExportToModel()
3769
    export_to.convert_to_pdf = False
3770
    export_to.label = 'create doc'
3771
    upload = QuixoteUpload('/foo/test.rtf', content_type='application/rtf')
3772
    upload.fp = BytesIO()
3773
    upload.fp.write(b'HELLO WORLD')
3774
    upload.fp.seek(0)
3775
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
3776
    export_to.id = '_export_to'
3777
    export_to.by = ['_submitter']
3778
    st1.items.append(export_to)
3779
    export_to.parent = st1
3780
    wf.store()
3781

  
3782
    formdef = create_formdef()
3783
    formdef.workflow_id = wf.id
3784
    formdef.fields = []
3785
    formdef.store()
3786
    formdef.data_class().wipe()
3787

  
3788
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
3789
    resp = resp.forms[0].submit('submit')
3790
    assert 'Check values then click submit.' in resp.text
3791
    resp = resp.forms[0].submit('submit')
3792
    assert resp.status_int == 302
3793
    form_location = resp.location
3794
    resp = resp.follow()
3795
    assert 'The form has been recorded' in resp.text
3796

  
3797
    resp = resp.form.submit('button_export_to')
3798

  
3799
    resp = resp.follow() # $form/$id/create_doc
3800
    resp = resp.follow() # $form/$id/create_doc/
3801
    assert resp.text == 'HELLO WORLD'
3802

  
3803
    export_to.attach_to_history = True
3804
    wf.store()
3805

  
3806
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
3807
    resp = resp.form.submit('button_export_to')
3808
    assert resp.location == form_location + '#action-zone'
3809
    resp = resp.follow() # back to form page
3810

  
3811
    resp = resp.click('test.rtf')
3812
    assert resp.location.endswith('/test.rtf')
3813
    resp = resp.follow()
3814
    assert resp.content_type == 'application/rtf'
3815
    assert resp.text == 'HELLO WORLD'
3816

  
3817
    # change export model to now be a RTF file, do the action again on the same form and
3818
    # check that both the old .odt file and the new .rtf file are there and valid.
3819
    upload = QuixoteUpload('/foo/test.rtf', content_type='application/rtf')
3820
    upload.fp = BytesIO()
3821
    upload.fp.write(b'HELLO NEW WORLD')
3822
    upload.fp.seek(0)
3823
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
3824
    wf.store()
3825

  
3826
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
3827
    resp = resp.form.submit('button_export_to')
3828
    assert resp.location == form_location + '#action-zone'
3829
    resp = resp.follow() # back to form page
3830

  
3831
    assert resp.click('test.rtf', index=0).follow().text == 'HELLO WORLD'
3832
    assert resp.click('test.rtf', index=1).follow().text == 'HELLO NEW WORLD'
3833

  
3834
    # use substitution variables on rtf: only ezt format is accepted
3835
    upload = QuixoteUpload('/foo/test.rtf', content_type='application/rtf')
3836
    upload.fp = BytesIO()
3837
    upload.fp.write(b'HELLO {{DJANGO}} WORLD [form_name]')
3838
    upload.fp.seek(0)
3839
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
3840
    wf.store()
3841

  
3842
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
3843
    resp = resp.form.submit('button_export_to')
3844
    assert resp.location == form_location + '#action-zone'
3845
    resp = resp.follow()
3846

  
3847
    assert resp.click('test.rtf', index=2).follow().text == 'HELLO {{DJANGO}} WORLD {\\uc1{test}}'
3848

  
3849

  
3850
@pytest.fixture(params=['template.odt', 'template-django.odt'])
3851
def odt_template(request):
3852
    return request.param
3853

  
3854

  
3855
def test_formdata_generated_document_odt_download(pub, odt_template):
3856
    create_user(pub)
3857
    wf = Workflow(name='status')
3858
    st1 = wf.add_status('Status1', 'st1')
3859
    export_to = ExportToModel()
3860
    export_to.convert_to_pdf = False
3861
    export_to.label = 'create doc'
3862
    template_filename = os.path.join(os.path.dirname(__file__), '..', odt_template)
3863
    template = open(template_filename, 'rb').read()
3864
    upload = QuixoteUpload('/foo/' + odt_template, content_type='application/octet-stream')
3865
    upload.fp = BytesIO()
3866
    upload.fp.write(template)
3867
    upload.fp.seek(0)
3868
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
3869
    export_to.id = '_export_to'
3870
    export_to.by = ['_submitter']
3871
    st1.items.append(export_to)
3872
    export_to.parent = st1
3873
    wf.store()
3874

  
3875
    formdef = create_formdef()
3876
    formdef.workflow_id = wf.id
3877
    formdef.fields = [fields.TextField(id='0', label='comment', type='text', varname='comment')]
3878
    formdef.store()
3879
    formdef.data_class().wipe()
3880

  
3881
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
3882
    resp.form['f0'] = 'Hello\n\nWorld.'
3883
    resp = resp.forms[0].submit('submit')
3884
    assert 'Check values then click submit.' in resp.text
3885
    resp = resp.forms[0].submit('submit')
3886
    assert resp.status_int == 302
3887
    form_location = resp.location
3888
    resp = resp.follow()
3889
    assert 'The form has been recorded' in resp.text
3890

  
3891
    resp = resp.form.submit('button_export_to')
3892

  
3893
    resp = resp.follow() # $form/$id/create_doc
3894
    resp = resp.follow() # $form/$id/create_doc/
3895
    with open(os.path.join(os.path.dirname(__file__), '..', 'template-out.odt'), 'rb') as f:
3896
        assert_equal_zip(BytesIO(resp.body), f)
3897

  
3898
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
3899
    resp = resp.form.submit('button_export_to')
3900
    resp = resp.follow() # $form/$id/create_doc
3901
    with mock.patch('wcs.wf.export_to_model.get_formdata_template_context') as get_context_1:
3902
        with mock.patch('wcs.workflows.get_formdata_template_context') as get_context_never:
3903
            get_context_1.return_value = {}
3904
            get_context_never.return_value = {}
3905
            resp = resp.follow() # $form/$id/create_doc/
3906
            # substitution variables are computed only one :
3907
            assert get_context_1.call_count == 1
3908
            assert get_context_never.call_count == 0
3909

  
3910
    export_to.attach_to_history = True
3911
    wf.store()
3912

  
3913
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
3914
    resp = resp.form.submit('button_export_to')
3915
    assert resp.location == form_location + '#action-zone'
3916
    resp = resp.follow() # back to form page
3917

  
3918
    resp = resp.click(odt_template)
3919
    assert resp.location.endswith('/' + odt_template)
3920
    resp = resp.follow()
3921
    assert resp.content_type == 'application/octet-stream'
3922
    with open(os.path.join(os.path.dirname(__file__), '..', 'template-out.odt'), 'rb') as f:
3923
        assert_equal_zip(BytesIO(resp.body), f)
3924

  
3925
    # change file content, same name
3926
    upload = QuixoteUpload('/foo/test.rtf', content_type='application/rtf')
3927
    upload.fp = BytesIO()
3928
    upload.fp.write(b'HELLO NEW WORLD')
3929
    upload.fp.seek(0)
3930
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
3931
    wf.store()
3932

  
3933
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
3934
    resp = resp.form.submit('button_export_to')
3935
    assert resp.location == form_location + '#action-zone'
3936
    resp = resp.follow() # back to form page
3937

  
3938
    with open(os.path.join(os.path.dirname(__file__), '..', 'template-out.odt'), 'rb') as f:
3939
        body = resp.click(odt_template, index=0).follow().body
3940
        assert_equal_zip(BytesIO(body), f)
3941
    assert resp.click('test.rtf', index=0).follow().body == b'HELLO NEW WORLD'
3942

  
3943

  
3944
def test_formdata_generated_document_odt_download_with_substitution_variable(pub):
3945
    create_user_and_admin(pub)
3946
    wf = Workflow(name='status')
3947
    st1 = wf.add_status('Status1', 'st1')
3948
    export_to = ExportToModel()
3949
    export_to.convert_to_pdf = False
3950
    export_to.label = 'create doc'
3951
    export_to.varname = 'created_doc'
3952
    template_filename = os.path.join(os.path.dirname(__file__), '..', 'template.odt')
3953
    template = open(template_filename, 'rb').read()
3954
    upload = QuixoteUpload('/foo/template.odt', content_type='application/octet-stream')
3955
    upload.fp = BytesIO()
3956
    upload.fp.write(template)
3957
    upload.fp.seek(0)
3958
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
3959
    export_to.id = '_export_to'
3960
    export_to.by = ['_submitter']
3961
    st1.items.append(export_to)
3962
    export_to.parent = st1
3963
    wf.store()
3964

  
3965
    formdef = create_formdef()
3966
    formdef.workflow_id = wf.id
3967
    formdef.fields = [fields.TextField(id='0', label='comment', type='text', varname='comment')]
3968
    formdef.store()
3969
    formdef.data_class().wipe()
3970

  
3971
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
3972
    resp.form['f0'] = 'Hello\n\nWorld.'
3973
    resp = resp.forms[0].submit('submit')
3974
    assert 'Check values then click submit.' in resp.text
3975
    resp = resp.forms[0].submit('submit')
3976
    assert resp.status_int == 302
3977
    form_location = resp.location
3978
    resp = resp.follow()
3979
    assert 'The form has been recorded' in resp.text
3980

  
3981
    resp = resp.form.submit('button_export_to')
3982

  
3983
    resp = resp.follow() # $form/$id/create_doc
3984
    resp = resp.follow() # $form/$id/create_doc/
3985
    with open(os.path.join(os.path.dirname(__file__), '..', 'template-out.odt'), 'rb') as f:
3986
        assert_equal_zip(BytesIO(resp.body), f)
3987

  
3988
    export_to.attach_to_history = True
3989
    wf.store()
3990

  
3991
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
3992
    resp = resp.form.submit('button_export_to')
3993
    assert resp.location == form_location + '#action-zone'
3994
    resp = resp.follow() # back to form page
3995

  
3996
    resp = resp.click('template.odt')
3997
    assert resp.location.endswith('/template.odt')
3998
    response1 = resp = resp.follow()
3999
    assert resp.content_type == 'application/octet-stream'
4000
    with open(os.path.join(os.path.dirname(__file__), '..', 'template-out.odt'), 'rb') as f:
4001
        assert_equal_zip(BytesIO(resp.body), f)
4002

  
4003
    # change file content, same name
4004
    upload = QuixoteUpload('/foo/test.rtf', content_type='application/rtf')
4005
    upload.fp = BytesIO()
4006
    upload.fp.write(b'HELLO NEW WORLD')
4007
    upload.fp.seek(0)
4008
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
4009
    wf.store()
4010

  
4011
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
4012
    resp = resp.form.submit('button_export_to')
4013
    assert resp.location == form_location + '#action-zone'
4014
    resp = resp.follow() # back to form page
4015

  
4016
    with open(os.path.join(os.path.dirname(__file__), '..', 'template-out.odt'), 'rb') as f:
4017
        body = resp.click('template.odt', index=0).follow().body
4018
        assert_equal_zip(BytesIO(body), f)
4019
    response2 = resp.click('test.rtf', index=0).follow()
4020
    assert response2.body == b'HELLO NEW WORLD'
4021
    # Test attachment substitution variables
4022
    variables = formdef.data_class().select()[0].get_substitution_variables()
4023
    assert 'attachments' in variables
4024
    attachments = variables['attachments']
4025
    assert attachments is not None
4026
    file1 = attachments.created_doc
4027
    assert file1.content == response2.body
4028
    assert file1.b64_content == base64.b64encode(response2.body)
4029
    assert file1.content_type == response2._headers['content-type']
4030
    content_disposition = response2._headers['content-disposition']
4031
    assert len(content_disposition.split(';')) == 2
4032
    assert content_disposition.split(';')[0] == 'attachment'
4033
    assert response2.request.environ['PATH_INFO'].endswith(file1.filename)
4034

  
4035
    resp = login(get_app(pub), username='admin', password='admin').get(file1.url).follow()
4036
    assert file1.content == resp.body
4037
    assert file1.b64_content == base64.b64encode(resp.body)
4038
    assert file1.content_type == resp._headers['content-type']
4039
    content_disposition = resp._headers['content-disposition']
4040
    assert len(content_disposition.split(';')) == 2
4041
    assert content_disposition.split(';')[0] == 'attachment'
4042
    assert resp.request.environ['PATH_INFO'].endswith(file1.filename)
4043

  
4044
    file2 = attachments.created_doc[1]
4045
    assert file2.content == response1.body
4046
    assert file2.b64_content == base64.b64encode(response1.body)
4047
    assert file2.content_type == response1._headers['content-type']
4048
    content_disposition = response1._headers['content-disposition']
4049
    assert len(content_disposition.split(';')) == 2
4050
    assert content_disposition.split(';')[0] == 'attachment'
4051
    assert response1.request.environ['PATH_INFO'].endswith(file2.filename)
4052

  
4053
    resp = login(get_app(pub), username='admin', password='admin').get(file2.url).follow()
4054
    assert file2.content == resp.body
4055
    assert file2.b64_content == base64.b64encode(resp.body)
4056
    assert file2.content_type == resp._headers['content-type']
4057
    content_disposition = resp._headers['content-disposition']
4058
    assert len(content_disposition.split(';')) == 2
4059
    assert content_disposition.split(';')[0] == 'attachment'
4060
    assert resp.request.environ['PATH_INFO'].endswith(file2.filename)
4061

  
4062

  
4063
@pytest.mark.skipif(transform_to_pdf is None, reason='libreoffice not found')
4064
def test_formdata_generated_document_odt_to_pdf_download(pub):
4065
    create_user(pub)
4066
    wf = Workflow(name='status')
4067
    st1 = wf.add_status('Status1', 'st1')
4068
    export_to = ExportToModel()
4069
    export_to.label = 'create doc'
4070
    export_to.varname = 'created_doc'
4071
    template_filename = os.path.join(os.path.dirname(__file__), '..', 'template.odt')
4072
    template = open(template_filename, 'rb').read()
4073
    upload = QuixoteUpload('/foo/template.odt', content_type='application/octet-stream')
4074
    upload.fp = BytesIO()
4075
    upload.fp.write(template)
4076
    upload.fp.seek(0)
4077
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
4078
    export_to.id = '_export_to'
4079
    export_to.by = ['_submitter']
4080
    export_to.convert_to_pdf = True
4081
    st1.items.append(export_to)
4082
    export_to.parent = st1
4083
    wf.store()
4084

  
4085
    formdef = create_formdef()
4086
    formdef.workflow_id = wf.id
4087
    formdef.fields = []
4088
    formdef.store()
4089
    formdef.data_class().wipe()
4090

  
4091
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
4092
    resp = resp.forms[0].submit('submit')
4093
    assert 'Check values then click submit.' in resp.text
4094
    resp = resp.forms[0].submit('submit')
4095
    assert resp.status_int == 302
4096
    form_location = resp.location
4097
    resp = resp.follow()
4098
    assert 'The form has been recorded' in resp.text
4099

  
4100
    resp = resp.form.submit('button_export_to')
4101

  
4102
    resp = resp.follow() # $form/$id/create_doc
4103
    resp = resp.follow() # $form/$id/create_doc/
4104
    assert resp.content_type == 'application/pdf'
4105
    assert b'PDF' in resp.body
4106

  
4107
    export_to.attach_to_history = True
4108
    wf.store()
4109

  
4110
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
4111
    resp = resp.form.submit('button_export_to')
4112
    assert resp.location == form_location + '#action-zone'
4113
    resp = resp.follow() # back to form page
4114

  
4115
    resp = resp.click('template.pdf')
4116
    assert resp.location.endswith('/template.pdf')
4117
    resp = resp.follow()
4118
    assert resp.content_type == 'application/pdf'
4119
    content_disposition = resp._headers['content-disposition']
4120
    assert len(content_disposition.split(';')) == 2
4121
    assert content_disposition.split(';')[0] == 'inline'
4122
    assert resp.body.startswith(b'%PDF-')
4123

  
4124

  
4125
@pytest.mark.skipif(transform_to_pdf is None, reason='libreoffice not found')
4126
def test_formdata_generated_document_odt_to_pdf_download_push_to_portfolio(pub, fargo_url,
4127
                                                                           fargo_secret, caplog):
4128
    user = create_user(pub)
4129
    user.name = 'Foo Baré'
4130
    user.store()
4131

  
4132
    pub.cfg['debug'] = {'logger': True}
4133
    pub.write_cfg()
4134
    wf = Workflow(name='status')
4135
    st1 = wf.add_status('Status1', 'st1')
4136
    export_to = ExportToModel()
4137
    export_to.label = 'create doc'
4138
    export_to.varname = 'created_doc'
4139
    template_filename = os.path.join(os.path.dirname(__file__), '..', 'template.odt')
4140
    template = open(template_filename, 'rb').read()
4141
    upload = QuixoteUpload('/foo/template.odt', content_type='application/octet-stream')
4142
    upload.fp = BytesIO()
4143
    upload.fp.write(template)
4144
    upload.fp.seek(0)
4145
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
4146
    export_to.id = '_export_to'
4147
    export_to.by = ['_submitter']
4148
    export_to.convert_to_pdf = True
4149
    export_to.push_to_portfolio = True
4150
    st1.items.append(export_to)
4151
    export_to.parent = st1
4152
    wf.store()
4153

  
4154
    formdef = create_formdef()
4155
    formdef.workflow_id = wf.id
4156
    formdef.fields = []
4157
    formdef.store()
4158
    formdef.data_class().wipe()
4159

  
4160
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
4161
    resp = resp.forms[0].submit('submit')
4162
    assert 'Check values then click submit.' in resp.text
4163
    resp = resp.forms[0].submit('submit')
4164
    assert resp.status_int == 302
4165
    form_location = resp.location
4166
    resp = resp.follow()
4167
    assert 'The form has been recorded' in resp.text
4168

  
4169
    with mock.patch('wcs.portfolio.http_post_request') as http_post_request:
4170
        http_post_request.return_value = None, 200, 'null', None
4171
        resp = resp.form.submit('button_export_to')
4172
        assert http_post_request.call_count == 1
4173
        if locale.getpreferredencoding() == 'UTF-8':
4174
            assert ("file 'template.pdf' pushed to portfolio of 'Foo Baré'"
4175
                    == caplog.records[-1].message)
4176
        else:  # Python < 3.7
4177
            assert ("file 'template.pdf' pushed to portfolio of 'Foo Bar\xe9'"
4178
                    == caplog.records[-1].message)
4179

  
4180
    resp = resp.follow()  # $form/$id/create_doc
4181
    resp = resp.follow()  # $form/$id/create_doc/
4182
    assert resp.content_type == 'application/pdf'
4183
    assert b'PDF' in resp.body
4184

  
4185
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
4186
    with mock.patch('wcs.portfolio.http_post_request') as http_post_request:
4187
        http_post_request.return_value = None, 400, 'null', None  # fail
4188
        resp = resp.form.submit('button_export_to')
4189
        assert http_post_request.call_count == 1
4190
        assert caplog.records[-1].message.startswith("file 'template.pdf' failed to be pushed to portfolio of 'Foo")
4191

  
4192
    # failed to push to portfolio, but document is here
4193
    resp = resp.follow()  # $form/$id/create_doc
4194
    resp = resp.follow()  # $form/$id/create_doc/
4195
    assert resp.content_type == 'application/pdf'
4196
    assert b'PDF' in resp.body
4197

  
4198
    export_to.attach_to_history = True
4199
    wf.store()
4200

  
4201
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
4202
    with mock.patch('wcs.portfolio.http_post_request') as http_post_request:
4203
        http_post_request.return_value = None, 200, 'null', None
4204
        resp = resp.form.submit('button_export_to')
4205
        assert http_post_request.call_count == 1
4206
        assert http_post_request.call_args[0][0].startswith('http://fargo.example.net/api/documents/push/')
4207
        payload = json.loads(http_post_request.call_args[0][1])
4208
        assert payload['file_name'] == 'template.pdf'
4209
        assert payload['user_email'] == 'foo@localhost'
4210
        assert payload['origin'] == 'example.net'
4211
        assert base64.decodebytes(force_bytes(payload['file_b64_content'])).startswith(b'%PDF')
4212
        assert caplog.records[-1].message.startswith("file 'template.pdf' pushed to portfolio of 'Foo")
4213
    assert resp.location == form_location + '#action-zone'
4214
    resp = resp.follow()  # back to form page
4215

  
4216
    resp = resp.click('template.pdf')
4217
    assert resp.location.endswith('/template.pdf')
4218
    resp = resp.follow()
4219
    assert resp.content_type == 'application/pdf'
4220
    assert resp.body.startswith(b'%PDF-')
4221

  
4222

  
4223
def test_formdata_generated_document_non_interactive(pub):
4224
    create_user(pub)
4225
    wf = Workflow(name='status')
4226
    st1 = wf.add_status('Status1', 'st1')
4227
    export_to = ExportToModel()
4228
    export_to.convert_to_pdf = False
4229
    export_to.method = 'non-interactive'
4230
    template_filename = os.path.join(os.path.dirname(__file__), '..', 'template.odt')
4231
    template = open(template_filename, 'rb').read()
4232
    upload = QuixoteUpload('/foo/template.odt', content_type='application/octet-stream')
4233
    upload.fp = BytesIO()
4234
    upload.fp.write(template)
4235
    upload.fp.seek(0)
4236
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
4237
    export_to.id = '_export_to'
4238
    export_to.attach_to_history = True
4239
    st1.items.append(export_to)
4240
    export_to.parent = st1
4241

  
4242
    jump = JumpWorkflowStatusItem()
4243
    jump.status = 'st2'
4244
    st1.items.append(jump)
4245
    jump.parent = st1
4246

  
4247
    st2 = wf.add_status('Status2', 'st2')
4248

  
4249
    wf.store()
4250

  
4251
    formdef = create_formdef()
4252
    formdef.workflow_id = wf.id
4253
    formdef.fields = [fields.TextField(id='0', label='comment', type='text', varname='comment')]
4254
    formdef.store()
4255
    formdef.data_class().wipe()
4256

  
4257
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
4258
    resp.form['f0'] = 'Hello\n\nWorld.'
4259
    resp = resp.forms[0].submit('submit')
4260
    assert 'Check values then click submit.' in resp.text
4261
    resp = resp.forms[0].submit('submit')
4262
    assert resp.status_int == 302
4263
    form_location = resp.location
4264
    resp = resp.follow()
4265
    assert 'The form has been recorded' in resp.text
4266

  
4267
    resp = resp.click('template.odt')
4268
    assert resp.location.endswith('/template.odt')
4269
    resp = resp.follow()
4270
    assert resp.content_type == 'application/octet-stream'
4271
    with open(os.path.join(os.path.dirname(__file__), '..', 'template-out.odt'), 'rb') as f:
4272
        assert_equal_zip(BytesIO(resp.body), f)
4273

  
4274
    assert formdef.data_class().count() == 1
4275
    assert formdef.data_class().select()[0].status == 'wf-st2'
4276

  
4277

  
4278
def test_formdata_generated_document_to_backoffice_field(pub):
4279
    create_user_and_admin(pub)
4280
    wf = Workflow(name='status')
4281
    wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf)
4282
    wf.backoffice_fields_formdef.fields = [
4283
        fields.FileField(id='bo1', label='bo field 1', type='file'),
4284
        fields.StringField(id='bo2', label='bo field 2', type='string'),
4285
    ]
4286

  
4287
    st1 = wf.add_status('Status1', 'st1')
4288
    export_to = ExportToModel()
4289
    export_to.convert_to_pdf = False
4290
    export_to.method = 'non-interactive'
4291
    template_filename = os.path.join(os.path.dirname(__file__), '..', 'template.odt')
4292
    template = open(template_filename, 'rb').read()
4293
    upload = QuixoteUpload('/foo/template.odt', content_type='application/octet-stream')
4294
    upload.fp = BytesIO()
4295
    upload.fp.write(template)
4296
    upload.fp.seek(0)
4297
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
4298
    export_to.id = '_export_to'
4299
    export_to.attach_to_history = True
4300
    export_to.backoffice_filefield_id = 'bo1'
4301
    st1.items.append(export_to)
4302
    export_to.parent = st1
4303

  
4304
    assert export_to.get_backoffice_filefield_options() == [('bo1', 'bo field 1', 'bo1')]
4305

  
4306
    jump = JumpWorkflowStatusItem()
4307
    jump.status = 'st2'
4308
    st1.items.append(jump)
4309
    jump.parent = st1
4310
    wf.add_status('Status2', 'st2')
4311
    wf.store()
4312

  
4313
    formdef = create_formdef()
4314
    formdef.workflow_id = wf.id
4315
    formdef.fields = [fields.TextField(id='0', label='comment', type='text', varname='comment')]
4316
    formdef.store()
4317
    formdef.data_class().wipe()
4318

  
4319
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
4320
    resp.form['f0'] = 'Hello\n\nWorld.'
4321
    resp = resp.forms[0].submit('submit')
4322
    assert 'Check values then click submit.' in resp.text
4323
    resp = resp.forms[0].submit('submit')
4324
    assert resp.status_int == 302
4325
    resp = resp.follow()
4326
    assert 'The form has been recorded' in resp.text
4327

  
4328
    # get the two generated files from backoffice: in backoffice fields
4329
    # (export_to.backoffice_filefield_id), and in history (export_to.attach_to_history)
4330
    for index in (0, 1):
4331
        resp = login(get_app(pub), username='admin', password='admin').get(
4332
                '/backoffice/management/test/1/')
4333
        resp = resp.click('template.odt', index=index)
4334
        assert resp.location.endswith('/template.odt')
4335
        resp = resp.follow()
4336
        assert resp.content_type == 'application/octet-stream'
4337
        with open(os.path.join(os.path.dirname(__file__), '..', 'template-out.odt'), 'rb') as f:
4338
            assert_equal_zip(BytesIO(resp.body), f)
4339

  
4340
    assert formdef.data_class().count() == 1
4341
    assert formdef.data_class().select()[0].status == 'wf-st2'
4342

  
4343

  
4344
def test_formdata_generated_document_in_private_history(pub):
4345
    user = create_user(pub)
4346

  
4347
    Role.wipe()
4348
    role = Role(name='xxx')
4349
    role.store()
4350

  
4351
    user.roles = [role.id]
4352
    user.store()
4353

  
4354
    wf = Workflow(name='status')
4355
    st0 = wf.add_status('Status0', 'st0')
4356
    st1 = wf.add_status('Status1', 'st1')
4357
    export_to = ExportToModel()
4358
    export_to.label = 'create doc'
4359
    upload = QuixoteUpload('/foo/test.rtf', content_type='application/rtf')
4360
    upload.fp = BytesIO()
4361
    upload.fp.write(b'HELLO WORLD')
4362
    upload.fp.seek(0)
4363
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
4364
    export_to.attach_to_history = True
4365
    export_to.id = '_export_to'
4366
    export_to.by = ['_submitter']
4367
    st1.items.append(export_to)
4368
    export_to.parent = st1
4369

  
4370
    st2 = wf.add_status('Status2', 'st2')
4371

  
4372
    jump1 = ChoiceWorkflowStatusItem()
4373
    jump1.id = '_jump1'
4374
    jump1.label = 'Jump 1'
4375
    jump1.by = ['_receiver']
4376
    jump1.status = st1.id
4377
    jump1.parent = st0
4378
    st0.items.append(jump1)
4379

  
4380
    jump2 = ChoiceWorkflowStatusItem()
4381
    jump2.id = '_jump2'
4382
    jump2.label = 'Jump 2'
4383
    jump2.by = ['_receiver']
4384
    jump2.status = st2.id
4385
    jump2.parent = st1
4386
    st1.items.append(jump2)
4387

  
4388
    wf.store()
4389

  
4390
    formdef = create_formdef()
4391
    formdef.workflow_id = wf.id
4392
    formdef.workflow_roles = {'_receiver': role.id}
4393
    formdef.fields = []
4394
    formdef.store()
4395
    formdef.data_class().wipe()
4396

  
4397
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
4398
    resp = resp.forms[0].submit('submit')
4399
    assert 'Check values then click submit.' in resp.text
4400
    resp = resp.forms[0].submit('submit')
4401
    assert resp.status_int == 302
4402
    form_location = resp.location
4403
    resp = resp.follow()
4404
    assert 'The form has been recorded' in resp.text
4405

  
4406
    resp = resp.form.submit('button_jump1')
4407
    resp = resp.follow()
4408

  
4409
    resp = resp.form.submit('button_export_to')
4410
    resp = resp.follow()
4411
    assert 'Form exported in a model' in resp.text
4412

  
4413
    resp = resp.form.submit('button_jump2')
4414
    resp = resp.follow()
4415

  
4416
    # limit visibility of status with document
4417
    st1.visibility = ['_receiver']
4418
    wf.store()
4419

  
4420
    formdata = formdef.data_class().select()[0]
4421
    resp = login(get_app(pub), username='foo', password='foo').get(formdata.get_url())
4422
    assert not 'Form exported in a model' in resp.text
4423

  
4424
    # check status is visible in backoffice
4425
    resp = login(get_app(pub), username='foo', password='foo').get(formdata.get_url(backoffice=True))
4426
    assert 'visibility-off' in resp.text
4427
    assert 'Form exported in a model' in resp.text
4428

  
4429

  
4430
def test_formdata_form_file_download(pub):
4431
    create_user(pub)
4432
    wf = Workflow(name='status')
4433
    st1 = wf.add_status('Status1', 'st1')
4434

  
4435
    display_form = FormWorkflowStatusItem()
4436
    display_form.id = '_x'
4437
    display_form.by = ['_submitter']
4438
    display_form.varname = 'xxx'
4439
    display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
4440
    display_form.formdef.fields.append(fields.FileField(id='1', label='File',
4441
        type='file', varname='yyy'))
4442
    st1.items.append(display_form)
4443
    display_form.parent = st1
4444

  
4445
    wf.store()
4446

  
4447
    formdef = create_formdef()
4448
    formdef.workflow_id = wf.id
4449
    formdef.fields = []
4450
    formdef.store()
4451
    formdef.data_class().wipe()
4452

  
4453
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
4454
    resp = resp.forms[0].submit('submit')
4455
    assert 'Check values then click submit.' in resp.text
4456
    resp = resp.forms[0].submit('submit')
4457
    assert resp.status_int == 302
4458
    resp = resp.follow()
4459
    assert 'The form has been recorded' in resp.text
4460

  
4461
    assert 'qommon.fileupload.js' in resp.text
4462
    resp.forms[0]['f1$file'] = Upload('test.txt', b'foobar', 'text/plain')
4463
    resp = resp.forms[0].submit('submit')
4464

  
4465
    assert formdef.data_class().count() == 1
4466
    formdata = formdef.data_class().select()[0]
4467
    assert 'xxx_var_yyy_raw' in formdata.workflow_data
4468

  
4469
    download = resp.test_app.get(urlparse.urljoin(resp.location, 'files/form-xxx-yyy/test.txt'))
4470
    assert download.content_type == 'text/plain'
4471
    assert download.body == b'foobar'
4472

  
4473
    # go back to the status page, this will exercise the substitution variables
4474
    # codepath.
4475
    resp = resp.follow()
4476

  
4477

  
4478
def test_formdata_workflow_form_prefill(pub):
4479
    create_user(pub)
4480
    wf = Workflow(name='status')
4481
    st1 = wf.add_status('Status1', 'st1')
4482

  
4483
    display_form = FormWorkflowStatusItem()
4484
    display_form.id = '_x'
4485
    display_form.by = ['_submitter']
4486
    display_form.varname = 'xxx'
4487
    display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
4488
    display_form.formdef.fields.append(fields.StringField(id='1', label='blah',
4489
        type='string', varname='yyy', prefill={'type': 'user', 'value': 'email'}))
4490
    st1.items.append(display_form)
4491
    display_form.parent = st1
4492

  
4493
    wf.store()
4494

  
4495
    formdef = create_formdef()
4496
    formdef.workflow_id = wf.id
4497
    formdef.fields = []
4498
    formdef.store()
4499
    formdef.data_class().wipe()
4500

  
4501
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
4502
    resp = resp.forms[0].submit('submit')
4503
    assert 'Check values then click submit.' in resp.text
4504
    resp = resp.forms[0].submit('submit')
4505
    assert resp.status_int == 302
4506
    resp = resp.follow()
4507
    assert 'The form has been recorded' in resp.text
4508
    assert resp.forms[0]['f1'].value == 'foo@localhost'
4509

  
4510

  
4511
def test_formdata_workflow_form_prefill_conditional_field(pub):
4512
    create_user(pub)
4513
    wf = Workflow(name='status')
4514
    st1 = wf.add_status('Status1', 'st1')
4515

  
4516
    display_form = FormWorkflowStatusItem()
4517
    display_form.id = '_x'
4518
    display_form.by = ['_submitter']
4519
    display_form.varname = 'xxx'
4520
    display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
4521
    display_form.formdef.fields.append(fields.StringField(id='1', label='blah1',
4522
        type='string', varname='yyy1', prefill={'type': 'user', 'value': 'email'},
4523
        condition={'type': 'django', 'value': '0'}))
4524
    display_form.formdef.fields.append(fields.StringField(id='2', label='blah2',
4525
        type='string', varname='yyy2', prefill={'type': 'user', 'value': 'email'},
4526
        condition={'type': 'django', 'value': '1'}))
4527
    st1.items.append(display_form)
4528
    display_form.parent = st1
4529

  
4530
    wf.store()
4531

  
4532
    formdef = create_formdef()
4533
    formdef.workflow_id = wf.id
4534
    formdef.fields = []
4535
    formdef.store()
4536
    formdef.data_class().wipe()
4537

  
4538
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
4539
    resp = resp.forms[0].submit('submit')
4540
    assert 'Check values then click submit.' in resp.text
4541
    resp = resp.forms[0].submit('submit')
4542
    assert resp.status_int == 302
4543
    resp = resp.follow()
4544
    assert 'The form has been recorded' in resp.text
4545
    assert resp.forms[0]['f2'].value == 'foo@localhost'
4546

  
4547

  
4548
def test_formdata_workflow_form_prefill_checkbox(pub):
4549
    create_user(pub)
4550
    wf = Workflow(name='status')
4551
    st1 = wf.add_status('Status1', 'st1')
4552

  
4553
    display_form = FormWorkflowStatusItem()
4554
    display_form.id = '_x'
4555
    display_form.by = ['_submitter']
4556
    display_form.varname = 'xxx'
4557
    display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
4558
    display_form.formdef.fields.append(fields.BoolField(id='1', label='blah',
4559
        type='bool', varname='yyy', prefill={'type': 'formula', 'value': 'True'}))
4560
    display_form.formdef.fields.append(fields.BoolField(id='2', label='blah2',
4561
        type='bool', varname='zzz', prefill={'type': 'formula', 'value': 'True'}))
4562
    st1.items.append(display_form)
4563
    display_form.parent = st1
4564

  
4565
    wf.store()
4566

  
4567
    formdef = create_formdef()
4568
    formdef.workflow_id = wf.id
4569
    formdef.fields = []
4570
    formdef.store()
4571
    formdef.data_class().wipe()
4572

  
4573
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
4574
    resp = resp.form.submit('submit')
4575
    assert 'Check values then click submit.' in resp
4576
    resp = resp.form.submit('submit').follow()
4577
    assert 'The form has been recorded' in resp
4578
    assert resp.form['f1'].checked is True
4579
    assert resp.form['f2'].checked is True
4580
    resp.form['f1'].checked = False
4581
    resp = resp.form.submit('submit')
4582

  
4583
    formdata = formdef.data_class().select()[0]
4584
    assert formdata.workflow_data['xxx_var_yyy_raw'] is False
4585
    assert formdata.workflow_data['xxx_var_zzz_raw'] is True
4586

  
4587

  
4588
def test_formdata_workflow_form_prefill_autocomplete(pub):
4589
    create_user(pub)
4590

  
4591
    NamedDataSource.wipe()
4592
    data_source = NamedDataSource(name='foobar')
4593
    data_source.data_source = {'type': 'json', 'value': 'http://local-mock/test'}
4594
    data_source.query_parameter = 'q'
4595
    data_source.id_parameter = 'id'
4596
    data_source.store()
4597

  
4598
    wf = Workflow(name='status')
4599
    st1 = wf.add_status('Status1', 'st1')
4600

  
4601
    display_form = FormWorkflowStatusItem()
4602
    display_form.id = '_x'
4603
    display_form.by = ['_submitter']
4604
    display_form.varname = 'xxx'
4605
    display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
4606
    display_form.formdef.fields = [
4607
        fields.ItemField(id='4', label='string', type='item',
4608
            data_source={'type': 'foobar'},
4609
            required=False,
4610
            display_mode='autocomplete',
4611
            prefill={'type': 'string', 'value': '{{ form_var_foo_raw }}'},
4612
        ),
4613
    ]
4614
    st1.items.append(display_form)
4615
    display_form.parent = st1
4616

  
4617
    wf.store()
4618

  
4619
    formdef = create_formdef()
4620
    formdef.workflow_id = wf.id
4621
    formdef.fields = [
4622
        fields.ItemField(id='0', label='string', type='item',
4623
            data_source={'type': 'foobar'},
4624
            display_mode='autocomplete',
4625
            required=False,
4626
            varname='foo',
4627
        ),
4628
    ]
4629
    formdef.store()
4630
    formdef.data_class().wipe()
4631

  
4632
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
4633
    with mock.patch('wcs.qommon.misc.urlopen') as urlopen:
4634
        data = {'data': [{'id': '1', 'text': 'hello', 'extra': 'foo'}]}
4635

  
4636
        def side_effect(url, *args):
4637
            return StringIO(json.dumps(data))
4638

  
4639
        urlopen.side_effect = side_effect
4640

  
4641
        assert 'data-select2-url=' in resp
4642
        # simulate select2
4643
        resp.form.fields['f0_display'] = Hidden(form=resp.form, tag='input', name='f0_display', pos=10)
4644
        resp.form['f0'].force_value('1')
4645
        resp.form.fields['f0_display'].force_value('foo')
4646
        resp = resp.form.submit('submit')
4647
        assert 'Check values then click submit.' in resp
4648
        resp = resp.form.submit('submit').follow()
4649
        assert 'The form has been recorded' in resp
4650

  
4651
        # check display value is in form action widget
4652
        assert resp.form['f4'].attrs['data-value'] == '1'
4653
        assert resp.form['f4'].attrs['data-initial-display-value'] == 'hello'
4654

  
4655
        # check it is also displayed in a fresh session
4656
        resp = login(get_app(pub), username='foo', password='foo').get(resp.request.url)
4657
        assert resp.form['f4'].attrs['data-value'] == '1'
4658
        assert resp.form['f4'].attrs['data-initial-display-value'] == 'hello'
4659

  
4660

  
4661 3520
def test_form_map_field_back_and_submit(pub):
4662 3521
    formdef = create_formdef()
4663 3522
    formdef.fields = [
......
6680 5539
    assert len(LoggedError.get_ids_with_indexed_value('workflow_id', 'X')) == 0
6681 5540

  
6682 5541

  
6683
def test_formdata_named_wscall(http_requests, pub):
6684
    create_user(pub)
6685
    NamedWsCall.wipe()
6686

  
6687
    wscall = NamedWsCall()
6688
    wscall.name = 'Hello world'
6689
    wscall.request = {'url': 'http://remote.example.net/json'}
6690
    wscall.store()
6691
    assert wscall.slug == 'hello_world'
6692

  
6693
    wf = Workflow(name='status')
6694
    st1 = wf.add_status('Status1', 'st1')
6695
    comment = RegisterCommenterWorkflowStatusItem()
6696
    comment.id = '_comment'
6697
    comment.comment = 'Hello [webservice.hello_world.foo] World'
6698
    st1.items.append(comment)
6699
    comment.parent = st1
6700

  
6701
    display = DisplayMessageWorkflowStatusItem()
6702
    display.message = 'The form has been recorded and: X[webservice.hello_world.foo]Y'
6703
    display.to = []
6704
    st1.items.append(display)
6705
    display.parent = st1
6706

  
6707
    wf.store()
6708

  
6709
    formdef = create_formdef()
6710
    formdef.workflow_id = wf.id
6711
    formdef.fields = []
6712
    formdef.store()
6713
    formdef.data_class().wipe()
6714

  
6715
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
6716
    resp = resp.forms[0].submit('submit')
6717
    assert 'Check values then click submit.' in resp.text
6718
    resp = resp.forms[0].submit('submit')
6719
    assert resp.status_int == 302
6720
    resp = resp.follow()
6721
    assert 'The form has been recorded and: XbarY' in resp.text
6722

  
6723
    formdata = formdef.data_class().select()[0]
6724
    assert formdata.evolution[0].parts[0].content == 'Hello bar World'
6725

  
6726
    # check with publisher variable in named webservice call
6727
    if not pub.site_options.has_section('variables'):
6728
        pub.site_options.add_section('variables')
6729
    pub.site_options.set('variables', 'example_url', 'http://remote.example.net/')
6730
    with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
6731
        pub.site_options.write(fd)
6732

  
6733
    wscall = NamedWsCall()
6734
    wscall.name = 'Hello world'
6735
    wscall.request = {'url': '[example_url]json'}
6736
    wscall.store()
6737

  
6738
    formdef.data_class().wipe()
6739

  
6740
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
6741
    resp = resp.forms[0].submit('submit')
6742
    assert 'Check values then click submit.' in resp.text
6743
    resp = resp.forms[0].submit('submit')
6744
    assert resp.status_int == 302
6745
    resp = resp.follow()
6746
    assert 'The form has been recorded and: XbarY' in resp.text
6747

  
6748
    formdata = formdef.data_class().select()[0]
6749
    assert formdata.evolution[0].parts[0].content == 'Hello bar World'
6750

  
6751

  
6752
def test_formdata_named_wscall_in_conditions(http_requests, pub):
6753
    create_user(pub)
6754
    NamedWsCall.wipe()
6755

  
6756
    wscall = NamedWsCall()
6757
    wscall.name = 'Hello world'
6758
    wscall.request = {'url': 'http://remote.example.net/json', 'method': 'GET'}
6759
    wscall.store()
6760
    assert wscall.slug == 'hello_world'
6761

  
6762
    formdef = create_formdef()
6763
    formdef.fields = [
6764
        fields.PageField(id='0', label='1st page', type='page'),
6765
        fields.PageField(id='1', label='2nd page', type='page',
6766
            condition={'type': 'python', 'value': 'webservice.hello_world["foo"] == "bar"'}),
6767
        fields.PageField(id='1', label='3rd page', type='page',
6768
            condition={'type': 'python', 'value': 'webservice.hello_world["foo"] != "bar"'}),
6769
        fields.PageField(id='1', label='4th page', type='page',
6770
            condition={'type': 'python', 'value': 'webservice.hello_world["foo"] == "bar"'}),
6771
    ]
6772
    formdef.store()
6773
    formdef.data_class().wipe()
6774

  
6775
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
6776
    assert '>1st page<' in resp.text
6777
    assert '>2nd page<' in resp.text
6778
    assert '>3rd page<' not in resp.text
6779
    assert '>4th page<' in resp.text
6780
    assert len(http_requests.requests) == 1
6781

  
6782

  
6783 5542
def test_resubmit(pub):
6784 5543
    user = create_user(pub)
6785 5544

  
tests/form_pages/test_block.py
3 3
import pytest
4 4
from webtest import Upload
5 5

  
6
from wcs.qommon.ident.password_accounts import PasswordAccount
7 6
from wcs.blocks import BlockDef
8 7
from wcs.formdef import FormDef
9 8
from wcs.workflows import Workflow
......
13 12
from wcs import fields
14 13

  
15 14
from utilities import get_app, login, create_temporary_pub, clean_temporary_pub
15
from .test_all import create_user
16 16

  
17 17

  
18 18
def pytest_generate_tests(metafunc):
......
42 42
    clean_temporary_pub()
43 43

  
44 44

  
45
def create_user(pub):
46
    pub.user_class.wipe()
47
    PasswordAccount.wipe()
48

  
49
    user = pub.user_class()
50
    user.name = 'User Name'
51
    user.email = 'foo@localhost'
52
    user.store()
53
    account = PasswordAccount(id='foo')
54
    account.set_password('foo')
55
    account.user_id = user.id
56
    account.store()
57
    return user
58

  
59

  
60 45
def test_block_simple(pub, blocks_feature):
61 46
    FormDef.wipe()
62 47
    BlockDef.wipe()
tests/form_pages/test_formdata.py
1
# -*- coding: utf-8 -*-
2
import base64
3
import json
4
import locale
5
import os
6
import xml.etree.ElementTree as ET
7
import zipfile
8

  
9
import mock
10
import pytest
11
from django.utils.encoding import force_bytes
12
from django.utils.six import BytesIO, StringIO
13
from django.utils.six.moves.urllib import parse as urlparse
14
from quixote.http_request import Upload as QuixoteUpload
15
from webtest import Upload, Hidden
16

  
17
from wcs import fields
18
from wcs.data_sources import NamedDataSource
19
from wcs.formdef import FormDef
20
from wcs.qommon.form import UploadedFile
21
from wcs.roles import Role
22
from wcs.wf.attachment import AddAttachmentWorkflowStatusItem
23
from wcs.wf.export_to_model import ExportToModel, transform_to_pdf
24
from wcs.wf.form import FormWorkflowStatusItem, WorkflowFormFieldsFormDef
25
from wcs.wf.jump import JumpWorkflowStatusItem
26
from wcs.wf.register_comment import RegisterCommenterWorkflowStatusItem
27
from wcs.workflows import (ChoiceWorkflowStatusItem,
28
                           DisplayMessageWorkflowStatusItem, Workflow,
29
                           WorkflowBackofficeFieldsFormDef)
30
from wcs.wscalls import NamedWsCall
31

  
32
from .test_all import create_user, create_user_and_admin
33
from utilities import clean_temporary_pub, create_temporary_pub, get_app, login
34

  
35

  
36
def pytest_generate_tests(metafunc):
37
    if 'pub' in metafunc.fixturenames:
38
        metafunc.parametrize('pub', ['pickle', 'sql', 'pickle-templates', 'pickle-lazy'], indirect=True)
39

  
40

  
41
@pytest.fixture
42
def pub(request, emails):
43
    pub = create_temporary_pub(
44
            sql_mode=bool('sql' in request.param),
45
            templates_mode=bool('templates' in request.param),
46
            lazy_mode=bool('lazy' in request.param),
47
            )
48
    pub.cfg['identification'] = {'methods': ['password']}
49
    pub.cfg['language'] = {'language': 'en'}
50
    pub.write_cfg()
51

  
52
    return pub
53

  
54

  
55
def teardown_module(module):
56
    clean_temporary_pub()
57

  
58

  
59
def assert_equal_zip(stream1, stream2):
60
    z1 = zipfile.ZipFile(stream1)
61
    z2 = zipfile.ZipFile(stream2)
62
    assert set(z1.namelist()) == set(z2.namelist())
63
    for name in z1.namelist():
64
        if name == 'styles.xml':
65
            continue
66
        if name in ['content.xml', 'meta.xml']:
67
            t1, t2 = ET.tostring(ET.XML(z1.read(name))), ET.tostring(ET.XML(z2.read(name)))
68
            try:
69
                # >= python 3.8: tostring preserves attribute order; use canonicalize to sort them
70
                t1, t2 = ET.canonicalize(t1), ET.canonicalize(t2)
71
            except AttributeError:
72
                pass
73
        else:
74
            t1, t2 = z1.read(name), z2.read(name)
75
        assert t1 == t2, 'file "%s" differs' % name
76

  
77

  
78
def test_formdata_attachment_download(pub):
79
    create_user(pub)
80
    wf = Workflow(name='status')
81
    st1 = wf.add_status('Status1', 'st1')
82
    attach = AddAttachmentWorkflowStatusItem()
83
    attach.id = '_attach'
84
    attach.by = ['_submitter']
85
    st1.items.append(attach)
86
    attach.parent = st1
87
    wf.store()
88

  
89
    FormDef.wipe()
90
    formdef = FormDef()
91
    formdef.name = 'test'
92
    formdef.workflow_id = wf.id
93
    formdef.fields = []
94
    formdef.store()
95
    formdef.data_class().wipe()
96

  
97
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
98
    resp = resp.forms[0].submit('submit')
99
    assert 'Check values then click submit.' in resp.text
100
    resp = resp.forms[0].submit('submit')
101
    assert resp.status_int == 302
102
    resp = resp.follow()
103
    assert 'The form has been recorded' in resp.text
104

  
105
    resp.forms[0]['attachment_attach$file'] = Upload('test.txt', b'foobar', 'text/plain')
106
    resp = resp.forms[0].submit('button_attach')
107

  
108
    assert formdef.data_class().count() == 1
109
    formdata = formdef.data_class().select()[0]
110
    assert formdata.evolution[-1].parts[0].__class__.__name__ == 'AttachmentEvolutionPart'
111
    attachment = formdata.evolution[-1].parts[0]
112
    assert attachment.content_type == 'text/plain'
113
    assert attachment.orig_filename == 'test.txt'
114

  
115
    resp = resp.follow()  # back to form page
116
    resp = resp.click('test.txt')
117
    assert resp.location.endswith('/test.txt')
118
    resp = resp.follow()
119
    assert resp.content_type == 'text/plain'
120
    assert resp.text == 'foobar'
121

  
122

  
123
def test_formdata_attachment_download_with_substitution_variable(pub):
124
    create_user_and_admin(pub)
125
    wf = Workflow(name='status')
126
    st1 = wf.add_status('Status1', 'st1')
127
    attach = AddAttachmentWorkflowStatusItem()
128
    attach.varname = 'attached_doc'
129
    attach.id = '_attach'
130
    attach.by = ['_submitter']
131
    st1.items.append(attach)
132
    attach.parent = st1
133
    wf.store()
134

  
135
    FormDef.wipe()
136
    formdef = FormDef()
137
    formdef.name = 'test'
138
    formdef.workflow_id = wf.id
139
    formdef.fields = []
140
    formdef.store()
141
    formdef.data_class().wipe()
142

  
143
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
144
    resp = resp.forms[0].submit('submit')
145
    assert 'Check values then click submit.' in resp.text
146
    resp = resp.forms[0].submit('submit')
147
    assert resp.status_int == 302
148
    resp = resp.follow()
149
    assert 'The form has been recorded' in resp.text
150

  
151
    resp.forms[0]['attachment_attach$file'] = Upload('test.txt', b'foobar', 'text/plain')
152
    resp = resp.forms[0].submit('button_attach')
153

  
154
    assert formdef.data_class().count() == 1
155
    formdata = formdef.data_class().select()[0]
156
    assert formdata.evolution[-1].parts[0].__class__.__name__ == 'AttachmentEvolutionPart'
157
    attachment = formdata.evolution[-1].parts[0]
158
    assert attachment.content_type == 'text/plain'
159
    assert attachment.orig_filename == 'test.txt'
160

  
161
    resp = resp.follow()  # back to form page
162
    resp = resp.click('test.txt')
163
    assert resp.location.endswith('/test.txt')
164
    resp = resp.follow()
165
    assert resp.content_type == 'text/plain'
166
    assert resp.text == 'foobar'
167

  
168
    variables = formdef.data_class().select()[0].get_substitution_variables()
169
    assert 'attachments' in variables
170
    attachments = variables['attachments']
171
    assert attachments is not None
172
    attachment_variable = attachments.attached_doc
173

  
174
    resp = login(get_app(pub), username='admin', password='admin').get(
175
        attachment_variable.url).follow()
176
    assert attachment_variable.content == resp.body
177
    assert attachment_variable.b64_content == base64.b64encode(resp.body)
178
    assert attachment_variable.content_type == resp._headers['content-type'].split(';')[0]
179
    content_disposition = resp._headers['content-disposition']
180
    assert len(content_disposition.split(';')) == 2
181
    assert content_disposition.split(';')[0] == 'attachment'
182
    assert resp.request.environ['PATH_INFO'].endswith(attachment_variable.filename)
183

  
184

  
185
def test_formdata_attachment_download_to_backoffice_file_field(pub):
186
    create_user(pub)
187
    wf = Workflow(name='status')
188
    wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf)
189
    wf.backoffice_fields_formdef.fields = [
190
        fields.FileField(id='bo1', label='bo field 1', type='file'),
191
    ]
192
    st1 = wf.add_status('Status1', 'st1')
193
    attach = AddAttachmentWorkflowStatusItem()
194
    attach.id = '_attach'
195
    attach.by = ['_submitter']
196
    attach.backoffice_filefield_id = 'bo1'
197
    st1.items.append(attach)
198
    attach.parent = st1
199
    wf.store()
200

  
201
    assert attach.get_backoffice_filefield_options() == [('bo1', 'bo field 1', 'bo1')]
202

  
203
    FormDef.wipe()
204
    formdef = FormDef()
205
    formdef.name = 'test'
206
    formdef.workflow_id = wf.id
207
    formdef.fields = []
208
    formdef.store()
209
    formdef.data_class().wipe()
210

  
211
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
212
    resp = resp.forms[0].submit('submit')
213
    assert 'Check values then click submit.' in resp.text
214
    resp = resp.forms[0].submit('submit')
215
    assert resp.status_int == 302
216
    resp = resp.follow()
217
    assert 'The form has been recorded' in resp.text
218

  
219
    resp.forms[0]['attachment_attach$file'] = Upload('test.txt', b'foobar', 'text/plain')
220
    resp = resp.forms[0].submit('button_attach')
221

  
222
    # backoffice file field is set
223
    assert formdef.data_class().count() == 1
224
    formdata = formdef.data_class().select()[0]
225
    assert 'bo1' in formdata.data
226
    bo1 = formdata.data['bo1']
227
    assert bo1.base_filename == 'test.txt'
228
    assert bo1.content_type == 'text/plain'
229
    assert bo1.get_content() == b'foobar'
230

  
231
    # and file is in history, too
232
    assert formdata.evolution[-1].parts[0].__class__.__name__ == 'AttachmentEvolutionPart'
233
    attachment = formdata.evolution[-1].parts[0]
234
    assert attachment.content_type == 'text/plain'
235
    assert attachment.orig_filename == 'test.txt'
236

  
237

  
238
def test_formdata_attachment_download_to_backoffice_file_field_only(pub):
239
    create_user(pub)
240
    wf = Workflow(name='status')
241
    wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf)
242
    wf.backoffice_fields_formdef.fields = [
243
        fields.FileField(id='bo1', label='bo field 1', type='file'),
244
    ]
245
    st1 = wf.add_status('Status1', 'st1')
246
    attach = AddAttachmentWorkflowStatusItem()
247
    attach.id = '_attach'
248
    attach.by = ['_submitter']
249
    attach.backoffice_filefield_id = 'bo1'
250
    attach.attach_to_history = False  # store only in backoffice field
251
    st1.items.append(attach)
252
    attach.parent = st1
253
    wf.store()
254

  
255
    assert attach.get_backoffice_filefield_options() == [('bo1', 'bo field 1', 'bo1')]
256

  
257
    FormDef.wipe()
258
    formdef = FormDef()
259
    formdef.name = 'test'
260
    formdef.workflow_id = wf.id
261
    formdef.fields = []
262
    formdef.store()
263
    formdef.data_class().wipe()
264

  
265
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
266
    resp = resp.forms[0].submit('submit')
267
    assert 'Check values then click submit.' in resp.text
268
    resp = resp.forms[0].submit('submit')
269
    assert resp.status_int == 302
270
    resp = resp.follow()
271
    assert 'The form has been recorded' in resp.text
272

  
273
    resp.forms[0]['attachment_attach$file'] = Upload('test.txt', b'foobar', 'text/plain')
274
    resp = resp.forms[0].submit('button_attach')
275

  
276
    # backoffice file field is set
277
    assert formdef.data_class().count() == 1
278
    formdata = formdef.data_class().select()[0]
279
    assert 'bo1' in formdata.data
280
    bo1 = formdata.data['bo1']
281
    assert bo1.base_filename == 'test.txt'
282
    assert bo1.content_type == 'text/plain'
283
    assert bo1.get_content() == b'foobar'
284

  
285
    # but nothing in history
286
    for evo in formdata.evolution:
287
        assert not evo.parts
288

  
289

  
290
def test_formdata_attachment_file_options(pub):
291
    create_user(pub)
292
    wf = Workflow(name='status')
293
    st1 = wf.add_status('Status1', 'st1')
294
    attach = AddAttachmentWorkflowStatusItem()
295
    attach.id = '_attach'
296
    attach.by = ['_submitter']
297
    attach.document_type = {'label': 'Fichiers vidéo', 'mimetypes': ['video/*'], 'id': '_video'}
298
    attach.max_file_size = '3Mo'
299

  
300
    st1.items.append(attach)
301
    attach.parent = st1
302
    wf.store()
303

  
304
    FormDef.wipe()
305
    formdef = FormDef()
306
    formdef.name = 'test'
307
    formdef.workflow_id = wf.id
308
    formdef.fields = []
309
    formdef.store()
310
    formdef.data_class().wipe()
311

  
312
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
313
    resp = resp.forms[0].submit('submit')
314
    assert 'Check values then click submit.' in resp.text
315
    resp = resp.forms[0].submit('submit')
316
    assert resp.status_int == 302
317
    resp = resp.follow()
318
    assert 'The form has been recorded' in resp.text
319

  
320
    file_input = resp.forms[0]['attachment_attach$file']
321
    assert file_input.attrs['accept'] == 'video/*'
322
    assert file_input.attrs['data-max-file-size'] == '3000000'
323

  
324

  
325
def test_formdata_generated_document_download(pub):
326
    create_user(pub)
327
    wf = Workflow(name='status')
328
    st1 = wf.add_status('Status1', 'st1')
329
    export_to = ExportToModel()
330
    export_to.convert_to_pdf = False
331
    export_to.label = 'create doc'
332
    upload = QuixoteUpload('/foo/test.rtf', content_type='application/rtf')
333
    upload.fp = BytesIO()
334
    upload.fp.write(b'HELLO WORLD')
335
    upload.fp.seek(0)
336
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
337
    export_to.id = '_export_to'
338
    export_to.by = ['_submitter']
339
    st1.items.append(export_to)
340
    export_to.parent = st1
341
    wf.store()
342

  
343
    FormDef.wipe()
344
    formdef = FormDef()
345
    formdef.name = 'test'
346
    formdef.workflow_id = wf.id
347
    formdef.fields = []
348
    formdef.store()
349
    formdef.data_class().wipe()
350

  
351
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
352
    resp = resp.forms[0].submit('submit')
353
    assert 'Check values then click submit.' in resp.text
354
    resp = resp.forms[0].submit('submit')
355
    assert resp.status_int == 302
356
    form_location = resp.location
357
    resp = resp.follow()
358
    assert 'The form has been recorded' in resp.text
359

  
360
    resp = resp.form.submit('button_export_to')
361

  
362
    resp = resp.follow()  # $form/$id/create_doc
363
    resp = resp.follow()  # $form/$id/create_doc/
364
    assert resp.text == 'HELLO WORLD'
365

  
366
    export_to.attach_to_history = True
367
    wf.store()
368

  
369
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
370
    resp = resp.form.submit('button_export_to')
371
    assert resp.location == form_location + '#action-zone'
372
    resp = resp.follow()  # back to form page
373

  
374
    resp = resp.click('test.rtf')
375
    assert resp.location.endswith('/test.rtf')
376
    resp = resp.follow()
377
    assert resp.content_type == 'application/rtf'
378
    assert resp.text == 'HELLO WORLD'
379

  
380
    # change export model to now be a RTF file, do the action again on the same form and
381
    # check that both the old .odt file and the new .rtf file are there and valid.
382
    upload = QuixoteUpload('/foo/test.rtf', content_type='application/rtf')
383
    upload.fp = BytesIO()
384
    upload.fp.write(b'HELLO NEW WORLD')
385
    upload.fp.seek(0)
386
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
387
    wf.store()
388

  
389
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
390
    resp = resp.form.submit('button_export_to')
391
    assert resp.location == form_location + '#action-zone'
392
    resp = resp.follow()  # back to form page
393

  
394
    assert resp.click('test.rtf', index=0).follow().text == 'HELLO WORLD'
395
    assert resp.click('test.rtf', index=1).follow().text == 'HELLO NEW WORLD'
396

  
397
    # use substitution variables on rtf: only ezt format is accepted
398
    upload = QuixoteUpload('/foo/test.rtf', content_type='application/rtf')
399
    upload.fp = BytesIO()
400
    upload.fp.write(b'HELLO {{DJANGO}} WORLD [form_name]')
401
    upload.fp.seek(0)
402
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
403
    wf.store()
404

  
405
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
406
    resp = resp.form.submit('button_export_to')
407
    assert resp.location == form_location + '#action-zone'
408
    resp = resp.follow()
409

  
410
    assert resp.click('test.rtf', index=2).follow().text == 'HELLO {{DJANGO}} WORLD {\\uc1{test}}'
411

  
412

  
413
@pytest.fixture(params=['template.odt', 'template-django.odt'])
414
def odt_template(request):
415
    return request.param
416

  
417

  
418
def test_formdata_generated_document_odt_download(pub, odt_template):
419
    create_user(pub)
420
    wf = Workflow(name='status')
421
    st1 = wf.add_status('Status1', 'st1')
422
    export_to = ExportToModel()
423
    export_to.convert_to_pdf = False
424
    export_to.label = 'create doc'
425
    template_filename = os.path.join(os.path.dirname(__file__), '..', odt_template)
426
    template = open(template_filename, 'rb').read()
427
    upload = QuixoteUpload('/foo/' + odt_template, content_type='application/octet-stream')
428
    upload.fp = BytesIO()
429
    upload.fp.write(template)
430
    upload.fp.seek(0)
431
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
432
    export_to.id = '_export_to'
433
    export_to.by = ['_submitter']
434
    st1.items.append(export_to)
435
    export_to.parent = st1
436
    wf.store()
437

  
438
    FormDef.wipe()
439
    formdef = FormDef()
440
    formdef.name = 'test'
441
    formdef.workflow_id = wf.id
442
    formdef.fields = [fields.TextField(id='0', label='comment', type='text', varname='comment')]
443
    formdef.store()
444
    formdef.data_class().wipe()
445

  
446
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
447
    resp.form['f0'] = 'Hello\n\nWorld.'
448
    resp = resp.forms[0].submit('submit')
449
    assert 'Check values then click submit.' in resp.text
450
    resp = resp.forms[0].submit('submit')
451
    assert resp.status_int == 302
452
    form_location = resp.location
453
    resp = resp.follow()
454
    assert 'The form has been recorded' in resp.text
455

  
456
    resp = resp.form.submit('button_export_to')
457

  
458
    resp = resp.follow()  # $form/$id/create_doc
459
    resp = resp.follow()  # $form/$id/create_doc/
460
    with open(os.path.join(os.path.dirname(__file__), '..', 'template-out.odt'), 'rb') as f:
461
        assert_equal_zip(BytesIO(resp.body), f)
462

  
463
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
464
    resp = resp.form.submit('button_export_to')
465
    resp = resp.follow()  # $form/$id/create_doc
466
    with mock.patch('wcs.wf.export_to_model.get_formdata_template_context') as get_context_1:
467
        with mock.patch('wcs.workflows.get_formdata_template_context') as get_context_never:
468
            get_context_1.return_value = {}
469
            get_context_never.return_value = {}
470
            resp = resp.follow()  # $form/$id/create_doc/
471
            # substitution variables are computed only one :
472
            assert get_context_1.call_count == 1
473
            assert get_context_never.call_count == 0
474

  
475
    export_to.attach_to_history = True
476
    wf.store()
477

  
478
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
479
    resp = resp.form.submit('button_export_to')
480
    assert resp.location == form_location + '#action-zone'
481
    resp = resp.follow()  # back to form page
482

  
483
    resp = resp.click(odt_template)
484
    assert resp.location.endswith('/' + odt_template)
485
    resp = resp.follow()
486
    assert resp.content_type == 'application/octet-stream'
487
    with open(os.path.join(os.path.dirname(__file__), '..', 'template-out.odt'), 'rb') as f:
488
        assert_equal_zip(BytesIO(resp.body), f)
489

  
490
    # change file content, same name
491
    upload = QuixoteUpload('/foo/test.rtf', content_type='application/rtf')
492
    upload.fp = BytesIO()
493
    upload.fp.write(b'HELLO NEW WORLD')
494
    upload.fp.seek(0)
495
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
496
    wf.store()
497

  
498
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
499
    resp = resp.form.submit('button_export_to')
500
    assert resp.location == form_location + '#action-zone'
501
    resp = resp.follow()  # back to form page
502

  
503
    with open(os.path.join(os.path.dirname(__file__), '..', 'template-out.odt'), 'rb') as f:
504
        body = resp.click(odt_template, index=0).follow().body
505
        assert_equal_zip(BytesIO(body), f)
506
    assert resp.click('test.rtf', index=0).follow().body == b'HELLO NEW WORLD'
507

  
508

  
509
def test_formdata_generated_document_odt_download_with_substitution_variable(pub):
510
    create_user_and_admin(pub)
511
    wf = Workflow(name='status')
512
    st1 = wf.add_status('Status1', 'st1')
513
    export_to = ExportToModel()
514
    export_to.convert_to_pdf = False
515
    export_to.label = 'create doc'
516
    export_to.varname = 'created_doc'
517
    template_filename = os.path.join(os.path.dirname(__file__), '..', 'template.odt')
518
    template = open(template_filename, 'rb').read()
519
    upload = QuixoteUpload('/foo/template.odt', content_type='application/octet-stream')
520
    upload.fp = BytesIO()
521
    upload.fp.write(template)
522
    upload.fp.seek(0)
523
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
524
    export_to.id = '_export_to'
525
    export_to.by = ['_submitter']
526
    st1.items.append(export_to)
527
    export_to.parent = st1
528
    wf.store()
529

  
530
    FormDef.wipe()
531
    formdef = FormDef()
532
    formdef.name = 'test'
533
    formdef.workflow_id = wf.id
534
    formdef.fields = [fields.TextField(id='0', label='comment', type='text', varname='comment')]
535
    formdef.store()
536
    formdef.data_class().wipe()
537

  
538
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
539
    resp.form['f0'] = 'Hello\n\nWorld.'
540
    resp = resp.forms[0].submit('submit')
541
    assert 'Check values then click submit.' in resp.text
542
    resp = resp.forms[0].submit('submit')
543
    assert resp.status_int == 302
544
    form_location = resp.location
545
    resp = resp.follow()
546
    assert 'The form has been recorded' in resp.text
547

  
548
    resp = resp.form.submit('button_export_to')
549

  
550
    resp = resp.follow()  # $form/$id/create_doc
551
    resp = resp.follow()  # $form/$id/create_doc/
552
    with open(os.path.join(os.path.dirname(__file__), '..', 'template-out.odt'), 'rb') as f:
553
        assert_equal_zip(BytesIO(resp.body), f)
554

  
555
    export_to.attach_to_history = True
556
    wf.store()
557

  
558
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
559
    resp = resp.form.submit('button_export_to')
560
    assert resp.location == form_location + '#action-zone'
561
    resp = resp.follow()  # back to form page
562

  
563
    resp = resp.click('template.odt')
564
    assert resp.location.endswith('/template.odt')
565
    response1 = resp = resp.follow()
566
    assert resp.content_type == 'application/octet-stream'
567
    with open(os.path.join(os.path.dirname(__file__), '..', 'template-out.odt'), 'rb') as f:
568
        assert_equal_zip(BytesIO(resp.body), f)
569

  
570
    # change file content, same name
571
    upload = QuixoteUpload('/foo/test.rtf', content_type='application/rtf')
572
    upload.fp = BytesIO()
573
    upload.fp.write(b'HELLO NEW WORLD')
574
    upload.fp.seek(0)
575
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
576
    wf.store()
577

  
578
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
579
    resp = resp.form.submit('button_export_to')
580
    assert resp.location == form_location + '#action-zone'
581
    resp = resp.follow()  # back to form page
582

  
583
    with open(os.path.join(os.path.dirname(__file__), '..', 'template-out.odt'), 'rb') as f:
584
        body = resp.click('template.odt', index=0).follow().body
585
        assert_equal_zip(BytesIO(body), f)
586
    response2 = resp.click('test.rtf', index=0).follow()
587
    assert response2.body == b'HELLO NEW WORLD'
588
    # Test attachment substitution variables
589
    variables = formdef.data_class().select()[0].get_substitution_variables()
590
    assert 'attachments' in variables
591
    attachments = variables['attachments']
592
    assert attachments is not None
593
    file1 = attachments.created_doc
594
    assert file1.content == response2.body
595
    assert file1.b64_content == base64.b64encode(response2.body)
596
    assert file1.content_type == response2._headers['content-type']
597
    content_disposition = response2._headers['content-disposition']
598
    assert len(content_disposition.split(';')) == 2
599
    assert content_disposition.split(';')[0] == 'attachment'
600
    assert response2.request.environ['PATH_INFO'].endswith(file1.filename)
601

  
602
    resp = login(get_app(pub), username='admin', password='admin').get(file1.url).follow()
603
    assert file1.content == resp.body
604
    assert file1.b64_content == base64.b64encode(resp.body)
605
    assert file1.content_type == resp._headers['content-type']
606
    content_disposition = resp._headers['content-disposition']
607
    assert len(content_disposition.split(';')) == 2
608
    assert content_disposition.split(';')[0] == 'attachment'
609
    assert resp.request.environ['PATH_INFO'].endswith(file1.filename)
610

  
611
    file2 = attachments.created_doc[1]
612
    assert file2.content == response1.body
613
    assert file2.b64_content == base64.b64encode(response1.body)
614
    assert file2.content_type == response1._headers['content-type']
615
    content_disposition = response1._headers['content-disposition']
616
    assert len(content_disposition.split(';')) == 2
617
    assert content_disposition.split(';')[0] == 'attachment'
618
    assert response1.request.environ['PATH_INFO'].endswith(file2.filename)
619

  
620
    resp = login(get_app(pub), username='admin', password='admin').get(file2.url).follow()
621
    assert file2.content == resp.body
622
    assert file2.b64_content == base64.b64encode(resp.body)
623
    assert file2.content_type == resp._headers['content-type']
624
    content_disposition = resp._headers['content-disposition']
625
    assert len(content_disposition.split(';')) == 2
626
    assert content_disposition.split(';')[0] == 'attachment'
627
    assert resp.request.environ['PATH_INFO'].endswith(file2.filename)
628

  
629

  
630
@pytest.mark.skipif(transform_to_pdf is None, reason='libreoffice not found')
631
def test_formdata_generated_document_odt_to_pdf_download(pub):
632
    create_user(pub)
633
    wf = Workflow(name='status')
634
    st1 = wf.add_status('Status1', 'st1')
635
    export_to = ExportToModel()
636
    export_to.label = 'create doc'
637
    export_to.varname = 'created_doc'
638
    template_filename = os.path.join(os.path.dirname(__file__), '..', 'template.odt')
639
    template = open(template_filename, 'rb').read()
640
    upload = QuixoteUpload('/foo/template.odt', content_type='application/octet-stream')
641
    upload.fp = BytesIO()
642
    upload.fp.write(template)
643
    upload.fp.seek(0)
644
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
645
    export_to.id = '_export_to'
646
    export_to.by = ['_submitter']
647
    export_to.convert_to_pdf = True
648
    st1.items.append(export_to)
649
    export_to.parent = st1
650
    wf.store()
651

  
652
    FormDef.wipe()
653
    formdef = FormDef()
654
    formdef.name = 'test'
655
    formdef.workflow_id = wf.id
656
    formdef.fields = []
657
    formdef.store()
658
    formdef.data_class().wipe()
659

  
660
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
661
    resp = resp.forms[0].submit('submit')
662
    assert 'Check values then click submit.' in resp.text
663
    resp = resp.forms[0].submit('submit')
664
    assert resp.status_int == 302
665
    form_location = resp.location
666
    resp = resp.follow()
667
    assert 'The form has been recorded' in resp.text
668

  
669
    resp = resp.form.submit('button_export_to')
670

  
671
    resp = resp.follow()  # $form/$id/create_doc
672
    resp = resp.follow()  # $form/$id/create_doc/
673
    assert resp.content_type == 'application/pdf'
674
    assert b'PDF' in resp.body
675

  
676
    export_to.attach_to_history = True
677
    wf.store()
678

  
679
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
680
    resp = resp.form.submit('button_export_to')
681
    assert resp.location == form_location + '#action-zone'
682
    resp = resp.follow()  # back to form page
683

  
684
    resp = resp.click('template.pdf')
685
    assert resp.location.endswith('/template.pdf')
686
    resp = resp.follow()
687
    assert resp.content_type == 'application/pdf'
688
    content_disposition = resp._headers['content-disposition']
689
    assert len(content_disposition.split(';')) == 2
690
    assert content_disposition.split(';')[0] == 'inline'
691
    assert resp.body.startswith(b'%PDF-')
692

  
693

  
694
@pytest.mark.skipif(transform_to_pdf is None, reason='libreoffice not found')
695
def test_formdata_generated_document_odt_to_pdf_download_push_to_portfolio(pub, fargo_url,
696
                                                                           fargo_secret, caplog):
697
    user = create_user(pub)
698
    user.name = 'Foo Baré'
699
    user.store()
700

  
701
    pub.cfg['debug'] = {'logger': True}
702
    pub.write_cfg()
703
    wf = Workflow(name='status')
704
    st1 = wf.add_status('Status1', 'st1')
705
    export_to = ExportToModel()
706
    export_to.label = 'create doc'
707
    export_to.varname = 'created_doc'
708
    template_filename = os.path.join(os.path.dirname(__file__), '..', 'template.odt')
709
    template = open(template_filename, 'rb').read()
710
    upload = QuixoteUpload('/foo/template.odt', content_type='application/octet-stream')
711
    upload.fp = BytesIO()
712
    upload.fp.write(template)
713
    upload.fp.seek(0)
714
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
715
    export_to.id = '_export_to'
716
    export_to.by = ['_submitter']
717
    export_to.convert_to_pdf = True
718
    export_to.push_to_portfolio = True
719
    st1.items.append(export_to)
720
    export_to.parent = st1
721
    wf.store()
722

  
723
    FormDef.wipe()
724
    formdef = FormDef()
725
    formdef.name = 'test'
726
    formdef.workflow_id = wf.id
727
    formdef.fields = []
728
    formdef.store()
729
    formdef.data_class().wipe()
730

  
731
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
732
    resp = resp.forms[0].submit('submit')
733
    assert 'Check values then click submit.' in resp.text
734
    resp = resp.forms[0].submit('submit')
735
    assert resp.status_int == 302
736
    form_location = resp.location
737
    resp = resp.follow()
738
    assert 'The form has been recorded' in resp.text
739

  
740
    with mock.patch('wcs.portfolio.http_post_request') as http_post_request:
741
        http_post_request.return_value = None, 200, 'null', None
742
        resp = resp.form.submit('button_export_to')
743
        assert http_post_request.call_count == 1
744
        if locale.getpreferredencoding() == 'UTF-8':
745
            assert ("file 'template.pdf' pushed to portfolio of 'Foo Baré'"
746
                    == caplog.records[-1].message)
747
        else:  # Python < 3.7
748
            assert ("file 'template.pdf' pushed to portfolio of 'Foo Bar\xe9'"
749
                    == caplog.records[-1].message)
750

  
751
    resp = resp.follow()  # $form/$id/create_doc
752
    resp = resp.follow()  # $form/$id/create_doc/
753
    assert resp.content_type == 'application/pdf'
754
    assert b'PDF' in resp.body
755

  
756
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
757
    with mock.patch('wcs.portfolio.http_post_request') as http_post_request:
758
        http_post_request.return_value = None, 400, 'null', None  # fail
759
        resp = resp.form.submit('button_export_to')
760
        assert http_post_request.call_count == 1
761
        assert caplog.records[-1].message.startswith("file 'template.pdf' failed to be pushed to portfolio of 'Foo")
762

  
763
    # failed to push to portfolio, but document is here
764
    resp = resp.follow()  # $form/$id/create_doc
765
    resp = resp.follow()  # $form/$id/create_doc/
766
    assert resp.content_type == 'application/pdf'
767
    assert b'PDF' in resp.body
768

  
769
    export_to.attach_to_history = True
770
    wf.store()
771

  
772
    resp = login(get_app(pub), username='foo', password='foo').get(form_location)
773
    with mock.patch('wcs.portfolio.http_post_request') as http_post_request:
774
        http_post_request.return_value = None, 200, 'null', None
775
        resp = resp.form.submit('button_export_to')
776
        assert http_post_request.call_count == 1
777
        assert http_post_request.call_args[0][0].startswith('http://fargo.example.net/api/documents/push/')
778
        payload = json.loads(http_post_request.call_args[0][1])
779
        assert payload['file_name'] == 'template.pdf'
780
        assert payload['user_email'] == 'foo@localhost'
781
        assert payload['origin'] == 'example.net'
782
        assert base64.decodebytes(force_bytes(payload['file_b64_content'])).startswith(b'%PDF')
783
        assert caplog.records[-1].message.startswith("file 'template.pdf' pushed to portfolio of 'Foo")
784
    assert resp.location == form_location + '#action-zone'
785
    resp = resp.follow()  # back to form page
786

  
787
    resp = resp.click('template.pdf')
788
    assert resp.location.endswith('/template.pdf')
789
    resp = resp.follow()
790
    assert resp.content_type == 'application/pdf'
791
    assert resp.body.startswith(b'%PDF-')
792

  
793

  
794
def test_formdata_generated_document_non_interactive(pub):
795
    create_user(pub)
796
    wf = Workflow(name='status')
797
    st1 = wf.add_status('Status1', 'st1')
798
    export_to = ExportToModel()
799
    export_to.convert_to_pdf = False
800
    export_to.method = 'non-interactive'
801
    template_filename = os.path.join(os.path.dirname(__file__), '..', 'template.odt')
802
    template = open(template_filename, 'rb').read()
803
    upload = QuixoteUpload('/foo/template.odt', content_type='application/octet-stream')
804
    upload.fp = BytesIO()
805
    upload.fp.write(template)
806
    upload.fp.seek(0)
807
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
808
    export_to.id = '_export_to'
809
    export_to.attach_to_history = True
810
    st1.items.append(export_to)
811
    export_to.parent = st1
812

  
813
    jump = JumpWorkflowStatusItem()
814
    jump.status = 'st2'
815
    st1.items.append(jump)
816
    jump.parent = st1
817

  
818
    wf.add_status('Status2', 'st2')
819

  
820
    wf.store()
821

  
822
    FormDef.wipe()
823
    formdef = FormDef()
824
    formdef.name = 'test'
825
    formdef.workflow_id = wf.id
826
    formdef.fields = [fields.TextField(id='0', label='comment', type='text', varname='comment')]
827
    formdef.store()
828
    formdef.data_class().wipe()
829

  
830
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
831
    resp.form['f0'] = 'Hello\n\nWorld.'
832
    resp = resp.forms[0].submit('submit')
833
    assert 'Check values then click submit.' in resp.text
834
    resp = resp.forms[0].submit('submit')
835
    assert resp.status_int == 302
836
    resp = resp.follow()
837
    assert 'The form has been recorded' in resp.text
838

  
839
    resp = resp.click('template.odt')
840
    assert resp.location.endswith('/template.odt')
841
    resp = resp.follow()
842
    assert resp.content_type == 'application/octet-stream'
843
    with open(os.path.join(os.path.dirname(__file__), '..', 'template-out.odt'), 'rb') as f:
844
        assert_equal_zip(BytesIO(resp.body), f)
845

  
846
    assert formdef.data_class().count() == 1
847
    assert formdef.data_class().select()[0].status == 'wf-st2'
848

  
849

  
850
def test_formdata_generated_document_to_backoffice_field(pub):
851
    create_user_and_admin(pub)
852
    wf = Workflow(name='status')
853
    wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf)
854
    wf.backoffice_fields_formdef.fields = [
855
        fields.FileField(id='bo1', label='bo field 1', type='file'),
856
        fields.StringField(id='bo2', label='bo field 2', type='string'),
857
    ]
858

  
859
    st1 = wf.add_status('Status1', 'st1')
860
    export_to = ExportToModel()
861
    export_to.convert_to_pdf = False
862
    export_to.method = 'non-interactive'
863
    template_filename = os.path.join(os.path.dirname(__file__), '..', 'template.odt')
864
    template = open(template_filename, 'rb').read()
865
    upload = QuixoteUpload('/foo/template.odt', content_type='application/octet-stream')
866
    upload.fp = BytesIO()
867
    upload.fp.write(template)
868
    upload.fp.seek(0)
869
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
870
    export_to.id = '_export_to'
871
    export_to.attach_to_history = True
872
    export_to.backoffice_filefield_id = 'bo1'
873
    st1.items.append(export_to)
874
    export_to.parent = st1
875

  
876
    assert export_to.get_backoffice_filefield_options() == [('bo1', 'bo field 1', 'bo1')]
877

  
878
    jump = JumpWorkflowStatusItem()
879
    jump.status = 'st2'
880
    st1.items.append(jump)
881
    jump.parent = st1
882
    wf.add_status('Status2', 'st2')
883
    wf.store()
884

  
885
    FormDef.wipe()
886
    formdef = FormDef()
887
    formdef.name = 'test'
888
    formdef.workflow_id = wf.id
889
    formdef.fields = [fields.TextField(id='0', label='comment', type='text', varname='comment')]
890
    formdef.store()
891
    formdef.data_class().wipe()
892

  
893
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
894
    resp.form['f0'] = 'Hello\n\nWorld.'
895
    resp = resp.forms[0].submit('submit')
896
    assert 'Check values then click submit.' in resp.text
897
    resp = resp.forms[0].submit('submit')
898
    assert resp.status_int == 302
899
    resp = resp.follow()
900
    assert 'The form has been recorded' in resp.text
901

  
902
    # get the two generated files from backoffice: in backoffice fields
903
    # (export_to.backoffice_filefield_id), and in history (export_to.attach_to_history)
904
    for index in (0, 1):
905
        resp = login(get_app(pub), username='admin', password='admin').get(
906
                '/backoffice/management/test/1/')
907
        resp = resp.click('template.odt', index=index)
908
        assert resp.location.endswith('/template.odt')
909
        resp = resp.follow()
910
        assert resp.content_type == 'application/octet-stream'
911
        with open(os.path.join(os.path.dirname(__file__), '..', 'template-out.odt'), 'rb') as f:
912
            assert_equal_zip(BytesIO(resp.body), f)
913

  
914
    assert formdef.data_class().count() == 1
915
    assert formdef.data_class().select()[0].status == 'wf-st2'
916

  
917

  
918
def test_formdata_generated_document_in_private_history(pub):
919
    user = create_user(pub)
920

  
921
    Role.wipe()
922
    role = Role(name='xxx')
923
    role.store()
924

  
925
    user.roles = [role.id]
926
    user.store()
927

  
928
    wf = Workflow(name='status')
929
    st0 = wf.add_status('Status0', 'st0')
930
    st1 = wf.add_status('Status1', 'st1')
931
    export_to = ExportToModel()
932
    export_to.label = 'create doc'
933
    upload = QuixoteUpload('/foo/test.rtf', content_type='application/rtf')
934
    upload.fp = BytesIO()
935
    upload.fp.write(b'HELLO WORLD')
936
    upload.fp.seek(0)
937
    export_to.model_file = UploadedFile(pub.app_dir, None, upload)
938
    export_to.attach_to_history = True
939
    export_to.id = '_export_to'
940
    export_to.by = ['_submitter']
941
    st1.items.append(export_to)
942
    export_to.parent = st1
943

  
944
    st2 = wf.add_status('Status2', 'st2')
945

  
946
    jump1 = ChoiceWorkflowStatusItem()
947
    jump1.id = '_jump1'
948
    jump1.label = 'Jump 1'
949
    jump1.by = ['_receiver']
950
    jump1.status = st1.id
951
    jump1.parent = st0
952
    st0.items.append(jump1)
953

  
954
    jump2 = ChoiceWorkflowStatusItem()
955
    jump2.id = '_jump2'
956
    jump2.label = 'Jump 2'
957
    jump2.by = ['_receiver']
958
    jump2.status = st2.id
959
    jump2.parent = st1
960
    st1.items.append(jump2)
961

  
962
    wf.store()
963

  
964
    FormDef.wipe()
965
    formdef = FormDef()
966
    formdef.name = 'test'
967
    formdef.workflow_id = wf.id
968
    formdef.workflow_roles = {'_receiver': role.id}
969
    formdef.fields = []
970
    formdef.store()
971
    formdef.data_class().wipe()
972

  
973
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
974
    resp = resp.forms[0].submit('submit')
975
    assert 'Check values then click submit.' in resp.text
976
    resp = resp.forms[0].submit('submit')
977
    assert resp.status_int == 302
978
    resp = resp.follow()
979
    assert 'The form has been recorded' in resp.text
980

  
981
    resp = resp.form.submit('button_jump1')
982
    resp = resp.follow()
983

  
984
    resp = resp.form.submit('button_export_to')
985
    resp = resp.follow()
986
    assert 'Form exported in a model' in resp.text
987

  
988
    resp = resp.form.submit('button_jump2')
989
    resp = resp.follow()
990

  
991
    # limit visibility of status with document
992
    st1.visibility = ['_receiver']
993
    wf.store()
994

  
995
    formdata = formdef.data_class().select()[0]
996
    resp = login(get_app(pub), username='foo', password='foo').get(formdata.get_url())
997
    assert 'Form exported in a model' not in resp.text
998

  
999
    # check status is visible in backoffice
1000
    resp = login(get_app(pub), username='foo', password='foo').get(formdata.get_url(backoffice=True))
1001
    assert 'visibility-off' in resp.text
1002
    assert 'Form exported in a model' in resp.text
1003

  
1004

  
1005
def test_formdata_form_file_download(pub):
1006
    create_user(pub)
1007
    wf = Workflow(name='status')
1008
    st1 = wf.add_status('Status1', 'st1')
1009

  
1010
    display_form = FormWorkflowStatusItem()
1011
    display_form.id = '_x'
1012
    display_form.by = ['_submitter']
1013
    display_form.varname = 'xxx'
1014
    display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
1015
    display_form.formdef.fields.append(fields.FileField(
1016
        id='1', label='File',
1017
        type='file', varname='yyy'))
1018
    st1.items.append(display_form)
1019
    display_form.parent = st1
1020

  
1021
    wf.store()
1022

  
1023
    FormDef.wipe()
1024
    formdef = FormDef()
1025
    formdef.name = 'test'
1026
    formdef.workflow_id = wf.id
1027
    formdef.fields = []
1028
    formdef.store()
1029
    formdef.data_class().wipe()
1030

  
1031
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
1032
    resp = resp.forms[0].submit('submit')
1033
    assert 'Check values then click submit.' in resp.text
1034
    resp = resp.forms[0].submit('submit')
1035
    assert resp.status_int == 302
1036
    resp = resp.follow()
1037
    assert 'The form has been recorded' in resp.text
1038

  
1039
    assert 'qommon.fileupload.js' in resp.text
1040
    resp.forms[0]['f1$file'] = Upload('test.txt', b'foobar', 'text/plain')
1041
    resp = resp.forms[0].submit('submit')
1042

  
1043
    assert formdef.data_class().count() == 1
1044
    formdata = formdef.data_class().select()[0]
1045
    assert 'xxx_var_yyy_raw' in formdata.workflow_data
1046

  
1047
    download = resp.test_app.get(urlparse.urljoin(resp.location, 'files/form-xxx-yyy/test.txt'))
1048
    assert download.content_type == 'text/plain'
1049
    assert download.body == b'foobar'
1050

  
1051
    # go back to the status page, this will exercise the substitution variables
1052
    # codepath.
1053
    resp = resp.follow()
1054

  
1055

  
1056
def test_formdata_workflow_form_prefill(pub):
1057
    create_user(pub)
1058
    wf = Workflow(name='status')
1059
    st1 = wf.add_status('Status1', 'st1')
1060

  
1061
    display_form = FormWorkflowStatusItem()
1062
    display_form.id = '_x'
1063
    display_form.by = ['_submitter']
1064
    display_form.varname = 'xxx'
1065
    display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
1066
    display_form.formdef.fields.append(fields.StringField(
1067
        id='1', label='blah',
1068
        type='string', varname='yyy', prefill={'type': 'user', 'value': 'email'}))
1069
    st1.items.append(display_form)
1070
    display_form.parent = st1
1071

  
1072
    wf.store()
1073

  
1074
    FormDef.wipe()
1075
    formdef = FormDef()
1076
    formdef.name = 'test'
1077
    formdef.workflow_id = wf.id
1078
    formdef.fields = []
1079
    formdef.store()
1080
    formdef.data_class().wipe()
1081

  
1082
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
1083
    resp = resp.forms[0].submit('submit')
1084
    assert 'Check values then click submit.' in resp.text
1085
    resp = resp.forms[0].submit('submit')
1086
    assert resp.status_int == 302
1087
    resp = resp.follow()
1088
    assert 'The form has been recorded' in resp.text
1089
    assert resp.forms[0]['f1'].value == 'foo@localhost'
1090

  
1091

  
1092
def test_formdata_workflow_form_prefill_conditional_field(pub):
1093
    create_user(pub)
1094
    wf = Workflow(name='status')
1095
    st1 = wf.add_status('Status1', 'st1')
1096

  
1097
    display_form = FormWorkflowStatusItem()
1098
    display_form.id = '_x'
1099
    display_form.by = ['_submitter']
1100
    display_form.varname = 'xxx'
1101
    display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
1102
    display_form.formdef.fields.append(fields.StringField(
1103
        id='1', label='blah1',
1104
        type='string', varname='yyy1', prefill={'type': 'user', 'value': 'email'},
1105
        condition={'type': 'django', 'value': '0'}))
1106
    display_form.formdef.fields.append(fields.StringField(
1107
        id='2', label='blah2',
1108
        type='string', varname='yyy2', prefill={'type': 'user', 'value': 'email'},
1109
        condition={'type': 'django', 'value': '1'}))
1110
    st1.items.append(display_form)
1111
    display_form.parent = st1
1112

  
1113
    wf.store()
1114

  
1115
    FormDef.wipe()
1116
    formdef = FormDef()
1117
    formdef.name = 'test'
1118
    formdef.workflow_id = wf.id
1119
    formdef.fields = []
1120
    formdef.store()
1121
    formdef.data_class().wipe()
1122

  
1123
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
1124
    resp = resp.forms[0].submit('submit')
1125
    assert 'Check values then click submit.' in resp.text
1126
    resp = resp.forms[0].submit('submit')
1127
    assert resp.status_int == 302
1128
    resp = resp.follow()
1129
    assert 'The form has been recorded' in resp.text
1130
    assert resp.forms[0]['f2'].value == 'foo@localhost'
1131

  
1132

  
1133
def test_formdata_workflow_form_prefill_checkbox(pub):
1134
    create_user(pub)
1135
    wf = Workflow(name='status')
1136
    st1 = wf.add_status('Status1', 'st1')
1137

  
1138
    display_form = FormWorkflowStatusItem()
1139
    display_form.id = '_x'
1140
    display_form.by = ['_submitter']
1141
    display_form.varname = 'xxx'
1142
    display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
1143
    display_form.formdef.fields.append(fields.BoolField(
1144
        id='1', label='blah',
1145
        type='bool', varname='yyy', prefill={'type': 'formula', 'value': 'True'}))
1146
    display_form.formdef.fields.append(fields.BoolField(
1147
        id='2', label='blah2',
1148
        type='bool', varname='zzz', prefill={'type': 'formula', 'value': 'True'}))
1149
    st1.items.append(display_form)
1150
    display_form.parent = st1
1151

  
1152
    wf.store()
1153

  
1154
    FormDef.wipe()
1155
    formdef = FormDef()
1156
    formdef.name = 'test'
1157
    formdef.workflow_id = wf.id
1158
    formdef.fields = []
1159
    formdef.store()
1160
    formdef.data_class().wipe()
1161

  
1162
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
1163
    resp = resp.form.submit('submit')
1164
    assert 'Check values then click submit.' in resp
1165
    resp = resp.form.submit('submit').follow()
1166
    assert 'The form has been recorded' in resp
1167
    assert resp.form['f1'].checked is True
1168
    assert resp.form['f2'].checked is True
1169
    resp.form['f1'].checked = False
1170
    resp = resp.form.submit('submit')
1171

  
1172
    formdata = formdef.data_class().select()[0]
1173
    assert formdata.workflow_data['xxx_var_yyy_raw'] is False
1174
    assert formdata.workflow_data['xxx_var_zzz_raw'] is True
1175

  
1176

  
1177
def test_formdata_workflow_form_prefill_autocomplete(pub):
1178
    create_user(pub)
1179

  
1180
    NamedDataSource.wipe()
1181
    data_source = NamedDataSource(name='foobar')
1182
    data_source.data_source = {'type': 'json', 'value': 'http://local-mock/test'}
1183
    data_source.query_parameter = 'q'
1184
    data_source.id_parameter = 'id'
1185
    data_source.store()
1186

  
1187
    wf = Workflow(name='status')
1188
    st1 = wf.add_status('Status1', 'st1')
1189

  
1190
    display_form = FormWorkflowStatusItem()
1191
    display_form.id = '_x'
1192
    display_form.by = ['_submitter']
1193
    display_form.varname = 'xxx'
1194
    display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
1195
    display_form.formdef.fields = [
1196
        fields.ItemField(
1197
            id='4', label='string', type='item',
1198
            data_source={'type': 'foobar'},
1199
            required=False,
1200
            display_mode='autocomplete',
1201
            prefill={'type': 'string', 'value': '{{ form_var_foo_raw }}'},
1202
        ),
1203
    ]
1204
    st1.items.append(display_form)
1205
    display_form.parent = st1
1206

  
1207
    wf.store()
1208

  
1209
    FormDef.wipe()
1210
    formdef = FormDef()
1211
    formdef.name = 'test'
1212
    formdef.workflow_id = wf.id
1213
    formdef.fields = [
1214
        fields.ItemField(
1215
            id='0', label='string', type='item',
1216
            data_source={'type': 'foobar'},
1217
            display_mode='autocomplete',
1218
            required=False,
1219
            varname='foo',
1220
        ),
1221
    ]
1222
    formdef.store()
1223
    formdef.data_class().wipe()
1224

  
1225
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
1226
    with mock.patch('wcs.qommon.misc.urlopen') as urlopen:
1227
        data = {'data': [{'id': '1', 'text': 'hello', 'extra': 'foo'}]}
1228

  
1229
        def side_effect(url, *args):
1230
            return StringIO(json.dumps(data))
1231

  
1232
        urlopen.side_effect = side_effect
1233

  
1234
        assert 'data-select2-url=' in resp
1235
        # simulate select2
1236
        resp.form.fields['f0_display'] = Hidden(form=resp.form, tag='input', name='f0_display', pos=10)
1237
        resp.form['f0'].force_value('1')
1238
        resp.form.fields['f0_display'].force_value('foo')
1239
        resp = resp.form.submit('submit')
1240
        assert 'Check values then click submit.' in resp
1241
        resp = resp.form.submit('submit').follow()
1242
        assert 'The form has been recorded' in resp
1243

  
1244
        # check display value is in form action widget
1245
        assert resp.form['f4'].attrs['data-value'] == '1'
1246
        assert resp.form['f4'].attrs['data-initial-display-value'] == 'hello'
1247

  
1248
        # check it is also displayed in a fresh session
1249
        resp = login(get_app(pub), username='foo', password='foo').get(resp.request.url)
1250
        assert resp.form['f4'].attrs['data-value'] == '1'
1251
        assert resp.form['f4'].attrs['data-initial-display-value'] == 'hello'
1252

  
1253

  
1254
def test_formdata_named_wscall(http_requests, pub):
1255
    create_user(pub)
1256
    NamedWsCall.wipe()
1257

  
1258
    wscall = NamedWsCall()
1259
    wscall.name = 'Hello world'
1260
    wscall.request = {'url': 'http://remote.example.net/json'}
1261
    wscall.store()
1262
    assert wscall.slug == 'hello_world'
1263

  
1264
    wf = Workflow(name='status')
1265
    st1 = wf.add_status('Status1', 'st1')
1266
    comment = RegisterCommenterWorkflowStatusItem()
1267
    comment.id = '_comment'
1268
    comment.comment = 'Hello [webservice.hello_world.foo] World'
1269
    st1.items.append(comment)
1270
    comment.parent = st1
1271

  
1272
    display = DisplayMessageWorkflowStatusItem()
1273
    display.message = 'The form has been recorded and: X[webservice.hello_world.foo]Y'
1274
    display.to = []
1275
    st1.items.append(display)
1276
    display.parent = st1
1277

  
1278
    wf.store()
1279

  
1280
    FormDef.wipe()
1281
    formdef = FormDef()
1282
    formdef.name = 'test'
1283
    formdef.workflow_id = wf.id
1284
    formdef.fields = []
1285
    formdef.store()
1286
    formdef.data_class().wipe()
1287

  
1288
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
1289
    resp = resp.forms[0].submit('submit')
1290
    assert 'Check values then click submit.' in resp.text
1291
    resp = resp.forms[0].submit('submit')
1292
    assert resp.status_int == 302
1293
    resp = resp.follow()
1294
    assert 'The form has been recorded and: XbarY' in resp.text
1295

  
1296
    formdata = formdef.data_class().select()[0]
1297
    assert formdata.evolution[0].parts[0].content == 'Hello bar World'
1298

  
1299
    # check with publisher variable in named webservice call
1300
    if not pub.site_options.has_section('variables'):
1301
        pub.site_options.add_section('variables')
1302
    pub.site_options.set('variables', 'example_url', 'http://remote.example.net/')
1303
    with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
1304
        pub.site_options.write(fd)
1305

  
1306
    wscall = NamedWsCall()
1307
    wscall.name = 'Hello world'
1308
    wscall.request = {'url': '[example_url]json'}
1309
    wscall.store()
1310

  
1311
    formdef.data_class().wipe()
1312

  
1313
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
1314
    resp = resp.forms[0].submit('submit')
1315
    assert 'Check values then click submit.' in resp.text
1316
    resp = resp.forms[0].submit('submit')
1317
    assert resp.status_int == 302
1318
    resp = resp.follow()
1319
    assert 'The form has been recorded and: XbarY' in resp.text
1320

  
1321
    formdata = formdef.data_class().select()[0]
1322
    assert formdata.evolution[0].parts[0].content == 'Hello bar World'
1323

  
1324

  
1325
def test_formdata_named_wscall_in_conditions(http_requests, pub):
1326
    create_user(pub)
1327
    NamedWsCall.wipe()
1328

  
1329
    wscall = NamedWsCall()
1330
    wscall.name = 'Hello world'
1331
    wscall.request = {'url': 'http://remote.example.net/json', 'method': 'GET'}
1332
    wscall.store()
1333
    assert wscall.slug == 'hello_world'
1334

  
1335
    FormDef.wipe()
1336
    formdef = FormDef()
1337
    formdef.name = 'test'
1338
    formdef.fields = [
1339
        fields.PageField(id='0', label='1st page', type='page'),
1340
        fields.PageField(
1341
            id='1', label='2nd page', type='page',
1342
            condition={'type': 'python', 'value': 'webservice.hello_world["foo"] == "bar"'}),
1343
        fields.PageField(
1344
            id='1', label='3rd page', type='page',
1345
            condition={'type': 'python', 'value': 'webservice.hello_world["foo"] != "bar"'}),
1346
        fields.PageField(
1347
            id='1', label='4th page', type='page',
1348
            condition={'type': 'python', 'value': 'webservice.hello_world["foo"] == "bar"'}),
1349
    ]
1350
    formdef.store()
1351
    formdef.data_class().wipe()
1352

  
1353
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
1354
    assert '>1st page<' in resp.text
1355
    assert '>2nd page<' in resp.text
1356
    assert '>3rd page<' not in resp.text
1357
    assert '>4th page<' in resp.text
1358
    assert len(http_requests.requests) == 1
0
-