Projet

Général

Profil

0001-workflows-add-live-conditions-to-workflow-form-actio.patch

Frédéric Péters, 05 juin 2019 08:22

Télécharger (36,8 ko)

Voir les différences:

Subject: [PATCH] workflows: add live conditions to workflow form action
 (#32960)

 tests/test_admin_pages.py      |   2 +-
 tests/test_backoffice_pages.py | 113 +++++++++++++++++++++++++++++
 tests/test_form_pages.py       | 116 ++++++++++++++++++++++++++++++
 wcs/fields.py                  |  10 +--
 wcs/formdata.py                |  10 ++-
 wcs/formdef.py                 |  33 +++++++++
 wcs/forms/common.py            | 125 ++++++++++++++++++++++++---------
 wcs/forms/root.py              |  60 +---------------
 wcs/wf/attachment.py           |   2 +-
 wcs/wf/export_to_model.py      |   2 +-
 wcs/wf/form.py                 |  25 ++++---
 wcs/wf/resubmit.py             |   2 +-
 wcs/workflows.py               |  61 +++++++++-------
 13 files changed, 425 insertions(+), 136 deletions(-)
tests/test_admin_pages.py
2586 2586
    assert 'foobar' in resp.body
2587 2587
    resp = resp.click('Edit')
2588 2588
    assert not 'in_listing' in resp.form.fields.keys()
2589
    assert not 'condition$type' in resp.form.fields.keys()
2589
    assert 'condition$type' in resp.form.fields.keys()
2590 2590
    resp = resp.form.submit('cancel')
2591 2591
    resp = resp.follow()
2592 2592
    resp = resp.click('Remove')
tests/test_backoffice_pages.py
3620 3620
    assert formdef.data_class().get(formdata.id).workflow_data == {
3621 3621
            'blah_var_str': 'blah', 'blah_var_radio': 'c', 'blah_var_radio_raw': 'c'}
3622 3622

  
3623
def test_backoffice_workflow_form_with_conditions(pub):
3624
    user = create_user(pub)
3625
    create_environment(pub)
3626

  
3627
    wf = Workflow.get_default_workflow()
3628
    wf.id = '2'
3629
    wf.store()
3630
    wf = Workflow.get(wf.id)
3631
    status = wf.get_status('new')
3632
    status.items = []
3633
    display_form = FormWorkflowStatusItem()
3634
    display_form.id = '_display_form'
3635
    display_form.by = [user.roles[0]]
3636
    display_form.varname = 'blah'
3637
    display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
3638
    display_form.formdef.fields = [
3639
            fields.StringField(id='1', label='Test', varname='str', type='string', required=True),
3640
            fields.StringField(id='2', label='Test2', varname='str2', type='string', required=True),
3641
    ]
3642
    status.items.append(display_form)
3643
    display_form.parent = status
3644

  
3645
    wf.store()
3646
    formdef = FormDef.get_by_urlname('form-title')
3647
    formdef.workflow_id = wf.id
3648
    formdef.fields[0].varname = 'plop'
3649
    formdef.store()
3650

  
3651
    for formdata in formdef.data_class().select():
3652
        if formdata.status == 'wf-new':
3653
            break
3654
    app = login(get_app(pub))
3655
    resp = app.get(formdata.get_url(backoffice=True))
3656
    assert 'f1' in resp.form.fields
3657
    assert 'f2' in resp.form.fields
3658

  
3659
    # check with static condition
3660
    display_form.formdef.fields = [
3661
            fields.StringField(id='1', label='Test', varname='str', type='string', required=True),
3662
            fields.StringField(id='2', label='Test2', varname='str2',
3663
                type='string', required=True,
3664
                condition={'type': 'django', 'value': '0'}),
3665
    ]
3666
    wf.store()
3667

  
3668
    resp = login(get_app(pub)).get(formdata.get_url(backoffice=True))
3669
    assert 'f1' in resp.form.fields
3670
    assert 'f2' not in resp.form.fields
3671

  
3672
    # check condition based on formdata
3673
    display_form.formdef.fields = [
3674
            fields.StringField(id='1', label='Test', varname='str', type='string', required=True),
3675
            fields.StringField(id='2', label='Test2', varname='str2',
3676
                type='string', required=True,
3677
                condition={'type': 'django', 'value': 'form_var_plop'}),
3678
    ]
3679
    wf.store()
3680

  
3681
    resp = login(get_app(pub)).get(formdata.get_url(backoffice=True))
3682
    assert 'f1' in resp.form.fields
3683
    assert 'f2' in resp.form.fields
3684

  
3685
    display_form.formdef.fields = [
3686
            fields.StringField(id='1', label='Test', varname='str', type='string', required=True),
3687
            fields.StringField(id='2', label='Test2', varname='str2',
3688
                type='string', required=True,
3689
                condition={'type': 'django', 'value': 'form_var_plop != "xxx"'}),
3690
    ]
3691
    wf.store()
3692

  
3693
    resp = login(get_app(pub)).get(formdata.get_url(backoffice=True))
3694
    assert 'f1' in resp.form.fields
3695
    assert 'f2' in resp.form.fields
3696

  
3697
    # check with live conditions
3698
    display_form.formdef.fields = [
3699
            fields.StringField(id='1', label='Test', varname='str', type='string', required=True),
3700
            fields.StringField(id='2', label='Test2', varname='str2',
3701
                type='string', required=True,
3702
                condition={'type': 'django', 'value': 'blah_var_str == "xxx"'}),
3703
    ]
3704
    wf.store()
3705

  
3706
    resp = login(get_app(pub)).get(formdata.get_url(backoffice=True))
3707
    assert 'f1' in resp.form.fields
3708
    assert 'f2' in resp.form.fields
3709
    assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
3710
    assert resp.html.find('div', {'data-field-id': '2'}).attrs.get('style') == 'display: none'
3711
    live_url = resp.html.find('form').attrs['data-live-url']
3712
    resp.form['f1'] = ''
3713
    live_resp = app.post(live_url, params=resp.form.submit_fields())
3714
    assert live_resp.json['result']['1']['visible']
3715
    assert not live_resp.json['result']['2']['visible']
3716

  
3717
    resp.form['f1'] = 'xxx'
3718
    live_resp = app.post(live_url, params=resp.form.submit_fields())
3719
    assert live_resp.json['result']['1']['visible']
3720
    assert live_resp.json['result']['2']['visible']
3721

  
3722
    # check submit doesn't work
3723
    resp = resp.form.submit('submit')
3724
    assert 'There were errors processing your form.' in resp.body
3725

  
3726
    resp.form['f1'] = 'xxx2'
3727
    live_resp = app.post(live_url, params=resp.form.submit_fields())
3728
    assert live_resp.json['result']['1']['visible']
3729
    assert not live_resp.json['result']['2']['visible']
3730

  
3731
    # check submit does work when second field is hidden
3732
    resp = resp.form.submit('submit').follow()
3733

  
3734
    assert formdef.data_class().get(formdata.id).workflow_data == {'blah_var_str': 'xxx2', 'blah_var_str2': None}
3735

  
3623 3736
def test_backoffice_criticality_in_formdef_listing(pub):
3624 3737
    if not pub.is_using_postgresql():
3625 3738
        pytest.skip('this requires SQL')
tests/test_form_pages.py
6465 6465
    assert 'You already started to fill this form.' in resp.body
6466 6466
    assert 'href="%s"' % draft.id in resp.body
6467 6467
    assert 'href="%s"' % draft2.id in resp.body
6468

  
6469
def test_frontoffice_workflow_form_with_conditions(pub):
6470
    user = create_user(pub)
6471
    wf = Workflow.get_default_workflow()
6472
    wf.id = '2'
6473
    wf.store()
6474
    wf = Workflow.get(wf.id)
6475
    status = wf.get_status('new')
6476
    status.items = []
6477
    display_form = FormWorkflowStatusItem()
6478
    display_form.id = '_display_form'
6479
    display_form.by = ['_submitter']
6480
    display_form.varname = 'blah'
6481
    display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
6482
    display_form.formdef.fields = [
6483
            fields.StringField(id='1', label='Test', varname='str', type='string', required=True),
6484
            fields.StringField(id='2', label='Test2', varname='str2', type='string', required=True),
6485
    ]
6486
    status.items.append(display_form)
6487
    display_form.parent = status
6488

  
6489
    wf.store()
6490
    formdef = create_formdef()
6491
    formdef.workflow_id = wf.id
6492
    formdef.fields = [fields.StringField(id='0', label='string', varname='plop')]
6493
    formdef.store()
6494

  
6495
    formdef.data_class().wipe()
6496

  
6497
    formdata = formdef.data_class()()
6498
    formdata.user_id = user.id
6499
    formdata.status = 'wf-new'
6500
    formdata.data = {'0': 'plop'}
6501
    formdata.store()
6502

  
6503
    app = login(get_app(pub), username='foo', password='foo')
6504
    resp = app.get(formdata.get_url(backoffice=False))
6505
    assert 'f1' in resp.form.fields
6506
    assert 'f2' in resp.form.fields
6507

  
6508
    # check with static condition
6509
    display_form.formdef.fields = [
6510
            fields.StringField(id='1', label='Test', varname='str', type='string', required=True),
6511
            fields.StringField(id='2', label='Test2', varname='str2',
6512
                type='string', required=True,
6513
                condition={'type': 'django', 'value': '0'}),
6514
    ]
6515
    wf.store()
6516

  
6517
    resp = login(get_app(pub), username='foo', password='foo').get(formdata.get_url(backoffice=False))
6518
    assert 'f1' in resp.form.fields
6519
    assert 'f2' not in resp.form.fields
6520

  
6521
    # check condition based on formdata
6522
    display_form.formdef.fields = [
6523
            fields.StringField(id='1', label='Test', varname='str', type='string', required=True),
6524
            fields.StringField(id='2', label='Test2', varname='str2',
6525
                type='string', required=True,
6526
                condition={'type': 'django', 'value': 'form_var_plop'}),
6527
    ]
6528
    wf.store()
6529

  
6530
    resp = login(get_app(pub), username='foo', password='foo').get(formdata.get_url(backoffice=False))
6531
    assert 'f1' in resp.form.fields
6532
    assert 'f2' in resp.form.fields
6533

  
6534
    display_form.formdef.fields = [
6535
            fields.StringField(id='1', label='Test', varname='str', type='string', required=True),
6536
            fields.StringField(id='2', label='Test2', varname='str2',
6537
                type='string', required=True,
6538
                condition={'type': 'django', 'value': 'form_var_plop != "xxx"'}),
6539
    ]
6540
    wf.store()
6541

  
6542
    resp = login(get_app(pub), username='foo', password='foo').get(formdata.get_url(backoffice=False))
6543
    assert 'f1' in resp.form.fields
6544
    assert 'f2' in resp.form.fields
6545

  
6546
    # check with live conditions
6547
    display_form.formdef.fields = [
6548
            fields.StringField(id='1', label='Test', varname='str', type='string', required=True),
6549
            fields.StringField(id='2', label='Test2', varname='str2',
6550
                type='string', required=True,
6551
                condition={'type': 'django', 'value': 'blah_var_str == "xxx"'}),
6552
    ]
6553
    wf.store()
6554

  
6555
    resp = login(get_app(pub), username='foo', password='foo').get(formdata.get_url(backoffice=False))
6556
    assert 'f1' in resp.form.fields
6557
    assert 'f2' in resp.form.fields
6558
    assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
6559
    assert resp.html.find('div', {'data-field-id': '2'}).attrs.get('style') == 'display: none'
6560
    live_url = resp.html.find('form').attrs['data-live-url']
6561
    resp.form['f1'] = ''
6562
    live_resp = app.post(live_url, params=resp.form.submit_fields())
6563
    assert live_resp.json['result']['1']['visible']
6564
    assert not live_resp.json['result']['2']['visible']
6565

  
6566
    resp.form['f1'] = 'xxx'
6567
    live_resp = app.post(live_url, params=resp.form.submit_fields())
6568
    assert live_resp.json['result']['1']['visible']
6569
    assert live_resp.json['result']['2']['visible']
6570

  
6571
    # check submit doesn't work
6572
    resp = resp.form.submit('submit')
6573
    assert 'There were errors processing your form.' in resp.body
6574

  
6575
    resp.form['f1'] = 'xxx2'
6576
    live_resp = app.post(live_url, params=resp.form.submit_fields())
6577
    assert live_resp.json['result']['1']['visible']
6578
    assert not live_resp.json['result']['2']['visible']
6579

  
6580
    # check submit does work when second field is hidden
6581
    resp = resp.form.submit('submit').follow()
6582

  
6583
    assert formdef.data_class().get(formdata.id).workflow_data == {'blah_var_str': 'xxx2', 'blah_var_str2': None}
wcs/fields.py
403 403
        except RuntimeError:
404 404
            return True
405 405

  
406
    def get_referenced_varnames(self, value):
407
        return re.findall(r'\bform[_\.]var[_\.]([a-zA-Z0-9_]+?)(?:_raw|\b)', value or '')
406
    def get_referenced_varnames(self, formdef, value):
407
        return re.findall(r'\b%s[_\.]var[_\.]([a-zA-Z0-9_]+?)(?:_raw|\b)' % formdef.var_prefix, value or '')
408 408

  
409
    def get_condition_varnames(self):
410
        return self.get_referenced_varnames(self.condition['value'])
409
    def get_condition_varnames(self, formdef):
410
        return self.get_referenced_varnames(formdef, self.condition['value'])
411 411

  
412 412
    def has_live_conditions(self, formdef):
413
        varnames = self.get_condition_varnames()
413
        varnames = self.get_condition_varnames(formdef)
414 414
        if not varnames:
415 415
            return False
416 416
        field_position = formdef.fields.index(self)
wcs/formdata.py
525 525
            last_seen_author = evolution_part.who or last_seen_author
526 526
            yield evolution_part
527 527

  
528
    def get_workflow_form(self, user):
528
    def get_workflow_form(self, user, displayed_fields=None):
529 529
        wf_status = self.get_status()
530 530
        if not wf_status:
531 531
            return None
532
        return wf_status.get_action_form(self, user)
532
        return wf_status.get_action_form(self, user, displayed_fields=displayed_fields)
533 533

  
534 534
    def handle_workflow_form(self, user, form):
535 535
        wf_status = self.get_status()
......
537 537
            return None
538 538
        return wf_status.handle_form(form, self, user)
539 539

  
540
    def evaluate_live_workflow_form(self, user, form):
541
        wf_status = self.get_status()
542
        if not wf_status:
543
            return None
544
        wf_status.evaluate_live_form(form, self, user)
545

  
540 546
    def pop_previous_marked_status(self):
541 547
        if not self.workflow_data or not '_markers_stack' in self.workflow_data:
542 548
            return None
wcs/formdef.py
110 110
    # store fields in a separate pickle chunk
111 111
    lightweight = True
112 112

  
113
    # prefix for formdata variables
114
    var_prefix = 'form'
115

  
113 116
    # declarations for serialization
114 117
    TEXT_ATTRIBUTES = ['name', 'url_name', 'description', 'keywords',
115 118
            'publication_date', 'expiration_date', 'internal_identifier',
......
627 630

  
628 631
        return form
629 632

  
633
    def set_live_condition_sources(self, form, fields):
634
        live_condition_fields = {}
635
        for field in fields:
636
            if field.condition:
637
                field.varnames = field.get_condition_varnames(formdef=self)
638
                for varname in field.varnames:
639
                    if not varname in live_condition_fields:
640
                        live_condition_fields[varname] = []
641
                    live_condition_fields[varname].append(field)
642
            if field.key == 'item' and field.data_source:
643
                real_data_source = data_sources.get_real(field.data_source)
644
                if real_data_source.get('type') != 'json':
645
                    continue
646
                varnames = field.get_referenced_varnames(
647
                        formdef=self,
648
                        value=real_data_source.get('value'))
649
                for varname in varnames:
650
                    if not varname in live_condition_fields:
651
                        live_condition_fields[varname] = []
652
                    live_condition_fields[varname].append(field)
653
            if field.key == 'comment':
654
                for varname in field.get_referenced_varnames(formdef=self, value=field.label):
655
                    if not varname in live_condition_fields:
656
                        live_condition_fields[varname] = []
657
                    live_condition_fields[varname].append(field)
658

  
659
        for field in fields:
660
            if field.varname in live_condition_fields:
661
                form.get_widget('f%s' % field.id).live_condition_source = True
662

  
630 663
    def get_field_data(self, field, widget):
631 664
        d = {}
632 665
        d[field.id] = widget.parse()
wcs/forms/common.py
21 21
from quixote.directory import Directory
22 22
from quixote.html import TemplateIO, htmltext
23 23

  
24
from wcs import data_sources
24 25
from wcs.api_utils import get_user_from_api_query_string, is_url_signed
25 26
from wcs.fields import WidgetField, FileField
26 27
from wcs.workflows import EditableWorkflowStatusItem
......
114 115

  
115 116

  
116 117
class FormStatusPage(Directory, FormTemplateMixin):
117
    _q_exports_orig = ['', 'download', 'json', 'action']
118
    _q_exports_orig = ['', 'download', 'json', 'action', 'live']
118 119
    _q_extra_exports = []
119 120
    form_page_class = None
120 121

  
......
238 239
        get_logger().info('form %s - id: %s - view' % (self.formdef.name, self.filled.id))
239 240

  
240 241
        user = get_request().user
241
        form = self.filled.get_workflow_form(user)
242
        if form and form.is_submitted():
243
            if not form.has_errors():
244
                url = self.submit(form, comment_only = True)
245
            if not form.has_errors():
246
                if url is None:
247
                    url = get_request().get_frontoffice_url()
248
                response = get_response()
249
                response.set_status(303)
250
                response.headers[str('location')] = url
251
                response.content_type = 'text/plain'
252
                return "Your browser should redirect you"
242
        form = self.get_workflow_form(user)
243
        response = self.check_submitted_form(form)
244
        if response:
245
            return response
253 246

  
254 247
        if form:
255 248
            form.add_media()
......
267 260
                templates=list(self.get_formdef_template_variants(self.status_templates)),
268 261
                context=context)
269 262

  
263
    def get_workflow_form(self, user):
264
        submitted_fields = []
265
        form = self.filled.get_workflow_form(user, displayed_fields=submitted_fields)
266
        if form:
267
            form.attrs['data-live-url'] = self.filled.get_url() + 'live'
268
        if form and form.is_submitted():
269
            with get_publisher().substitutions.temporary_feed(self.filled, force_mode='lazy'):
270
                # remove fields that could be required but are not visible
271
                self.filled.evaluate_live_workflow_form(user, form)
272
                get_publisher().substitutions.feed(self.filled)
273
                for field in submitted_fields:
274
                    if not field.is_visible(self.filled.data, self.formdef) and 'f%s' % field.id in form._names:
275
                        del form._names['f%s' % field.id]
276
        return form
277

  
278

  
279
    def check_submitted_form(self, form):
280
        if form and form.is_submitted() and not form.has_errors():
281
            url = self.submit(form)
282
            if url is None:
283
                url = get_request().get_frontoffice_url()
284
            response = get_response()
285
            response.set_status(303)
286
            response.headers[str('location')] = url
287
            response.content_type = 'text/plain'
288
            return "Your browser should redirect you"
289

  
290

  
270 291
    def export_to_json(self, anonymise=False):
271 292
        get_response().set_content_type('application/json')
272 293
        return self.filled.export_to_json(anonymise=anonymise)
......
467 488
            return redirect('./#lock-notice')
468 489

  
469 490
        user = self.check_receiver()
470
        form = None
471

  
472
        try:
473
            form = self.filled.get_workflow_form(user)
474
        except:
475
            # XXX: probably because there are mixed forms, with and without
476
            # workflow; send a trace nevertheless.
477
            get_publisher().notify_of_exception(sys.exc_info(), context='[BACKOFFICE]')
478
            form = Form()
479

  
480
        if form and form.is_submitted() and not form.has_errors():
481
            url = self.submit(form)
491
        form = self.get_workflow_form(user)
492
        response = self.check_submitted_form(form)
493
        if response:
482 494
            get_session().unmark_visited_object(object_key)
483
            if url is None:
484
                url = get_request().get_frontoffice_url()
485
            response = get_response()
486
            response.set_status(303)
487
            response.headers[str('location')] = url
488
            response.content_type = 'text/plain'
489
            return "Your browser should redirect you"
495
            return response
490 496

  
491 497
        get_logger().info('form %s - id: %s - view status' % (self.formdef.name, self.filled.id))
492 498
        get_response().add_javascript(['jquery.js', 'qommon.forms.js'])
......
566 572
        r += htmltext('<a href="..">%s</a>') % _('Back to Listing')
567 573
        return r.getvalue()
568 574

  
569
    def submit(self, form, comment_only = False):
575
    def submit(self, form):
570 576
        current_status = self.filled.status
571 577
        user = get_request().user
572 578
        next_url = self.filled.handle_workflow_form(user, form)
......
612 618
            file_url += file.base_filename
613 619
        return redirect(file_url)
614 620

  
621
    @classmethod
622
    def live_process_fields(cls, form, formdata, displayed_fields):
623
        result = {}
624
        for field in displayed_fields:
625
            result[field.id] = {'visible': field.is_visible(formdata.data, formdata.formdef)}
626

  
627
        modified_field_varname = None
628
        for field in displayed_fields:
629
            if field.id == get_request().form.get('modified_field_id'):
630
                modified_field_varname = field.varname
631

  
632
        for field in displayed_fields:
633
            if field.key == 'item' and field.data_source:
634
                data_source = data_sources.get_object(field.data_source)
635
                if data_source.type != 'json':
636
                    continue
637
                varnames = field.get_referenced_varnames(
638
                        formdef=formdata.formdef,
639
                        value=data_source.data_source.get('value'))
640
                if (modified_field_varname is None or modified_field_varname in varnames) and (
641
                        field.display_mode == 'autocomplete' and data_source.query_parameter):
642
                    # computed earlier, in perform_more_widget_changes, when the field
643
                    # was added to the form
644
                    result[field.id]['source_url'] = field.url
645
                if modified_field_varname in varnames:
646
                    result[field.id]['items'] = [
647
                            {'id': x[2], 'text': x[1]} for x in field.get_options(mode='lazy')]
648
        for widget in form.widgets:
649
            if not getattr(widget, 'field', None):
650
                continue
651
            if widget.field.key == 'comment':
652
                result[widget.field.id]['content'] = widget.content
653

  
654
        return json.dumps({'result': result})
655

  
656
    def live(self):
657
        get_request().ignore_session = True
658
        # live evaluation of fields
659
        get_response().set_content_type('application/json')
660
        def result_error(reason):
661
            return json.dumps({'result': 'error', 'reason': reason})
662

  
663
        session = get_session()
664
        if not session:
665
            return result_error('missing session')
666

  
667
        displayed_fields = []
668
        user = get_request().user
669
        form = self.filled.get_workflow_form(user, displayed_fields=displayed_fields)
670
        self.filled.evaluate_live_workflow_form(user, form)
671
        get_publisher().substitutions.feed(self.filled)
672
        return self.live_process_fields(form, self.filled, displayed_fields)
673

  
615 674
    def _q_lookup(self, component):
616 675
        if component == 'files':
617 676
            self.check_receiver()
wcs/forms/root.py
386 386
                # always set additional attributes as they will be used for
387 387
                # "live prefill", regardless of existing data.
388 388
                form.get_widget('f%s' % field.id).prefill_attributes = field.get_prefill_attributes()
389
            if field.condition:
390
                field.varnames = field.get_condition_varnames()
391
                for varname in field.varnames:
392
                    if not varname in live_condition_fields:
393
                        live_condition_fields[varname] = []
394
                    live_condition_fields[varname].append(field)
395
            if field.key == 'item' and field.data_source:
396
                real_data_source = data_sources.get_real(field.data_source)
397
                if real_data_source.get('type') != 'json':
398
                    continue
399
                varnames = re.findall(r'\bform[_\.]var[_\.]([a-zA-Z0-9_]+?)(?:_raw|\b)',
400
                        real_data_source.get('value'))
401
                for varname in varnames:
402
                    if not varname in live_condition_fields:
403
                        live_condition_fields[varname] = []
404
                    live_condition_fields[varname].append(field)
405
            if field.key == 'comment':
406
                for varname in field.get_referenced_varnames(field.label):
407
                    if not varname in live_condition_fields:
408
                        live_condition_fields[varname] = []
409
                    live_condition_fields[varname].append(field)
410 389

  
411
        for field in displayed_fields:
412
            if field.varname in live_condition_fields:
413
                form.get_widget('f%s' % field.id).live_condition_source = True
390
        self.formdef.set_live_condition_sources(form, displayed_fields)
414 391

  
415 392
        self.html_top(self.formdef.name)
416 393

  
......
1105 1082
                    displayed_fields=displayed_fields,
1106 1083
                    transient_formdata=formdata)
