forms: don't let conditional pages alter evaluation of field visibility (#27247)
This commit is contained in:
parent
929e23c2d0
commit
25a033b728
|
@ -5270,6 +5270,49 @@ def test_field_live_condition(pub):
|
|||
assert '<span class="label">Bar</span>' in resp.body
|
||||
assert '<span class="label">Foo</span>' in resp.body
|
||||
|
||||
def test_field_condition_on_required_field(pub):
|
||||
# from https://dev.entrouvert.org/issues/27247
|
||||
FormDef.wipe()
|
||||
formdef = FormDef()
|
||||
formdef.name = 'Foo'
|
||||
formdef.fields = [
|
||||
fields.PageField(id='0', label='1st page', type='page'),
|
||||
fields.ItemField(type='item', id='1', label='Bar',
|
||||
items=['oui', 'non'],
|
||||
show_as_radio=True,
|
||||
required=True, varname='bar'),
|
||||
fields.ItemField(type='item', id='2', label='Foo', size='40',
|
||||
required=True,
|
||||
hint='---',
|
||||
items=['plop'],
|
||||
condition={'type': 'django', 'value': 'form_var_bar == "oui"'}),
|
||||
fields.PageField(id='3', label='1st page', type='page',
|
||||
condition={'type': 'python', 'value': 'True'}),
|
||||
fields.CommentField(type='comment', id='4', label='HELLO!'),
|
||||
]
|
||||
formdef.store()
|
||||
|
||||
app = get_app(pub)
|
||||
resp = app.get('/foo/')
|
||||
assert 'f1' in resp.form.fields
|
||||
assert 'f2' in resp.form.fields
|
||||
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
|
||||
assert resp.html.find('div', {'data-field-id': '2'}).attrs.get('style') == 'display: none'
|
||||
resp.form['f1'] = 'non'
|
||||
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
|
||||
assert live_resp.json['result']['1']['visible']
|
||||
assert not live_resp.json['result']['2']['visible']
|
||||
resp = resp.form.submit('submit')
|
||||
assert 'HELLO' in resp.body
|
||||
resp = resp.form.submit('previous')
|
||||
resp.form['f1'] = 'oui'
|
||||
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
|
||||
assert live_resp.json['result']['1']['visible']
|
||||
assert live_resp.json['result']['2']['visible']
|
||||
resp = resp.form.submit('submit')
|
||||
assert '<div class="error">required field</div>' in resp.body
|
||||
assert 'HELLO' not in resp.body
|
||||
|
||||
def test_field_live_condition_multipages(pub):
|
||||
FormDef.wipe()
|
||||
formdef = FormDef()
|
||||
|
|
|
@ -515,11 +515,16 @@ class FormPage(Directory, FormTemplateMixin):
|
|||
current_data = self.get_transient_formdata().data
|
||||
pages = []
|
||||
field_page = None
|
||||
for field in self.formdef.fields:
|
||||
if field.type == 'page':
|
||||
field_page = field
|
||||
if field.is_visible(current_data, self.formdef):
|
||||
pages.append(field)
|
||||
with get_publisher().substitutions.freeze():
|
||||
# don't let evaluation of pages alter substitution variables (this
|
||||
# avoids a ConditionVars being added with current form data and
|
||||
# influencing later code evaluating field visibility based on
|
||||
# submitted data) (#27247).
|
||||
for field in self.formdef.fields:
|
||||
if field.type == 'page':
|
||||
field_page = field
|
||||
if field.is_visible(current_data, self.formdef):
|
||||
pages.append(field)
|
||||
if not field_page: # form without page fields
|
||||
pages = [None]
|
||||
self._pages = pages
|
||||
|
|
|
@ -68,6 +68,14 @@ class Substitutions(object):
|
|||
self.sources.append(source)
|
||||
self.invalidate_cache()
|
||||
|
||||
@contextmanager
|
||||
def freeze(self):
|
||||
orig_sources, self.sources = self.sources, self.sources[:]
|
||||
self.invalidate_cache()
|
||||
yield
|
||||
self.sources = orig_sources
|
||||
self.invalidate_cache()
|
||||
|
||||
@contextmanager
|
||||
def temporary_feed(self, source):
|
||||
if source is None or source in self.sources:
|
||||
|
|
Loading…
Reference in New Issue