Projet

Général

Profil

0001-forms-don-t-let-conditional-pages-alter-evaluation-o.patch

Frédéric Péters, 12 octobre 2018 09:32

Télécharger (4,62 ko)

Voir les différences:

Subject: [PATCH] forms: don't let conditional pages alter evaluation of field
 visibility (#27247)

 tests/test_form_pages.py   | 44 ++++++++++++++++++++++++++++++++++++++
 wcs/forms/root.py          | 15 ++++++++-----
 wcs/qommon/substitution.py |  8 +++++++
 3 files changed, 62 insertions(+), 5 deletions(-)
tests/test_form_pages.py
5298 5298
    assert '<span class="label">Bar</span>' in resp.body
5299 5299
    assert '<span class="label">Foo</span>' in resp.body
5300 5300

  
5301
def test_field_condition_on_required_field(pub):
5302
    # from https://dev.entrouvert.org/issues/27247
5303
    FormDef.wipe()
5304
    formdef = FormDef()
5305
    formdef.name = 'Foo'
5306
    formdef.fields = [
5307
        fields.PageField(id='0', label='1st page', type='page'),
5308
        fields.ItemField(type='item', id='1', label='Bar',
5309
            items=['oui', 'non'],
5310
            show_as_radio=True,
5311
            required=True, varname='bar'),
5312
        fields.ItemField(type='item', id='2', label='Foo', size='40',
5313
            required=True,
5314
            hint='---',
5315
            items=['plop'],
5316
            condition={'type': 'django', 'value': 'form_var_bar == "oui"'}),
5317
        fields.PageField(id='3', label='1st page', type='page',
5318
            condition={'type': 'python', 'value': 'True'}),
5319
        fields.CommentField(type='comment', id='4', label='HELLO!'),
5320
    ]
5321
    formdef.store()
5322

  
5323
    app = get_app(pub)
5324
    resp = app.get('/foo/')
5325
    assert 'f1' in resp.form.fields
5326
    assert 'f2' in resp.form.fields
5327
    assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
5328
    assert resp.html.find('div', {'data-field-id': '2'}).attrs.get('style') == 'display: none'
5329
    resp.form['f1'] = 'non'
5330
    live_resp = app.post('/foo/live', params=resp.form.submit_fields())
5331
    assert live_resp.json['result']['1']['visible']
5332
    assert not live_resp.json['result']['2']['visible']
5333
    resp = resp.form.submit('submit')
5334
    assert 'HELLO' in resp.body
5335
    resp = resp.form.submit('previous')
5336
    resp.form['f1'] = 'oui'
5337
    live_resp = app.post('/foo/live', params=resp.form.submit_fields())
5338
    assert live_resp.json['result']['1']['visible']
5339
    assert live_resp.json['result']['2']['visible']
5340
    print 'HELLO!!!!'
5341
    resp = resp.form.submit('submit')
5342
    assert '<div class="error">required field</div>' in resp.body
5343
    assert 'HELLO' not in resp.body
5344

  
5301 5345
def test_field_live_condition_multipages(pub):
5302 5346
    FormDef.wipe()
5303 5347
    formdef = FormDef()
wcs/forms/root.py
524 524
        current_data = self.get_transient_formdata().data
525 525
        pages = []
526 526
        field_page = None
527
        for field in self.formdef.fields:
528
            if field.type == 'page':
529
                field_page = field
530
                if field.is_visible(current_data, self.formdef):
531
                    pages.append(field)
527
        with get_publisher().substitutions.freeze():
528
            # don't let evaluation of pages alter substitution variables (this
529
            # avoids a ConditionVars being added with current form data and
530
            # influencing later code evaluating field visibility based on
531
            # submitted data) (#27247).
532
            for field in self.formdef.fields:
533
                if field.type == 'page':
534
                    field_page = field
535
                    if field.is_visible(current_data, self.formdef):
536
                        pages.append(field)
532 537
        if not field_page:  # form without page fields
533 538
            pages = [None]
534 539
        self._pages = pages
wcs/qommon/substitution.py
69 69
            self.sources.append(source)
70 70
            self.invalidate_cache()
71 71

  
72
    @contextmanager
73
    def freeze(self):
74
        orig_sources, self.sources = self.sources, self.sources[:]
75
        self.invalidate_cache()
76
        yield
77
        self.sources = orig_sources
78
        self.invalidate_cache()
79

  
72 80
    @contextmanager
73 81
    def temporary_feed(self, source, force_mode=None):
74 82
        if source is None or source in self.sources:
75
-