1107 1084
        formdata.data.update(self.formdef.get_data(form))
1108

  
1109
        result = {}
1110
        for field in displayed_fields:
1111
            result[field.id] = {'visible': field.is_visible(formdata.data, self.formdef)}
1112

  
1113
        modified_field_varname = None
1114
        for field in displayed_fields:
1115
            if field.id == get_request().form.get('modified_field_id'):
1116
                modified_field_varname = field.varname
1117

  
1118
        for field in displayed_fields:
1119
            if field.key == 'item' and field.data_source:
1120
                data_source = data_sources.get_object(field.data_source)
1121
                if data_source.type != 'json':
1122
                    continue
1123
                varnames = re.findall(r'\bform[_\.]var[_\.]([a-zA-Z0-9_]+?)(?:_raw|\b)',
1124
                        data_source.data_source.get('value'))
1125
                if (modified_field_varname is None or modified_field_varname in varnames) and (
1126
                        field.display_mode == 'autocomplete' and data_source.query_parameter):
1127
                    # computed earlier, in perform_more_widget_changes, when the field
1128
                    # was added to the form
1129
                    result[field.id]['source_url'] = field.url
1130
                if modified_field_varname in varnames:
1131
                    result[field.id]['items'] = [
1132
                            {'id': x[2], 'text': x[1]} for x in field.get_options(mode='lazy')]
1133
        for widget in form.widgets:
1134
            if not getattr(widget, 'field', None):
1135
                continue
1136
            if widget.field.key == 'comment':
1137
                result[widget.field.id]['content'] = widget.content
1138

  
1139
        return json.dumps({'result': result})
1085
        return FormStatusPage.live_process_fields(form, formdata, displayed_fields)
1140 1086

  
1141 1087
    def submitted(self, form, existing_formdata = None):
1142 1088
        if existing_formdata: # modifying
......
1678 1624

  
1679 1625

  
1680 1626
class PublicFormStatusPage(FormStatusPage):
1681
    _q_exports_orig = ['', 'download', 'status']
1627
    _q_exports_orig = ['', 'download', 'status', 'live']
1682 1628
    form_page_class = FormPage
1683 1629
    history_templates = ['wcs/front/formdata_history.html', 'wcs/formdata_history.html']
1684 1630
    status_templates = ['wcs/front/formdata_status.html', 'wcs/formdata_status.html']
wcs/wf/attachment.py
100 100
        else:
101 101
            return _('not completed')
102 102

  
103
    def fill_form(self, form, formdata, user):
103
    def fill_form(self, form, formdata, user, **kwargs):
104 104
        if self.display_title:
105 105
            title = self.title or _('Upload File')
106 106
        else:
wcs/wf/export_to_model.py
207 207
        else:
208 208
            return _('no model set')
209 209

  
210
    def fill_form(self, form, formdata, user):
210
    def fill_form(self, form, formdata, user, **kwargs):
211 211
        if not self.method == 'interactive':
212 212
            return
213 213
        label = self.label
wcs/wf/form.py
56 56

  
57 57
class WorkflowFormFieldDefPage(FieldDefPage):
58 58
    section = 'workflows'
59
    blacklisted_attributes = ['condition', 'in_listing']
59
    blacklisted_attributes = ['in_listing']
60 60

  
61 61
    def get_deletion_extra_warning(self):
62 62
        return None
......
66 66
    section = 'workflows'
67 67
    support_import = False
68 68
    blacklisted_types = ['page']
69
    blacklisted_attributes = ['condition']
70 69
    field_def_page_class = WorkflowFormFieldDefPage
71 70

  
72 71

  
......
154 153
            return fields_directory
155 154
        return None
156 155

  
157
    def fill_form(self, form, formdata, user):
156
    def fill_form(self, form, formdata, user, displayed_fields=None, **kwargs):
158 157
        if not self.formdef:
159 158
            return
160
        self.formdef.add_fields_to_form(form)
159
        self.formdef.var_prefix = self.varname
160
        self.formdef.add_fields_to_form(form, displayed_fields=displayed_fields)
161 161
        form.add_submit('submit', _('Submit'))
162 162

  
163 163
        # put varname in a form attribute so it can be used in templates to
......
167 167
        formdata.feed_session()
168 168

  
169 169
        req = get_request()
170
        self.formdef.set_live_condition_sources(form, self.formdef.fields)
171

  
170 172
        for field in self.formdef.fields:
171 173
            if ('f%s' % field.id) in req.form:
172 174
                continue
......
187 189
                form.get_widget('f%s' % field.id).set_value(v)
188 190
                req.form['f%s' % field.id] = v
189 191

  
192
    def evaluate_live_form(self, form, formdata, user):
193
        workflow_data = {}
194
        for k, v in get_dict_with_varnames(
195
                        self.formdef.fields, self.formdef.get_data(form),
196
                        varnames_only=True).items():
197
            workflow_data['%s_%s' % (self.varname, k)] = v
198
        formdata.update_workflow_data(workflow_data)
199

  
190 200
    def submit_form(self, form, formdata, user, evo):
191 201
        if not self.formdef:
192 202
            return
193 203
        if form.get_submit() == 'submit' and not form.has_errors():
194
            workflow_data = {}
195
            for k, v in get_dict_with_varnames(
196
                            self.formdef.fields, self.formdef.get_data(form),
197
                            varnames_only=True).items():
198
                workflow_data['%s_%s' % (self.varname, k)] = v
199
            formdata.update_workflow_data(workflow_data)
204
            self.evaluate_live_form(form, formdata, user)
200 205
            formdata.store()
201 206

  
202 207
    def get_parameters_view(self):
wcs/wf/resubmit.py
46 46
        else:
47 47
            return _('not completed')
48 48

  
49
    def fill_form(self, form, formdata, user):
49
    def fill_form(self, form, formdata, user, **kwargs):
50 50
        label = self.label
51 51
        if not label:
52 52
            label = _('Resubmit')
wcs/workflows.py
1352 1352
                return item
1353 1353
        raise KeyError()
1354 1354

  
1355
    def get_action_form(self, filled, user):
1355
    def get_action_form(self, filled, user, displayed_fields=None):
1356 1356
        form = Form(enctype='multipart/form-data', use_tokens=False)
1357 1357
        form.attrs['id'] = 'wf-actions'
1358 1358
        for item in self.items:
......
1360 1360
                continue
1361 1361
            if not item.check_condition(filled):
1362 1362
                continue
1363
            item.fill_form(form, filled, user)
1363
            item.fill_form(form, filled, user, displayed_fields=displayed_fields)
1364 1364

  
1365 1365
        for action in filled.formdef.workflow.get_global_actions_for_user(filled, user):
1366 1366
            form.add_submit('button-action-%s' % action.id, action.name)
......
1372 1372
        else:
1373 1373
            return None
1374 1374

  
1375
    def handle_form(self, form, filled, user):
1376
        # check for global actions
1377
        for action in filled.formdef.workflow.get_global_actions_for_user(filled, user):
1378
            if 'button-action-%s' % action.id in get_request().form:
1379
                url = perform_items(action.items, filled)
1380
                if url:
1381
                    return url
1382
                return
1383

  
1384
        evo = Evolution()
1385
        evo.time = time.localtime()
1386
        if user:
1387
            if filled.is_submitter(user):
1388
                evo.who = '_submitter'
1389
            else:
1390
                evo.who = user.id
1391
        if not filled.evolution:
1392
            filled.evolution = []
1393

  
1375
    def get_active_items(self, form, filled, user):
1394 1376
        for item in self.items:
1395 1377
            if hasattr(item, 'by'):
1396 1378
                for role in item.by or []:
......
1410 1392
                    continue
1411 1393
            if not item.check_condition(filled):
1412 1394
                continue
1395
            yield item
1396

  
1397
    def evaluate_live_form(self, form, filled, user):
1398
        for item in self.get_active_items(form, filled, user):
1399
            item.evaluate_live_form(form, filled, user)
1400

  
1401
    def handle_form(self, form, filled, user):
1402
        # check for global actions
1403
        for action in filled.formdef.workflow.get_global_actions_for_user(filled, user):
1404
            if 'button-action-%s' % action.id in get_request().form:
1405
                url = perform_items(action.items, filled)
1406
                if url:
1407
                    return url
1408
                return
1409

  
1410
        evo = Evolution()
1411
        evo.time = time.localtime()
1412
        if user:
1413
            if filled.is_submitter(user):
1414
                evo.who = '_submitter'
1415
            else:
1416
                evo.who = user.id
1417
        if not filled.evolution:
1418
            filled.evolution = []
1419

  
1420
        for item in self.get_active_items(form, filled, user):
1413 1421
            next_url = item.submit_form(form, filled, user, evo)
1414 1422
            if next_url is True:
1415 1423
                break
......
1611 1619
    def perform(self, formdata):
1612 1620
        pass
1613 1621

  
1614
    def fill_form(self, form, formdata, user):
1622
    def fill_form(self, form, formdata, user, **kwargs):
1623
        pass
1624

  
1625
    def evaluate_live_form(self, form, formdata, user):
1615 1626
        pass
1616 1627

  
1617 1628
    def submit_form(self, form, formdata, user, evo):
......
2005 2016
        else:
2006 2017
            return _('not completed')
2007 2018

  
2008
    def fill_form(self, form, formdata, user):
2019
    def fill_form(self, form, formdata, user, **kwargs):
2009 2020
        if not 'comment' in [x.name for x in form.widgets]:
2010 2021
            if self.label is None:
2011 2022
                title = _('Comment')
......
2144 2155
        else:
2145 2156
            return _('not completed')
2146 2157

  
2147
    def fill_form(self, form, formdata, user):
2158
    def fill_form(self, form, formdata, user, **kwargs):
2148 2159
        label = self.compute(self.label)
2149 2160
        if not label:
2150 2161
            return
......
2727 2738
        else:
2728 2739
            return _('not completed')
2729 2740

  
2730
    def fill_form(self, form, formdata, user):
2741
    def fill_form(self, form, formdata, user, **kwargs):
2731 2742
        label = self.label
2732 2743
        if not label:
2733 2744
            label = _('Edit Form')
2734
-